From 2ba456894bc343b7c1fc045fe85cff7442fdb7a5 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Mon, 21 Sep 2026 10:16:02 +0100 Subject: [PATCH 01/28] feat: add initial to-presentaiton funcitonality for Kambites, and some tests --- deps/src/CMakeLists.txt | 1 + deps/src/libsemigroups_julia.cpp | 1 + deps/src/libsemigroups_julia.hpp | 1 + deps/src/to-presentation.cpp | 18 ++++++++++ src/Semigroups.jl | 1 + src/to-presentation.jl | 11 +++++++ test/runtests.jl | 1 + test/test_to_presentation.jl | 56 ++++++++++++++++++++++++++++++++ 8 files changed, 90 insertions(+) create mode 100644 deps/src/to-presentation.cpp create mode 100644 src/to-presentation.jl create mode 100644 test/test_to_presentation.jl diff --git a/deps/src/CMakeLists.txt b/deps/src/CMakeLists.txt index fff8946..daf8172 100644 --- a/deps/src/CMakeLists.txt +++ b/deps/src/CMakeLists.txt @@ -63,6 +63,7 @@ add_library(libsemigroups_julia SHARED kambites.cpp congruence.cpp to-cong.cpp + to-presentation.cpp ) # Include directories diff --git a/deps/src/libsemigroups_julia.cpp b/deps/src/libsemigroups_julia.cpp index 1f7f026..c88ea86 100644 --- a/deps/src/libsemigroups_julia.cpp +++ b/deps/src/libsemigroups_julia.cpp @@ -58,6 +58,7 @@ namespace libsemigroups_julia { define_kambites(mod); define_congruence(mod); define_to_cong(mod); + define_to_presentation(mod); } } // namespace libsemigroups_julia diff --git a/deps/src/libsemigroups_julia.hpp b/deps/src/libsemigroups_julia.hpp index a15d0b8..f30e798 100644 --- a/deps/src/libsemigroups_julia.hpp +++ b/deps/src/libsemigroups_julia.hpp @@ -73,6 +73,7 @@ namespace libsemigroups_julia { void define_kambites(jl::Module& mod); void define_congruence(jl::Module& mod); void define_to_cong(jl::Module& mod); + void define_to_presentation(jl::Module& mod); } // namespace libsemigroups_julia diff --git a/deps/src/to-presentation.cpp b/deps/src/to-presentation.cpp new file mode 100644 index 0000000..a18b4d5 --- /dev/null +++ b/deps/src/to-presentation.cpp @@ -0,0 +1,18 @@ +#include "libsemigroups_julia.hpp" + +#include +#include +#include +#include + +namespace libsemigroups_julia { + + void define_to_presentation(jl::Module& m) { + m.method("to_presentation_word", + [](libsemigroups::Kambites& k) { + return libsemigroups::to< + libsemigroups::Presentation>(k); + }); + } + +} // namespace libsemigroups_julia diff --git a/src/Semigroups.jl b/src/Semigroups.jl index 7553423..d3e5b94 100644 --- a/src/Semigroups.jl +++ b/src/Semigroups.jl @@ -80,6 +80,7 @@ include("cong-common.jl") include("knuth-bendix.jl") include("todd-coxeter.jl") include("kambites.jl") +include("to-presentation.jl") # High-level element types include("bmat8.jl") diff --git a/src/to-presentation.jl b/src/to-presentation.jl new file mode 100644 index 0000000..93dd136 --- /dev/null +++ b/src/to-presentation.jl @@ -0,0 +1,11 @@ +""" +to-presentation.jl - to Presentation conversion +""" + +""" + to(k::Kambites) -> Presentation + +Return a [`Presentation`](@ref Semigroups.Presentation) corresponding +to the conversion from the Kambites `k`. +""" +to(k::Kambites) = LibSemigroups.to_presentation_word(k) diff --git a/test/runtests.jl b/test/runtests.jl index 4aadc90..ca7048c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,4 +28,5 @@ using Semigroups include("test_knuth_bendix_6.jl") include("test_todd_coxeter.jl") include("test_kambites.jl") + include("test_to_presentation.jl") end diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl new file mode 100644 index 0000000..6376df1 --- /dev/null +++ b/test/test_to_presentation.jl @@ -0,0 +1,56 @@ +using Test +using Semigroups + +@testset verbose = true "to Presentation" begin + @testset "scaffolding" begin + @test isdefined(Semigroups, :to) + @test hasmethod(Semigroups.to, Tuple{Kambites}) + end + + @testset "conversion with no rules" begin + p = Presentation() + set_alphabet!(p, 3) + + q = Semigroups.to(Kambites(twosided, p)) + + @test q isa Presentation + @test alphabet(q) == alphabet(p) + @test rules(q) == rules(p) + end + + @testset "conversion of alphabet + rules" begin + p = Presentation() + set_alphabet!(p, 3) + add_rule_no_checks!(p, [1, 2, 1], [2, 3]) + add_rule_no_checks!(p, [3, 3], [1]) + + q = Semigroups.to(Kambites(twosided, p)) + + @test q == p + @test alphabet(q) == alphabet(p) + @test rules(q) == rules(p) + end + + @testset "contains empty word" begin + p = Presentation() + set_alphabet!(p, 1) + set_contains_empty_word!(p, true) + + q = Semigroups.to(Kambites(twosided, p)) + + @test contains_empty_word(q) + end + + @testset "copy semantics" begin + p = Presentation() + set_alphabet!(p, 2) + add_rule_no_checks!(p, [1, 1], [2]) + + q = Semigroups.to(Kambites(twosided, p)) + add_rule_no_checks!(q, [2, 2], [1]) + + @test q !== p + @test length(rules(p)) == 1 + @test length(rules(q)) == 2 + end +end From b77fa34570fc8e9597a21aba3f3d782a241ca8b8 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Mon, 21 Sep 2026 11:08:53 +0100 Subject: [PATCH 02/28] simplify tests --- test/test_to_presentation.jl | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index 6376df1..d7dd5bf 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -10,10 +10,12 @@ using Semigroups @testset "conversion with no rules" begin p = Presentation() set_alphabet!(p, 3) + k = Kambites(twosided, p) - q = Semigroups.to(Kambites(twosided, p)) + q = Semigroups.to(k) @test q isa Presentation + @test q == presentation(k) @test alphabet(q) == alphabet(p) @test rules(q) == rules(p) end @@ -23,9 +25,11 @@ using Semigroups set_alphabet!(p, 3) add_rule_no_checks!(p, [1, 2, 1], [2, 3]) add_rule_no_checks!(p, [3, 3], [1]) + k = Kambites(twosided, p) - q = Semigroups.to(Kambites(twosided, p)) + q = Semigroups.to(k) + @test q == presentation(k) @test q == p @test alphabet(q) == alphabet(p) @test rules(q) == rules(p) @@ -35,22 +39,24 @@ using Semigroups p = Presentation() set_alphabet!(p, 1) set_contains_empty_word!(p, true) + k = Kambites(twosided, p) - q = Semigroups.to(Kambites(twosided, p)) + q = Semigroups.to(k) + @test q == presentation(k) @test contains_empty_word(q) end - @testset "copy semantics" begin + @testset "matches Kambites presentation" begin p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 1], [2]) + k = Kambites(twosided, p) - q = Semigroups.to(Kambites(twosided, p)) - add_rule_no_checks!(q, [2, 2], [1]) + q = Semigroups.to(k) - @test q !== p - @test length(rules(p)) == 1 - @test length(rules(q)) == 2 + @test q == presentation(k) + @test alphabet(q) == alphabet(presentation(k)) + @test rules(q) == rules(presentation(k)) end end From d5fecd355c9296e22441966bd4d4646f38c64a61 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 22 Sep 2026 10:22:00 +0100 Subject: [PATCH 03/28] feat: added to pres -> ToddC, KnuthB, FroidPin --- deps/src/froidure-pin.cpp | 5 +++ deps/src/knuth-bendix.cpp | 5 +++ deps/src/libsemigroups_julia.cpp | 2 +- deps/src/to-presentation.cpp | 6 ++++ src/Semigroups.jl | 2 +- src/to-presentation.jl | 26 ++++++++++++++ test/test_to_presentation.jl | 62 ++++++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 2 deletions(-) diff --git a/deps/src/froidure-pin.cpp b/deps/src/froidure-pin.cpp index f7ba4c9..ff6d216 100644 --- a/deps/src/froidure-pin.cpp +++ b/deps/src/froidure-pin.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -109,6 +110,10 @@ namespace libsemigroups_julia { auto type = m.add_type(name, jlcxx::julia_base_type()); + type.method("to_presentation_word", [](FP& self) { + return libsemigroups::to>(self); + }); + //////////////////////////////////////////////////////////////////// // 1. Constructors — 1-4 generator arg lambdas // (Can't construct StdVector of wrapped types Julia-side) diff --git a/deps/src/knuth-bendix.cpp b/deps/src/knuth-bendix.cpp index c3b80c2..abd3668 100644 --- a/deps/src/knuth-bendix.cpp +++ b/deps/src/knuth-bendix.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "cong-common.hpp" @@ -103,6 +104,10 @@ namespace libsemigroups_julia { auto type = m.add_type("KnuthBendixRewriteTrie", jlcxx::julia_base_type()); + type.method("to_presentation_word", [](KB& self) { + return libsemigroups::to>(self); + }); + //////////////////////////////////////////////////////////////////////// // Constructors //////////////////////////////////////////////////////////////////////// diff --git a/deps/src/libsemigroups_julia.cpp b/deps/src/libsemigroups_julia.cpp index c88ea86..5b5cb73 100644 --- a/deps/src/libsemigroups_julia.cpp +++ b/deps/src/libsemigroups_julia.cpp @@ -49,9 +49,9 @@ namespace libsemigroups_julia { define_word_range(mod); define_word_graph(mod); define_paths(mod); + define_presentation(mod); define_froidure_pin_base(mod); define_froidure_pin(mod); - define_presentation(mod); define_presentation_examples(mod); define_knuth_bendix(mod); define_todd_coxeter(mod); diff --git a/deps/src/to-presentation.cpp b/deps/src/to-presentation.cpp index a18b4d5..48704ce 100644 --- a/deps/src/to-presentation.cpp +++ b/deps/src/to-presentation.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace libsemigroups_julia { @@ -13,6 +14,11 @@ namespace libsemigroups_julia { return libsemigroups::to< libsemigroups::Presentation>(k); }); + m.method("to_presentation_word", + [](libsemigroups::ToddCoxeter& tc) { + return libsemigroups::to< + libsemigroups::Presentation>(tc); + }); } } // namespace libsemigroups_julia diff --git a/src/Semigroups.jl b/src/Semigroups.jl index d3e5b94..e24ae5a 100644 --- a/src/Semigroups.jl +++ b/src/Semigroups.jl @@ -80,7 +80,6 @@ include("cong-common.jl") include("knuth-bendix.jl") include("todd-coxeter.jl") include("kambites.jl") -include("to-presentation.jl") # High-level element types include("bmat8.jl") @@ -88,6 +87,7 @@ include("transf.jl") # Algorithm types (must come after element types) include("froidure-pin.jl") +include("to-presentation.jl") function _version_string(v::Union{Nothing,VersionNumber}) return isnothing(v) ? "unknown" : string(v) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index 93dd136..04776bc 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -9,3 +9,29 @@ Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to the conversion from the Kambites `k`. """ to(k::Kambites) = LibSemigroups.to_presentation_word(k) + +""" + to(tc::ToddCoxeter) -> Presentation + +Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to +`tc`. +""" +to(tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) + +""" + to(kb::KnuthBendix) -> Presentation + +Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to +the active rules of `kb`. +""" +to(kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) + +""" + to(fp::FroidurePin) -> Presentation + +Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to +`fp`. + +The Froidure-Pin object should be fully enumerated before conversion. +""" +to(fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index d7dd5bf..ace4cbe 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -5,6 +5,9 @@ using Semigroups @testset "scaffolding" begin @test isdefined(Semigroups, :to) @test hasmethod(Semigroups.to, Tuple{Kambites}) + @test hasmethod(Semigroups.to, Tuple{ToddCoxeter}) + @test hasmethod(Semigroups.to, Tuple{KnuthBendix}) + @test hasmethod(Semigroups.to, Tuple{FroidurePin}) end @testset "conversion with no rules" begin @@ -59,4 +62,63 @@ using Semigroups @test alphabet(q) == alphabet(presentation(k)) @test rules(q) == rules(presentation(k)) end + + @testset "ToddCoxeter conversion" begin + p = Presentation() + set_alphabet!(p, 2) + add_rule_no_checks!(p, [1, 1], [2]) + tc = ToddCoxeter(twosided, p) + + q = Semigroups.to(tc) + + @test q isa Presentation + @test q == presentation(tc) + @test alphabet(q) == alphabet(p) + @test rules(q) == rules(p) + end + + @testset "ToddCoxeter conversion contains empty word" begin + p = Presentation() + set_alphabet!(p, 1) + set_contains_empty_word!(p, true) + tc = ToddCoxeter(twosided, p) + + @test contains_empty_word(Semigroups.to(tc)) + end + + @testset "KnuthBendix conversion" begin + p = Presentation() + set_alphabet!(p, 2) + add_rule_no_checks!(p, [1, 1], [1]) + kb = KnuthBendix(twosided, p) + run!(kb) + + q = Semigroups.to(kb) + + @test q isa Presentation + @test alphabet(q) == alphabet(presentation(kb)) + @test rules(q) == active_rules(kb) + @test number_of_rules(q) == length(active_rules(kb)) + end + + @testset "KnuthBendix conversion contains empty word" begin + p = Presentation() + set_alphabet!(p, 1) + set_contains_empty_word!(p, true) + kb = KnuthBendix(twosided, p) + + @test contains_empty_word(Semigroups.to(kb)) + end + + @testset "FroidurePin conversion" begin + fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) + run!(fp) + + q = Semigroups.to(fp) + + @test q isa Presentation + @test alphabet(q) == [1, 2] + @test number_of_rules(q) > 0 + throw_if_bad_alphabet_or_rules(q) + end end From 17837244a015d923a7e11631a1789df8b1412f4b Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 22 Sep 2026 10:41:21 +0100 Subject: [PATCH 04/28] feat: added to presentation --- src/to-presentation.jl | 7 +++++++ test/test_to_presentation.jl | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index 04776bc..d1b8f71 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -2,6 +2,13 @@ to-presentation.jl - to Presentation conversion """ +""" + to(p::Presentation) -> Presentation + +Return a copy of the presentation `p`. +""" +to(p::Presentation) = Presentation(p) + """ to(k::Kambites) -> Presentation diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index ace4cbe..188e0aa 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -4,12 +4,29 @@ using Semigroups @testset verbose = true "to Presentation" begin @testset "scaffolding" begin @test isdefined(Semigroups, :to) + @test hasmethod(Semigroups.to, Tuple{Presentation}) @test hasmethod(Semigroups.to, Tuple{Kambites}) @test hasmethod(Semigroups.to, Tuple{ToddCoxeter}) @test hasmethod(Semigroups.to, Tuple{KnuthBendix}) @test hasmethod(Semigroups.to, Tuple{FroidurePin}) end + @testset "Presentation conversion" begin + p = Presentation() + set_alphabet!(p, 2) + add_rule_no_checks!(p, [1, 1], [2]) + set_contains_empty_word!(p, true) + + q = Semigroups.to(p) + + @test q isa Presentation + @test q == p + @test q !== p + @test alphabet(q) == alphabet(p) + @test rules(q) == rules(p) + @test contains_empty_word(q) == contains_empty_word(p) + end + @testset "conversion with no rules" begin p = Presentation() set_alphabet!(p, 3) From 583775b5d0c326594e77f1326241a397720127ff Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 22 Sep 2026 12:15:38 +0100 Subject: [PATCH 05/28] feat: add conversions to knuth and tc --- deps/src/CMakeLists.txt | 2 ++ deps/src/knuth-bendix.cpp | 4 ++-- deps/src/libsemigroups_julia.cpp | 2 ++ deps/src/libsemigroups_julia.hpp | 2 ++ deps/src/to-knuth-bendix.cpp | 29 ++++++++++++++++++++++++++++ deps/src/to-presentation.cpp | 6 ++++++ deps/src/to-todd-coxeter.cpp | 30 +++++++++++++++++++++++++++++ docs/make.jl | 3 +-- src/Semigroups.jl | 4 +++- src/cong-common.jl | 2 ++ src/to-knuth-bendix.jl | 19 ++++++++++++++++++ src/to-presentation.jl | 7 +++++++ src/to-todd-coxeter.jl | 20 +++++++++++++++++++ test/runtests.jl | 2 ++ test/test_to_knuth_bendix.jl | 32 +++++++++++++++++++++++++++++++ test/test_to_presentation.jl | 14 ++++++++++++++ test/test_to_todd_coxeter.jl | 33 ++++++++++++++++++++++++++++++++ 17 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 deps/src/to-knuth-bendix.cpp create mode 100644 deps/src/to-todd-coxeter.cpp create mode 100644 src/to-knuth-bendix.jl create mode 100644 src/to-todd-coxeter.jl create mode 100644 test/test_to_knuth_bendix.jl create mode 100644 test/test_to_todd_coxeter.jl diff --git a/deps/src/CMakeLists.txt b/deps/src/CMakeLists.txt index daf8172..dd08d9a 100644 --- a/deps/src/CMakeLists.txt +++ b/deps/src/CMakeLists.txt @@ -63,6 +63,8 @@ add_library(libsemigroups_julia SHARED kambites.cpp congruence.cpp to-cong.cpp + to-todd-coxeter.cpp + to-knuth-bendix.cpp to-presentation.cpp ) diff --git a/deps/src/knuth-bendix.cpp b/deps/src/knuth-bendix.cpp index abd3668..f985647 100644 --- a/deps/src/knuth-bendix.cpp +++ b/deps/src/knuth-bendix.cpp @@ -80,8 +80,8 @@ namespace libsemigroups_julia { using CongruenceCommon = libsemigroups::detail::CongruenceCommon; using KBImpl = libsemigroups::detail::KnuthBendixImpl< - libsemigroups::detail::RewriteTrie, - libsemigroups::ShortLexCompare>; + libsemigroups::detail::RewriteTrie, + libsemigroups::ShortLexCompare>; using KB = libsemigroups::KnuthBendix; diff --git a/deps/src/libsemigroups_julia.cpp b/deps/src/libsemigroups_julia.cpp index 5b5cb73..b60c7f1 100644 --- a/deps/src/libsemigroups_julia.cpp +++ b/deps/src/libsemigroups_julia.cpp @@ -58,6 +58,8 @@ namespace libsemigroups_julia { define_kambites(mod); define_congruence(mod); define_to_cong(mod); + define_to_todd_coxeter(mod); + define_to_knuth_bendix(mod); define_to_presentation(mod); } diff --git a/deps/src/libsemigroups_julia.hpp b/deps/src/libsemigroups_julia.hpp index f30e798..79018cd 100644 --- a/deps/src/libsemigroups_julia.hpp +++ b/deps/src/libsemigroups_julia.hpp @@ -73,6 +73,8 @@ namespace libsemigroups_julia { void define_kambites(jl::Module& mod); void define_congruence(jl::Module& mod); void define_to_cong(jl::Module& mod); + void define_to_todd_coxeter(jl::Module& mod); + void define_to_knuth_bendix(jl::Module& mod); void define_to_presentation(jl::Module& mod); } // namespace libsemigroups_julia diff --git a/deps/src/to-knuth-bendix.cpp b/deps/src/to-knuth-bendix.cpp new file mode 100644 index 0000000..d9ba217 --- /dev/null +++ b/deps/src/to-knuth-bendix.cpp @@ -0,0 +1,29 @@ +#include "libsemigroups_julia.hpp" + +#include +#include +#include +#include +#include + +namespace libsemigroups_julia { + + void define_to_knuth_bendix(jl::Module& m) { + using KB = libsemigroups::KnuthBendix; + + m.method("to_knuth_bendix_from_fpb", + [](libsemigroups::congruence_kind knd, + libsemigroups::FroidurePinBase& fpb) { + return libsemigroups::to(knd, fpb); + }); + + m.method("to_knuth_bendix_from_tc", + [](libsemigroups::congruence_kind knd, + libsemigroups::ToddCoxeter& tc) { + return libsemigroups::to(knd, tc); + }); + } + +} // namespace libsemigroups_julia diff --git a/deps/src/to-presentation.cpp b/deps/src/to-presentation.cpp index 48704ce..839b232 100644 --- a/deps/src/to-presentation.cpp +++ b/deps/src/to-presentation.cpp @@ -1,5 +1,6 @@ #include "libsemigroups_julia.hpp" +#include #include #include #include @@ -9,6 +10,11 @@ namespace libsemigroups_julia { void define_to_presentation(jl::Module& m) { + m.method("to_presentation_word", + [](libsemigroups::Congruence& c) { + return libsemigroups::to< + libsemigroups::Presentation>(c); + }); m.method("to_presentation_word", [](libsemigroups::Kambites& k) { return libsemigroups::to< diff --git a/deps/src/to-todd-coxeter.cpp b/deps/src/to-todd-coxeter.cpp new file mode 100644 index 0000000..d7ca753 --- /dev/null +++ b/deps/src/to-todd-coxeter.cpp @@ -0,0 +1,30 @@ +#include "libsemigroups_julia.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace libsemigroups_julia { + + void define_to_todd_coxeter(jl::Module& m) { + m.method("to_todd_coxeter_from_fpb", + [](libsemigroups::congruence_kind knd, + libsemigroups::FroidurePinBase& fpb, + libsemigroups::WordGraph const& wg) { + return libsemigroups::to< + libsemigroups::ToddCoxeter>( + knd, fpb, wg); + }); + + m.method("to_todd_coxeter_from_kb", + [](libsemigroups::congruence_kind knd, + libsemigroups::KnuthBendix& kb) { + return libsemigroups::to(knd, kb); + }); + } + +} // namespace libsemigroups_julia diff --git a/docs/make.jl b/docs/make.jl index 6226c81..2031ef4 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -31,8 +31,7 @@ makedocs(; "Package Info" => [ "Installation" => "package-info/installation.md", "Authors" => "package-info/authors.md", - #= "Bibliography" => "package-info/bibliography.md", =# - "Exceptions" => "package-info/exceptions.md", + #= "Bibliography" => "package-info/bibliography.md", =# "Exceptions" => "package-info/exceptions.md", ], "Data Structures" => [ "Constants" => "data-structures/constants/index.md", diff --git a/src/Semigroups.jl b/src/Semigroups.jl index e24ae5a..da46d79 100644 --- a/src/Semigroups.jl +++ b/src/Semigroups.jl @@ -87,7 +87,9 @@ include("transf.jl") # Algorithm types (must come after element types) include("froidure-pin.jl") +include("to-todd-coxeter.jl") include("to-presentation.jl") +include("to-knuth-bendix.jl") function _version_string(v::Union{Nothing,VersionNumber}) return isnothing(v) ? "unknown" : string(v) @@ -261,7 +263,7 @@ end export enable_debug, is_debug, LibsemigroupsError, ReportGuard export UNDEFINED, POSITIVE_INFINITY, NEGATIVE_INFINITY, LIMIT_MAX export Runner, RunnerState -export CongruenceCommon +export CongruenceCommon, Congruence export STATE_NEVER_RUN, STATE_RUNNING_TO_FINISH, STATE_RUNNING_FOR export STATE_RUNNING_UNTIL, STATE_TIMED_OUT, STATE_STOPPED_BY_PREDICATE export STATE_NOT_RUNNING, STATE_DEAD diff --git a/src/cong-common.jl b/src/cong-common.jl index 182ec3d..71e4f7d 100644 --- a/src/cong-common.jl +++ b/src/cong-common.jl @@ -22,6 +22,8 @@ algorithm. """ const CongruenceCommon = LibSemigroups.CongruenceCommon +const Congruence = LibSemigroups.CongruenceWord + _words_to_cpp(words::AbstractVector{<:AbstractVector{<:Integer}}) = Any[_word_to_cpp(word) for word in words] diff --git a/src/to-knuth-bendix.jl b/src/to-knuth-bendix.jl new file mode 100644 index 0000000..2d660d5 --- /dev/null +++ b/src/to-knuth-bendix.jl @@ -0,0 +1,19 @@ +""" +to-knuth-bendix.jl - conversions to KnuthBendix +""" + +""" + to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) -> KnuthBendix + +Convert a Froidure-Pin object into a Knuth-Bendix instance. +""" +to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = + LibSemigroups.to_knuth_bendix_from_fpb(kind, fp.cxx_obj) + +""" + to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) -> KnuthBendix + +Convert a Todd-Coxeter object into a Knuth-Bendix instance. +""" +to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) = + LibSemigroups.to_knuth_bendix_from_tc(kind, tc) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index d1b8f71..b5ad9d9 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -9,6 +9,13 @@ Return a copy of the presentation `p`. """ to(p::Presentation) = Presentation(p) +""" + to(c::Congruence) -> Presentation + +Return the presentation underlying the congruence `c`. +""" +to(c::Congruence) = LibSemigroups.to_presentation_word(c) + """ to(k::Kambites) -> Presentation diff --git a/src/to-todd-coxeter.jl b/src/to-todd-coxeter.jl new file mode 100644 index 0000000..a995a7b --- /dev/null +++ b/src/to-todd-coxeter.jl @@ -0,0 +1,20 @@ +""" +to-todd-coxeter.jl - conversions to ToddCoxeter +""" + +""" + to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, + wg::WordGraph) -> ToddCoxeter + +Convert a Froidure-Pin Cayley graph into a Todd-Coxeter instance. +""" +to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg) = + LibSemigroups.to_todd_coxeter_from_fpb(kind, fp.cxx_obj, wg) + +""" + to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) -> ToddCoxeter + +Convert a Knuth-Bendix object into a Todd-Coxeter instance. +""" +to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) = + LibSemigroups.to_todd_coxeter_from_kb(kind, kb) diff --git a/test/runtests.jl b/test/runtests.jl index ca7048c..70f0c34 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,5 +28,7 @@ using Semigroups include("test_knuth_bendix_6.jl") include("test_todd_coxeter.jl") include("test_kambites.jl") + include("test_to_todd_coxeter.jl") include("test_to_presentation.jl") + include("test_to_knuth_bendix.jl") end diff --git a/test/test_to_knuth_bendix.jl b/test/test_to_knuth_bendix.jl new file mode 100644 index 0000000..f08a8a3 --- /dev/null +++ b/test/test_to_knuth_bendix.jl @@ -0,0 +1,32 @@ +using Test +using Semigroups + +@testset verbose = true "to KnuthBendix" begin + @testset "scaffolding" begin + @test hasmethod(Semigroups.to, Tuple{Type{KnuthBendix},congruence_kind,FroidurePin}) + @test hasmethod(Semigroups.to, Tuple{Type{KnuthBendix},congruence_kind,ToddCoxeter}) + end + + @testset "from FroidurePin" begin + fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) + run!(fp) + kb = Semigroups.to(KnuthBendix, twosided, fp) + + @test kb isa KnuthBendix + @test alphabet(presentation(kb)) == [1, 2] + @test number_of_rules(presentation(kb)) > 0 + throw_if_bad_alphabet_or_rules(presentation(kb)) + end + + @testset "from ToddCoxeter" begin + p = Presentation() + set_alphabet!(p, 1) + add_rule_no_checks!(p, [1, 1], [1]) + tc = ToddCoxeter(twosided, p) + + kb = Semigroups.to(KnuthBendix, twosided, tc) + + @test kb isa KnuthBendix + @test presentation(kb) == presentation(tc) + end +end diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index 188e0aa..96d5313 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -5,6 +5,7 @@ using Semigroups @testset "scaffolding" begin @test isdefined(Semigroups, :to) @test hasmethod(Semigroups.to, Tuple{Presentation}) + @test hasmethod(Semigroups.to, Tuple{Congruence}) @test hasmethod(Semigroups.to, Tuple{Kambites}) @test hasmethod(Semigroups.to, Tuple{ToddCoxeter}) @test hasmethod(Semigroups.to, Tuple{KnuthBendix}) @@ -27,6 +28,18 @@ using Semigroups @test contains_empty_word(q) == contains_empty_word(p) end + @testset "Congruence conversion" begin + p = Presentation() + set_alphabet!(p, 2) + add_rule_no_checks!(p, [1, 1], [2]) + c = Congruence(twosided, p) + + q = Semigroups.to(c) + + @test q isa Presentation + @test q == p + end + @testset "conversion with no rules" begin p = Presentation() set_alphabet!(p, 3) @@ -138,4 +151,5 @@ using Semigroups @test number_of_rules(q) > 0 throw_if_bad_alphabet_or_rules(q) end + end diff --git a/test/test_to_todd_coxeter.jl b/test/test_to_todd_coxeter.jl new file mode 100644 index 0000000..3dd19d6 --- /dev/null +++ b/test/test_to_todd_coxeter.jl @@ -0,0 +1,33 @@ +using Test +using Semigroups + +@testset verbose = true "to ToddCoxeter" begin + @testset "scaffolding" begin + @test hasmethod( + Semigroups.to, + Tuple{Type{ToddCoxeter},congruence_kind,FroidurePin,Any}, + ) + @test hasmethod(Semigroups.to, Tuple{Type{ToddCoxeter},congruence_kind,KnuthBendix}) + end + + @testset "from FroidurePin" begin + fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) + tc = Semigroups.to(ToddCoxeter, twosided, fp, right_cayley_graph(fp)) + + @test tc isa ToddCoxeter + @test alphabet(presentation(tc)) == [1, 2] + end + + @testset "from KnuthBendix" begin + p = Presentation() + set_alphabet!(p, 1) + add_rule_no_checks!(p, [1, 1], [1]) + kb = KnuthBendix(twosided, p) + + tc = Semigroups.to(ToddCoxeter, twosided, kb) + + @test tc isa ToddCoxeter + @test alphabet(presentation(tc)) == [1] + @test number_of_classes(tc) == 1 + end +end From e5aac3636a2bcacc0aa1c52dfed942eb7f6167e4 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 22 Sep 2026 17:24:15 +0100 Subject: [PATCH 06/28] feat: adding inverse, still some missing --- deps/src/CMakeLists.txt | 1 + deps/src/libsemigroups_julia.cpp | 1 + deps/src/libsemigroups_julia.hpp | 1 + deps/src/to-froidure-pin.cpp | 21 +++++++++++++++ deps/src/to-presentation.cpp | 12 +++++++++ src/Semigroups.jl | 3 +++ src/to-cong.jl | 20 ++++++++++++++ src/to-froidure-pin.jl | 12 +++++++++ src/to-inverse-presentation.jl | 20 ++++++++++++++ test/runtests.jl | 3 +++ test/test_to_congruence.jl | 35 ++++++++++++++++++++++++ test/test_to_froidure_pin.jl | 20 ++++++++++++++ test/test_to_inverse_presentation.jl | 40 ++++++++++++++++++++++++++++ 13 files changed, 189 insertions(+) create mode 100644 deps/src/to-froidure-pin.cpp create mode 100644 src/to-cong.jl create mode 100644 src/to-froidure-pin.jl create mode 100644 src/to-inverse-presentation.jl create mode 100644 test/test_to_congruence.jl create mode 100644 test/test_to_froidure_pin.jl create mode 100644 test/test_to_inverse_presentation.jl diff --git a/deps/src/CMakeLists.txt b/deps/src/CMakeLists.txt index dd08d9a..5b582cb 100644 --- a/deps/src/CMakeLists.txt +++ b/deps/src/CMakeLists.txt @@ -65,6 +65,7 @@ add_library(libsemigroups_julia SHARED to-cong.cpp to-todd-coxeter.cpp to-knuth-bendix.cpp + to-froidure-pin.cpp to-presentation.cpp ) diff --git a/deps/src/libsemigroups_julia.cpp b/deps/src/libsemigroups_julia.cpp index b60c7f1..250c7a1 100644 --- a/deps/src/libsemigroups_julia.cpp +++ b/deps/src/libsemigroups_julia.cpp @@ -60,6 +60,7 @@ namespace libsemigroups_julia { define_to_cong(mod); define_to_todd_coxeter(mod); define_to_knuth_bendix(mod); + define_to_froidure_pin(mod); define_to_presentation(mod); } diff --git a/deps/src/libsemigroups_julia.hpp b/deps/src/libsemigroups_julia.hpp index 79018cd..ff7f877 100644 --- a/deps/src/libsemigroups_julia.hpp +++ b/deps/src/libsemigroups_julia.hpp @@ -75,6 +75,7 @@ namespace libsemigroups_julia { void define_to_cong(jl::Module& mod); void define_to_todd_coxeter(jl::Module& mod); void define_to_knuth_bendix(jl::Module& mod); + void define_to_froidure_pin(jl::Module& mod); void define_to_presentation(jl::Module& mod); } // namespace libsemigroups_julia diff --git a/deps/src/to-froidure-pin.cpp b/deps/src/to-froidure-pin.cpp new file mode 100644 index 0000000..158c55e --- /dev/null +++ b/deps/src/to-froidure-pin.cpp @@ -0,0 +1,21 @@ +#include "libsemigroups_julia.hpp" + +#include + +#include +#include +#include +#include + +namespace libsemigroups_julia { + + void define_to_froidure_pin(jl::Module& m) { + using FP = libsemigroups::FroidurePin>; + + m.method("to_froidure_pin_from_wg", + [](libsemigroups::WordGraph const& wg) { + return libsemigroups::to(wg); + }); + } + +} // namespace libsemigroups_julia diff --git a/deps/src/to-presentation.cpp b/deps/src/to-presentation.cpp index 839b232..019978c 100644 --- a/deps/src/to-presentation.cpp +++ b/deps/src/to-presentation.cpp @@ -10,6 +10,18 @@ namespace libsemigroups_julia { void define_to_presentation(jl::Module& m) { + m.method( + "to_inverse_presentation_word", + [](libsemigroups::Presentation const& p) { + return libsemigroups::to(p); + }); + m.method( + "to_inverse_presentation_word", + [](libsemigroups::InversePresentation const& + ip) { + return libsemigroups::InversePresentation( + ip); + }); m.method("to_presentation_word", [](libsemigroups::Congruence& c) { return libsemigroups::to< diff --git a/src/Semigroups.jl b/src/Semigroups.jl index da46d79..6ed0f09 100644 --- a/src/Semigroups.jl +++ b/src/Semigroups.jl @@ -87,9 +87,12 @@ include("transf.jl") # Algorithm types (must come after element types) include("froidure-pin.jl") +include("to-cong.jl") include("to-todd-coxeter.jl") include("to-presentation.jl") include("to-knuth-bendix.jl") +include("to-froidure-pin.jl") +include("to-inverse-presentation.jl") function _version_string(v::Union{Nothing,VersionNumber}) return isnothing(v) ? "unknown" : string(v) diff --git a/src/to-cong.jl b/src/to-cong.jl new file mode 100644 index 0000000..0abe2c2 --- /dev/null +++ b/src/to-cong.jl @@ -0,0 +1,20 @@ +""" +to-cong.jl - conversions to Congruence +""" + +""" + to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, + wg) -> Congruence + +Convert a Froidure-Pin Cayley graph into a Congruence instance. +""" +to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) = + LibSemigroups.to_congruence_from_fpb(kind, fp.cxx_obj, wg) + +""" + to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) -> Congruence + +Convert a WordGraph into a Congruence instance. +""" +to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) = + LibSemigroups.to_congruence_from_wg(kind, wg) diff --git a/src/to-froidure-pin.jl b/src/to-froidure-pin.jl new file mode 100644 index 0000000..144bcb2 --- /dev/null +++ b/src/to-froidure-pin.jl @@ -0,0 +1,12 @@ +""" +to-froidure-pin.jl - conversions to FroidurePin +""" + +""" + to(::Type{FroidurePin}, wg::WordGraph) -> FroidurePin{Transf{UInt8}} + +Convert a complete WordGraph into a Froidure-Pin instance whose generators +are transformations of the graph nodes. +""" +to(::Type{FroidurePin}, wg::WordGraph) = + FroidurePin{Transf{UInt8}}(LibSemigroups.to_froidure_pin_from_wg(wg)) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl new file mode 100644 index 0000000..fd2c37e --- /dev/null +++ b/src/to-inverse-presentation.jl @@ -0,0 +1,20 @@ +""" +to-inverse-presentation.jl - conversions to InversePresentation +""" + +""" + to(::Type{InversePresentation}, p::Presentation) -> InversePresentation + +Convert a presentation into an inverse presentation over a normalized doubled +alphabet. The inverse of letter `i` is `i + n` modulo `2n`, where `n` is the +number of letters in `p`. +""" +to(::Type{InversePresentation}, p::Presentation) = + LibSemigroups.to_inverse_presentation_word(p) + +""" + to(ip::InversePresentation) -> InversePresentation + +Return a copy of the inverse presentation `ip`. +""" +to(ip::InversePresentation) = InversePresentation(ip) diff --git a/test/runtests.jl b/test/runtests.jl index 70f0c34..5c99d6d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -31,4 +31,7 @@ using Semigroups include("test_to_todd_coxeter.jl") include("test_to_presentation.jl") include("test_to_knuth_bendix.jl") + include("test_to_congruence.jl") + include("test_to_inverse_presentation.jl") + include("test_to_froidure_pin.jl") end diff --git a/test/test_to_congruence.jl b/test/test_to_congruence.jl new file mode 100644 index 0000000..80faea2 --- /dev/null +++ b/test/test_to_congruence.jl @@ -0,0 +1,35 @@ +using Test +using Semigroups + +@testset verbose = true "to Congruence" begin + @testset "scaffolding" begin + @test hasmethod( + Semigroups.to, + Tuple{Type{Congruence}, congruence_kind, FroidurePin, Any}, + ) + @test hasmethod( + Semigroups.to, + Tuple{Type{Congruence}, congruence_kind, WordGraph}, + ) + end + + @testset "from FroidurePin" begin + fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) + run!(fp) + c = Semigroups.to(Congruence, twosided, fp, right_cayley_graph(fp)) + + @test c isa Congruence + @test c isa CongruenceCommon + end + + @testset "from WordGraph" begin + wg = WordGraph(2, 1) + target!(wg, 1, 1, 2) + target!(wg, 2, 1, 2) + + c = Semigroups.to(Congruence, twosided, wg) + + @test c isa Congruence + @test c isa CongruenceCommon + end +end diff --git a/test/test_to_froidure_pin.jl b/test/test_to_froidure_pin.jl new file mode 100644 index 0000000..ac3367d --- /dev/null +++ b/test/test_to_froidure_pin.jl @@ -0,0 +1,20 @@ +using Test +using Semigroups + +@testset verbose = true "to FroidurePin" begin + @testset "scaffolding" begin + @test hasmethod(Semigroups.to, Tuple{Type{FroidurePin}, WordGraph}) + end + + @testset "from WordGraph" begin + wg = WordGraph(2, 1) + target!(wg, 1, 1, 2) + target!(wg, 2, 1, 2) + + fp = Semigroups.to(FroidurePin, wg) + + @test fp isa FroidurePin{Transf{UInt8}} + @test number_of_generators(fp) == 1 + @test degree(fp) == 2 + end +end diff --git a/test/test_to_inverse_presentation.jl b/test/test_to_inverse_presentation.jl new file mode 100644 index 0000000..09759e4 --- /dev/null +++ b/test/test_to_inverse_presentation.jl @@ -0,0 +1,40 @@ +using Test +using Semigroups + +@testset verbose = true "to InversePresentation" begin + @testset "scaffolding" begin + @test hasmethod( + Semigroups.to, + Tuple{Type{InversePresentation}, Presentation}, + ) + @test hasmethod(Semigroups.to, Tuple{InversePresentation}) + end + + @testset "from Presentation" begin + p = Presentation() + set_alphabet!(p, 2) + add_rule_no_checks!(p, [1, 2], [2, 1]) + + ip = Semigroups.to(InversePresentation, p) + + @test ip isa InversePresentation + @test alphabet(ip) == [1, 2, 3, 4] + @test rules(ip) == rules(p) + @test inverses(ip) == [3, 4, 1, 2] + throw_if_bad_alphabet_rules_or_inverses(ip) + end + + @testset "copy" begin + p = Presentation() + set_alphabet!(p, 1) + ip = InversePresentation(p) + set_inverses!(ip, [1]) + + copy = Semigroups.to(ip) + + @test copy isa InversePresentation + @test copy !== ip + @test copy == ip + @test inverses(copy) == [1] + end +end From 484a232a5762a7afe24f8c64e0c05709236bdde7 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 22 Sep 2026 17:30:57 +0100 Subject: [PATCH 07/28] Formatting --- test/test_to_congruence.jl | 7 ++----- test/test_to_froidure_pin.jl | 2 +- test/test_to_inverse_presentation.jl | 5 +---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/test/test_to_congruence.jl b/test/test_to_congruence.jl index 80faea2..f6d3c0b 100644 --- a/test/test_to_congruence.jl +++ b/test/test_to_congruence.jl @@ -5,12 +5,9 @@ using Semigroups @testset "scaffolding" begin @test hasmethod( Semigroups.to, - Tuple{Type{Congruence}, congruence_kind, FroidurePin, Any}, - ) - @test hasmethod( - Semigroups.to, - Tuple{Type{Congruence}, congruence_kind, WordGraph}, + Tuple{Type{Congruence},congruence_kind,FroidurePin,Any}, ) + @test hasmethod(Semigroups.to, Tuple{Type{Congruence},congruence_kind,WordGraph}) end @testset "from FroidurePin" begin diff --git a/test/test_to_froidure_pin.jl b/test/test_to_froidure_pin.jl index ac3367d..269667a 100644 --- a/test/test_to_froidure_pin.jl +++ b/test/test_to_froidure_pin.jl @@ -3,7 +3,7 @@ using Semigroups @testset verbose = true "to FroidurePin" begin @testset "scaffolding" begin - @test hasmethod(Semigroups.to, Tuple{Type{FroidurePin}, WordGraph}) + @test hasmethod(Semigroups.to, Tuple{Type{FroidurePin},WordGraph}) end @testset "from WordGraph" begin diff --git a/test/test_to_inverse_presentation.jl b/test/test_to_inverse_presentation.jl index 09759e4..5a73cd8 100644 --- a/test/test_to_inverse_presentation.jl +++ b/test/test_to_inverse_presentation.jl @@ -3,10 +3,7 @@ using Semigroups @testset verbose = true "to InversePresentation" begin @testset "scaffolding" begin - @test hasmethod( - Semigroups.to, - Tuple{Type{InversePresentation}, Presentation}, - ) + @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},Presentation}) @test hasmethod(Semigroups.to, Tuple{InversePresentation}) end From ad79c0a52cf7369efa21f8919da08a4b9b2f5861 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Wed, 23 Sep 2026 11:25:59 +0100 Subject: [PATCH 08/28] Comments and Doc --- deps/src/froidure-pin.cpp | 1 + deps/src/knuth-bendix.cpp | 5 +++-- src/to-cong.jl | 4 ++-- src/to-froidure-pin.jl | 3 +-- src/to-inverse-presentation.jl | 6 ++---- src/to-knuth-bendix.jl | 4 ++-- src/to-presentation.jl | 18 ++++++------------ src/to-todd-coxeter.jl | 4 ++-- 8 files changed, 19 insertions(+), 26 deletions(-) diff --git a/deps/src/froidure-pin.cpp b/deps/src/froidure-pin.cpp index ff6d216..c21fcce 100644 --- a/deps/src/froidure-pin.cpp +++ b/deps/src/froidure-pin.cpp @@ -110,6 +110,7 @@ namespace libsemigroups_julia { auto type = m.add_type(name, jlcxx::julia_base_type()); + // to_presentation_word, returns Presentation type.method("to_presentation_word", [](FP& self) { return libsemigroups::to>(self); }); diff --git a/deps/src/knuth-bendix.cpp b/deps/src/knuth-bendix.cpp index f985647..6447707 100644 --- a/deps/src/knuth-bendix.cpp +++ b/deps/src/knuth-bendix.cpp @@ -80,8 +80,8 @@ namespace libsemigroups_julia { using CongruenceCommon = libsemigroups::detail::CongruenceCommon; using KBImpl = libsemigroups::detail::KnuthBendixImpl< - libsemigroups::detail::RewriteTrie, - libsemigroups::ShortLexCompare>; + libsemigroups::detail::RewriteTrie, + libsemigroups::ShortLexCompare>; using KB = libsemigroups::KnuthBendix; @@ -104,6 +104,7 @@ namespace libsemigroups_julia { auto type = m.add_type("KnuthBendixRewriteTrie", jlcxx::julia_base_type()); + // to_presentation_word, returns Presentation type.method("to_presentation_word", [](KB& self) { return libsemigroups::to>(self); }); diff --git a/src/to-cong.jl b/src/to-cong.jl index 0abe2c2..08a33bf 100644 --- a/src/to-cong.jl +++ b/src/to-cong.jl @@ -6,7 +6,7 @@ to-cong.jl - conversions to Congruence to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) -> Congruence -Convert a Froidure-Pin Cayley graph into a Congruence instance. +Convert a Froidure-Pin graph to a Congruence. """ to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) = LibSemigroups.to_congruence_from_fpb(kind, fp.cxx_obj, wg) @@ -14,7 +14,7 @@ to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) = """ to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) -> Congruence -Convert a WordGraph into a Congruence instance. +Convert a WordGraph to a Congruence. """ to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) = LibSemigroups.to_congruence_from_wg(kind, wg) diff --git a/src/to-froidure-pin.jl b/src/to-froidure-pin.jl index 144bcb2..e54d2de 100644 --- a/src/to-froidure-pin.jl +++ b/src/to-froidure-pin.jl @@ -5,8 +5,7 @@ to-froidure-pin.jl - conversions to FroidurePin """ to(::Type{FroidurePin}, wg::WordGraph) -> FroidurePin{Transf{UInt8}} -Convert a complete WordGraph into a Froidure-Pin instance whose generators -are transformations of the graph nodes. +Convert a complete WordGraph to a Froidure-Pin """ to(::Type{FroidurePin}, wg::WordGraph) = FroidurePin{Transf{UInt8}}(LibSemigroups.to_froidure_pin_from_wg(wg)) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl index fd2c37e..aba1adb 100644 --- a/src/to-inverse-presentation.jl +++ b/src/to-inverse-presentation.jl @@ -5,9 +5,7 @@ to-inverse-presentation.jl - conversions to InversePresentation """ to(::Type{InversePresentation}, p::Presentation) -> InversePresentation -Convert a presentation into an inverse presentation over a normalized doubled -alphabet. The inverse of letter `i` is `i + n` modulo `2n`, where `n` is the -number of letters in `p`. +Convert a Presentation to an InversePresentation. """ to(::Type{InversePresentation}, p::Presentation) = LibSemigroups.to_inverse_presentation_word(p) @@ -15,6 +13,6 @@ to(::Type{InversePresentation}, p::Presentation) = """ to(ip::InversePresentation) -> InversePresentation -Return a copy of the inverse presentation `ip`. +Convert an InversePresentation to an InversePresentation. """ to(ip::InversePresentation) = InversePresentation(ip) diff --git a/src/to-knuth-bendix.jl b/src/to-knuth-bendix.jl index 2d660d5..d9ef77e 100644 --- a/src/to-knuth-bendix.jl +++ b/src/to-knuth-bendix.jl @@ -5,7 +5,7 @@ to-knuth-bendix.jl - conversions to KnuthBendix """ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) -> KnuthBendix -Convert a Froidure-Pin object into a Knuth-Bendix instance. +Convert a Froidure-Pin to a Knuth-Bendix. """ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = LibSemigroups.to_knuth_bendix_from_fpb(kind, fp.cxx_obj) @@ -13,7 +13,7 @@ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = """ to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) -> KnuthBendix -Convert a Todd-Coxeter object into a Knuth-Bendix instance. +Convert a Todd-Coxeter to a Knuth-Bendix. """ to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) = LibSemigroups.to_knuth_bendix_from_tc(kind, tc) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index b5ad9d9..717516a 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -5,47 +5,41 @@ to-presentation.jl - to Presentation conversion """ to(p::Presentation) -> Presentation -Return a copy of the presentation `p`. +Convert a Presentation to a Presentation. """ to(p::Presentation) = Presentation(p) """ to(c::Congruence) -> Presentation -Return the presentation underlying the congruence `c`. +Convert a Congruence to a Presentation. """ to(c::Congruence) = LibSemigroups.to_presentation_word(c) """ to(k::Kambites) -> Presentation -Return a [`Presentation`](@ref Semigroups.Presentation) corresponding -to the conversion from the Kambites `k`. +Convert a Kambites to a Presentation. """ to(k::Kambites) = LibSemigroups.to_presentation_word(k) """ to(tc::ToddCoxeter) -> Presentation -Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to -`tc`. +Convert a Todd-Coxeter to a Presentation. """ to(tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) """ to(kb::KnuthBendix) -> Presentation -Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to -the active rules of `kb`. +Convert a Knuth-Bendix to a Presentation. """ to(kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) """ to(fp::FroidurePin) -> Presentation -Return a [`Presentation`](@ref Semigroups.Presentation) corresponding to -`fp`. - -The Froidure-Pin object should be fully enumerated before conversion. +Convert a Froidure-Pin to a Presentation. """ to(fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) diff --git a/src/to-todd-coxeter.jl b/src/to-todd-coxeter.jl index a995a7b..02c9c2b 100644 --- a/src/to-todd-coxeter.jl +++ b/src/to-todd-coxeter.jl @@ -6,7 +6,7 @@ to-todd-coxeter.jl - conversions to ToddCoxeter to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg::WordGraph) -> ToddCoxeter -Convert a Froidure-Pin Cayley graph into a Todd-Coxeter instance. +Convert a Froidure-Pin graph to a Todd-Coxeter. """ to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg) = LibSemigroups.to_todd_coxeter_from_fpb(kind, fp.cxx_obj, wg) @@ -14,7 +14,7 @@ to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg) = """ to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) -> ToddCoxeter -Convert a Knuth-Bendix object into a Todd-Coxeter instance. +Convert a Knuth-Bendix to a Todd-Coxeter. """ to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) = LibSemigroups.to_todd_coxeter_from_kb(kind, kb) From 74493742e68b85642b6f205cecd7cdc4e65999e5 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Wed, 23 Sep 2026 17:43:59 +0100 Subject: [PATCH 09/28] made everything the same --- deps/src/knuth-bendix.cpp | 5 ----- deps/src/to-presentation.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/deps/src/knuth-bendix.cpp b/deps/src/knuth-bendix.cpp index 6447707..f05639a 100644 --- a/deps/src/knuth-bendix.cpp +++ b/deps/src/knuth-bendix.cpp @@ -104,11 +104,6 @@ namespace libsemigroups_julia { auto type = m.add_type("KnuthBendixRewriteTrie", jlcxx::julia_base_type()); - // to_presentation_word, returns Presentation - type.method("to_presentation_word", [](KB& self) { - return libsemigroups::to>(self); - }); - //////////////////////////////////////////////////////////////////////// // Constructors //////////////////////////////////////////////////////////////////////// diff --git a/deps/src/to-presentation.cpp b/deps/src/to-presentation.cpp index 019978c..3e046a0 100644 --- a/deps/src/to-presentation.cpp +++ b/deps/src/to-presentation.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -37,6 +38,11 @@ namespace libsemigroups_julia { return libsemigroups::to< libsemigroups::Presentation>(tc); }); + m.method("to_presentation_word", + [](libsemigroups::KnuthBendix& kb) { + return libsemigroups::to< + libsemigroups::Presentation>(kb); + }); } } // namespace libsemigroups_julia From 3c3dd82f03ebe86244af28f37f43cfe2e4957ac6 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Fri, 25 Sep 2026 10:31:34 +0100 Subject: [PATCH 10/28] Fixed the froidure pin to be in presentation and added docs --- deps/src/froidure-pin.cpp | 6 ------ deps/src/knuth-bendix.cpp | 5 ++--- deps/src/to-froidure-pin.cpp | 20 ++++++++++++++++++++ deps/src/to-knuth-bendix.cpp | 21 +++++++++++++++++++++ deps/src/to-presentation.cpp | 35 +++++++++++++++++++++++++++++++++-- deps/src/to-todd-coxeter.cpp | 21 +++++++++++++++++++++ 6 files changed, 97 insertions(+), 11 deletions(-) diff --git a/deps/src/froidure-pin.cpp b/deps/src/froidure-pin.cpp index c21fcce..f7ba4c9 100644 --- a/deps/src/froidure-pin.cpp +++ b/deps/src/froidure-pin.cpp @@ -24,7 +24,6 @@ #include #include -#include #include #include @@ -110,11 +109,6 @@ namespace libsemigroups_julia { auto type = m.add_type(name, jlcxx::julia_base_type()); - // to_presentation_word, returns Presentation - type.method("to_presentation_word", [](FP& self) { - return libsemigroups::to>(self); - }); - //////////////////////////////////////////////////////////////////// // 1. Constructors — 1-4 generator arg lambdas // (Can't construct StdVector of wrapped types Julia-side) diff --git a/deps/src/knuth-bendix.cpp b/deps/src/knuth-bendix.cpp index f05639a..6e23225 100644 --- a/deps/src/knuth-bendix.cpp +++ b/deps/src/knuth-bendix.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "cong-common.hpp" @@ -80,8 +79,8 @@ namespace libsemigroups_julia { using CongruenceCommon = libsemigroups::detail::CongruenceCommon; using KBImpl = libsemigroups::detail::KnuthBendixImpl< - libsemigroups::detail::RewriteTrie, - libsemigroups::ShortLexCompare>; + libsemigroups::detail::RewriteTrie, + libsemigroups::ShortLexCompare>; using KB = libsemigroups::KnuthBendix; diff --git a/deps/src/to-froidure-pin.cpp b/deps/src/to-froidure-pin.cpp index 158c55e..3042a4e 100644 --- a/deps/src/to-froidure-pin.cpp +++ b/deps/src/to-froidure-pin.cpp @@ -1,3 +1,22 @@ +// +// Semigroups.jl +// Copyright (C) 2026, Joseph E. Macgowan +// +// 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 . +// + +// CRITICAL: libsemigroups_julia.hpp MUST be included first (fmt consteval fix) #include "libsemigroups_julia.hpp" #include @@ -12,6 +31,7 @@ namespace libsemigroups_julia { void define_to_froidure_pin(jl::Module& m) { using FP = libsemigroups::FroidurePin>; + // Complete WordGraph to FroidurePin>. m.method("to_froidure_pin_from_wg", [](libsemigroups::WordGraph const& wg) { return libsemigroups::to(wg); diff --git a/deps/src/to-knuth-bendix.cpp b/deps/src/to-knuth-bendix.cpp index d9ba217..d86822e 100644 --- a/deps/src/to-knuth-bendix.cpp +++ b/deps/src/to-knuth-bendix.cpp @@ -1,3 +1,22 @@ +// +// Semigroups.jl +// Copyright (C) 2026, Joseph E. Macgowan +// +// 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 . +// + +// CRITICAL: libsemigroups_julia.hpp MUST be included first (fmt consteval fix) #include "libsemigroups_julia.hpp" #include @@ -13,12 +32,14 @@ namespace libsemigroups_julia { libsemigroups::detail::RewriteTrie, libsemigroups::ShortLexCompare>; + // FroidurePinBase and congruence kind to KnuthBendix. m.method("to_knuth_bendix_from_fpb", [](libsemigroups::congruence_kind knd, libsemigroups::FroidurePinBase& fpb) { return libsemigroups::to(knd, fpb); }); + // ToddCoxeter and congruence kind to KnuthBendix. m.method("to_knuth_bendix_from_tc", [](libsemigroups::congruence_kind knd, libsemigroups::ToddCoxeter& tc) { diff --git a/deps/src/to-presentation.cpp b/deps/src/to-presentation.cpp index 3e046a0..b852708 100644 --- a/deps/src/to-presentation.cpp +++ b/deps/src/to-presentation.cpp @@ -1,16 +1,34 @@ #include "libsemigroups_julia.hpp" +#include #include +#include #include #include #include #include #include +#include #include namespace libsemigroups_julia { + namespace { + + // Register the conversion for one concrete FroidurePin instantiation. + template + void define_froidure_pin_to_presentation(jl::Module& m) { + using FP = libsemigroups::FroidurePin; + m.method("to_presentation_word", [](FP& self) { + return libsemigroups::to< + libsemigroups::Presentation>(self); + }); + } + + } // namespace + void define_to_presentation(jl::Module& m) { + // Presentation to InversePresentation. m.method( "to_inverse_presentation_word", [](libsemigroups::Presentation const& p) { @@ -23,6 +41,7 @@ namespace libsemigroups_julia { return libsemigroups::InversePresentation( ip); }); + // Congruence, Kambites, ToddCoxeter, or KnuthBendix to Presentation. m.method("to_presentation_word", [](libsemigroups::Congruence& c) { return libsemigroups::to< @@ -39,10 +58,22 @@ namespace libsemigroups_julia { libsemigroups::Presentation>(tc); }); m.method("to_presentation_word", - [](libsemigroups::KnuthBendix& kb) { + [](libsemigroups::KnuthBendix& kb) { return libsemigroups::to< - libsemigroups::Presentation>(kb); + libsemigroups::Presentation>(kb); }); + + // FroidurePin to Presentation for all 10 E instantiations. + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation>(m); + define_froidure_pin_to_presentation(m); } } // namespace libsemigroups_julia diff --git a/deps/src/to-todd-coxeter.cpp b/deps/src/to-todd-coxeter.cpp index d7ca753..2bacc0f 100644 --- a/deps/src/to-todd-coxeter.cpp +++ b/deps/src/to-todd-coxeter.cpp @@ -1,3 +1,22 @@ +// +// Semigroups.jl +// Copyright (C) 2026, Joseph E. Macgowan +// +// 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 . +// + +// CRITICAL: libsemigroups_julia.hpp MUST be included first (fmt consteval fix) #include "libsemigroups_julia.hpp" #include @@ -11,6 +30,7 @@ namespace libsemigroups_julia { void define_to_todd_coxeter(jl::Module& m) { + // FroidurePinBase graph and congruence kind to ToddCoxeter. m.method("to_todd_coxeter_from_fpb", [](libsemigroups::congruence_kind knd, libsemigroups::FroidurePinBase& fpb, @@ -20,6 +40,7 @@ namespace libsemigroups_julia { knd, fpb, wg); }); + // KnuthBendix and congruence kind to ToddCoxeter. m.method("to_todd_coxeter_from_kb", [](libsemigroups::congruence_kind knd, libsemigroups::KnuthBendix& kb) { From 1b49a719bc606ebed81adb1748a2d3a636a172fa Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Fri, 25 Sep 2026 10:37:08 +0100 Subject: [PATCH 11/28] Added docs --- src/to-froidure-pin.jl | 2 +- test/test_to_congruence.jl | 3 +++ test/test_to_froidure_pin.jl | 2 ++ test/test_to_inverse_presentation.jl | 3 +++ test/test_to_knuth_bendix.jl | 3 +++ test/test_to_presentation.jl | 12 ++++++++++++ test/test_to_todd_coxeter.jl | 3 +++ 7 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/to-froidure-pin.jl b/src/to-froidure-pin.jl index e54d2de..138ce9b 100644 --- a/src/to-froidure-pin.jl +++ b/src/to-froidure-pin.jl @@ -5,7 +5,7 @@ to-froidure-pin.jl - conversions to FroidurePin """ to(::Type{FroidurePin}, wg::WordGraph) -> FroidurePin{Transf{UInt8}} -Convert a complete WordGraph to a Froidure-Pin +Convert a complete WordGraph to a Froidure-Pin. """ to(::Type{FroidurePin}, wg::WordGraph) = FroidurePin{Transf{UInt8}}(LibSemigroups.to_froidure_pin_from_wg(wg)) diff --git a/test/test_to_congruence.jl b/test/test_to_congruence.jl index f6d3c0b..1f08e4f 100644 --- a/test/test_to_congruence.jl +++ b/test/test_to_congruence.jl @@ -3,6 +3,7 @@ using Semigroups @testset verbose = true "to Congruence" begin @testset "scaffolding" begin + # Check both public conversion signatures. @test hasmethod( Semigroups.to, Tuple{Type{Congruence},congruence_kind,FroidurePin,Any}, @@ -11,6 +12,7 @@ using Semigroups end @testset "from FroidurePin" begin + # Convert the right Cayley graph of a completed Froidure-Pin. fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) run!(fp) c = Semigroups.to(Congruence, twosided, fp, right_cayley_graph(fp)) @@ -20,6 +22,7 @@ using Semigroups end @testset "from WordGraph" begin + # Convert a graph directly without a Froidure-Pin wrapper. wg = WordGraph(2, 1) target!(wg, 1, 1, 2) target!(wg, 2, 1, 2) diff --git a/test/test_to_froidure_pin.jl b/test/test_to_froidure_pin.jl index 269667a..a88a850 100644 --- a/test/test_to_froidure_pin.jl +++ b/test/test_to_froidure_pin.jl @@ -3,10 +3,12 @@ using Semigroups @testset verbose = true "to FroidurePin" begin @testset "scaffolding" begin + # Check the WordGraph conversion method is available. @test hasmethod(Semigroups.to, Tuple{Type{FroidurePin},WordGraph}) end @testset "from WordGraph" begin + # The current binding produces the UInt8 transformation variant. wg = WordGraph(2, 1) target!(wg, 1, 1, 2) target!(wg, 2, 1, 2) diff --git a/test/test_to_inverse_presentation.jl b/test/test_to_inverse_presentation.jl index 5a73cd8..fb829e0 100644 --- a/test/test_to_inverse_presentation.jl +++ b/test/test_to_inverse_presentation.jl @@ -3,11 +3,13 @@ using Semigroups @testset verbose = true "to InversePresentation" begin @testset "scaffolding" begin + # Check conversion from Presentation and copying an inverse presentation. @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},Presentation}) @test hasmethod(Semigroups.to, Tuple{InversePresentation}) end @testset "from Presentation" begin + # Conversion adds inverse generators and preserves the original rules. p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 2], [2, 1]) @@ -22,6 +24,7 @@ using Semigroups end @testset "copy" begin + # Copy conversion must produce an equal but distinct wrapper. p = Presentation() set_alphabet!(p, 1) ip = InversePresentation(p) diff --git a/test/test_to_knuth_bendix.jl b/test/test_to_knuth_bendix.jl index f08a8a3..b47beff 100644 --- a/test/test_to_knuth_bendix.jl +++ b/test/test_to_knuth_bendix.jl @@ -3,11 +3,13 @@ using Semigroups @testset verbose = true "to KnuthBendix" begin @testset "scaffolding" begin + # Check conversions from Froidure-Pin and Todd-Coxeter. @test hasmethod(Semigroups.to, Tuple{Type{KnuthBendix},congruence_kind,FroidurePin}) @test hasmethod(Semigroups.to, Tuple{Type{KnuthBendix},congruence_kind,ToddCoxeter}) end @testset "from FroidurePin" begin + # Conversion from a completed Froidure-Pin creates active rules. fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) run!(fp) kb = Semigroups.to(KnuthBendix, twosided, fp) @@ -19,6 +21,7 @@ using Semigroups end @testset "from ToddCoxeter" begin + # Conversion preserves the Todd-Coxeter presentation. p = Presentation() set_alphabet!(p, 1) add_rule_no_checks!(p, [1, 1], [1]) diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index 96d5313..1696677 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -3,6 +3,7 @@ using Semigroups @testset verbose = true "to Presentation" begin @testset "scaffolding" begin + # Check every public conversion to Presentation. @test isdefined(Semigroups, :to) @test hasmethod(Semigroups.to, Tuple{Presentation}) @test hasmethod(Semigroups.to, Tuple{Congruence}) @@ -13,6 +14,7 @@ using Semigroups end @testset "Presentation conversion" begin + # Copying preserves presentation data without aliasing the wrapper. p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 1], [2]) @@ -29,6 +31,7 @@ using Semigroups end @testset "Congruence conversion" begin + # A congruence converts back to its defining presentation. p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 1], [2]) @@ -41,6 +44,7 @@ using Semigroups end @testset "conversion with no rules" begin + # Conversion also preserves an alphabet when there are no rules. p = Presentation() set_alphabet!(p, 3) k = Kambites(twosided, p) @@ -54,6 +58,7 @@ using Semigroups end @testset "conversion of alphabet + rules" begin + # Conversion preserves both alphabet and rules. p = Presentation() set_alphabet!(p, 3) add_rule_no_checks!(p, [1, 2, 1], [2, 3]) @@ -69,6 +74,7 @@ using Semigroups end @testset "contains empty word" begin + # The empty-word flag is retained by conversion. p = Presentation() set_alphabet!(p, 1) set_contains_empty_word!(p, true) @@ -81,6 +87,7 @@ using Semigroups end @testset "matches Kambites presentation" begin + # The converted presentation matches Kambites exactly. p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 1], [2]) @@ -94,6 +101,7 @@ using Semigroups end @testset "ToddCoxeter conversion" begin + # Conversion preserves the Todd-Coxeter presentation data. p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 1], [2]) @@ -108,6 +116,7 @@ using Semigroups end @testset "ToddCoxeter conversion contains empty word" begin + # Check the empty-word flag for Todd-Coxeter conversion. p = Presentation() set_alphabet!(p, 1) set_contains_empty_word!(p, true) @@ -117,6 +126,7 @@ using Semigroups end @testset "KnuthBendix conversion" begin + # Conversion exposes the active Knuth-Bendix rules. p = Presentation() set_alphabet!(p, 2) add_rule_no_checks!(p, [1, 1], [1]) @@ -132,6 +142,7 @@ using Semigroups end @testset "KnuthBendix conversion contains empty word" begin + # Check the empty-word flag for Knuth-Bendix conversion. p = Presentation() set_alphabet!(p, 1) set_contains_empty_word!(p, true) @@ -141,6 +152,7 @@ using Semigroups end @testset "FroidurePin conversion" begin + # Conversion produces a valid presentation from Froidure-Pin. fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) run!(fp) diff --git a/test/test_to_todd_coxeter.jl b/test/test_to_todd_coxeter.jl index 3dd19d6..cb2d292 100644 --- a/test/test_to_todd_coxeter.jl +++ b/test/test_to_todd_coxeter.jl @@ -3,6 +3,7 @@ using Semigroups @testset verbose = true "to ToddCoxeter" begin @testset "scaffolding" begin + # Check conversions from Froidure-Pin and Knuth-Bendix. @test hasmethod( Semigroups.to, Tuple{Type{ToddCoxeter},congruence_kind,FroidurePin,Any}, @@ -11,6 +12,7 @@ using Semigroups end @testset "from FroidurePin" begin + # Convert a completed Froidure-Pin graph. fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) tc = Semigroups.to(ToddCoxeter, twosided, fp, right_cayley_graph(fp)) @@ -19,6 +21,7 @@ using Semigroups end @testset "from KnuthBendix" begin + # Convert a Knuth-Bendix instance built from a presentation. p = Presentation() set_alphabet!(p, 1) add_rule_no_checks!(p, [1, 1], [1]) From d66f57fff8c444e41a55090e224cdaebc03c2aba Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 6 Oct 2026 12:41:19 +0100 Subject: [PATCH 12/28] Refactor FroidurePin and update conversions to use UInt32; enhance documentation and tests --- deps/src/to-froidure-pin.cpp | 4 ++-- src/cong-common.jl | 23 ++++++++++++++++++++++ src/to-cong.jl | 28 +++++++++++++++++++++++++-- src/to-froidure-pin.jl | 19 +++++++++++++++--- src/to-inverse-presentation.jl | 7 +++++++ src/to-knuth-bendix.jl | 14 ++++++++++++-- src/to-presentation.jl | 35 ++++++++++++++++++++++++++++------ src/to-todd-coxeter.jl | 30 +++++++++++++++++++++++++++-- test/test_to_congruence.jl | 16 ++++++++++++++++ test/test_to_froidure_pin.jl | 8 +++++--- test/test_to_todd_coxeter.jl | 10 ++++++++++ 11 files changed, 174 insertions(+), 20 deletions(-) diff --git a/deps/src/to-froidure-pin.cpp b/deps/src/to-froidure-pin.cpp index 3042a4e..e52e6f3 100644 --- a/deps/src/to-froidure-pin.cpp +++ b/deps/src/to-froidure-pin.cpp @@ -29,9 +29,9 @@ namespace libsemigroups_julia { void define_to_froidure_pin(jl::Module& m) { - using FP = libsemigroups::FroidurePin>; + using FP = libsemigroups::FroidurePin>; - // Complete WordGraph to FroidurePin>. + // Complete WordGraph to FroidurePin>. The wider m.method("to_froidure_pin_from_wg", [](libsemigroups::WordGraph const& wg) { return libsemigroups::to(wg); diff --git a/src/cong-common.jl b/src/cong-common.jl index 71e4f7d..33fc472 100644 --- a/src/cong-common.jl +++ b/src/cong-common.jl @@ -24,6 +24,29 @@ const CongruenceCommon = LibSemigroups.CongruenceCommon const Congruence = LibSemigroups.CongruenceWord +""" + number_of_classes(cong::CongruenceCommon) -> UInt64 + +Return the number of classes of `cong`, triggering a full enumeration if +needed. +""" +number_of_classes(cong::CongruenceCommon) = + @wrap_libsemigroups_call LibSemigroups.number_of_classes(cong) + +""" + presentation(cong::CongruenceCommon) -> Presentation + +Return the presentation associated with `cong`. +""" +presentation(cong::CongruenceCommon) = LibSemigroups.presentation(cong) + +""" + kind(cong::CongruenceCommon) -> congruence_kind + +Return whether `cong` is a one-sided or two-sided congruence. +""" +kind(cong::CongruenceCommon) = LibSemigroups.kind(cong) + _words_to_cpp(words::AbstractVector{<:AbstractVector{<:Integer}}) = Any[_word_to_cpp(word) for word in words] diff --git a/src/to-cong.jl b/src/to-cong.jl index 08a33bf..8172349 100644 --- a/src/to-cong.jl +++ b/src/to-cong.jl @@ -7,14 +7,38 @@ to-cong.jl - conversions to Congruence wg) -> Congruence Convert a Froidure-Pin graph to a Congruence. + +Julia implementation of libsemigroups' `to(kind, fp, wg)` +conversion. `wg` must be either the left or right Cayley graph of `fp`. +The result represents the trivial congruence over the semigroup defined by +`fp`. + +`kind` selects whether the resulting congruence is one-sided or two-sided. + +# Throws + +- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if `wg` is not + the left or right Cayley graph of `fp`. """ to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) = - LibSemigroups.to_congruence_from_fpb(kind, fp.cxx_obj, wg) + @wrap_libsemigroups_call LibSemigroups.to_congruence_from_fpb( + kind, + fp.cxx_obj, + wg, + ) """ to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) -> Congruence Convert a WordGraph to a Congruence. + +Julia implementation of libsemigroups' `to(kind, wg)` +conversion. The result represents the trivial congruence over `wg`. + +`kind` selects whether the resulting congruence is one-sided or two-sided. + +The graph is added to the constructed congruence as-is; no checks are made +that the resulting Todd-Coxeter or Congruence object is valid. """ to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) = - LibSemigroups.to_congruence_from_wg(kind, wg) + @wrap_libsemigroups_call LibSemigroups.to_congruence_from_wg(kind, wg) diff --git a/src/to-froidure-pin.jl b/src/to-froidure-pin.jl index 138ce9b..059101f 100644 --- a/src/to-froidure-pin.jl +++ b/src/to-froidure-pin.jl @@ -3,9 +3,22 @@ to-froidure-pin.jl - conversions to FroidurePin """ """ - to(::Type{FroidurePin}, wg::WordGraph) -> FroidurePin{Transf{UInt8}} + to(::Type{FroidurePin}, wg::WordGraph) -> FroidurePin{Transf{UInt32}} -Convert a complete WordGraph to a Froidure-Pin. +Convert a complete `WordGraph` to a Froidure-Pin whose elements are +`Transf{UInt32}`. This is the Julia implementation of +libsemigroups' `to(wg)` conversion. Each graph label becomes a +transformation generator, with the transformation image determined by the +edge target for each node. + +The graph must be complete, and every target must be a node of the graph. + +# Throws + +- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if the graph is + incomplete or contains an out-of-range target. """ to(::Type{FroidurePin}, wg::WordGraph) = - FroidurePin{Transf{UInt8}}(LibSemigroups.to_froidure_pin_from_wg(wg)) + FroidurePin{Transf{UInt32}}(@wrap_libsemigroups_call begin + LibSemigroups.to_froidure_pin_from_wg(wg) + end) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl index aba1adb..33eb25b 100644 --- a/src/to-inverse-presentation.jl +++ b/src/to-inverse-presentation.jl @@ -6,6 +6,11 @@ to-inverse-presentation.jl - conversions to InversePresentation to(::Type{InversePresentation}, p::Presentation) -> InversePresentation Convert a Presentation to an InversePresentation. + +Julia implementation of libsemigroups' `to(p)` +conversion. The resulting inverse presentation has the same alphabet and +rules as `p`, with inverse data initialised for the inverse-presentation +representation. """ to(::Type{InversePresentation}, p::Presentation) = LibSemigroups.to_inverse_presentation_word(p) @@ -14,5 +19,7 @@ to(::Type{InversePresentation}, p::Presentation) = to(ip::InversePresentation) -> InversePresentation Convert an InversePresentation to an InversePresentation. + +Return a copy of `ip`. """ to(ip::InversePresentation) = InversePresentation(ip) diff --git a/src/to-knuth-bendix.jl b/src/to-knuth-bendix.jl index d9ef77e..c296a63 100644 --- a/src/to-knuth-bendix.jl +++ b/src/to-knuth-bendix.jl @@ -5,7 +5,14 @@ to-knuth-bendix.jl - conversions to KnuthBendix """ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) -> KnuthBendix -Convert a Froidure-Pin to a Knuth-Bendix. +Julia implementation of libsemigroups' `to(kind, fp)` +conversion. It constructs a Knuth-Bendix instance from the presentation +obtained from `fp` and represents the trivial congruence over that semigroup. +`kind` selects whether the resulting congruence is one-sided or two-sided. + +The conversion does not enumerate `fp` before obtaining its presentation. +Run `fp` first if the presentation must describe the fully enumerated +semigroup. """ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = LibSemigroups.to_knuth_bendix_from_fpb(kind, fp.cxx_obj) @@ -13,7 +20,10 @@ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = """ to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) -> KnuthBendix -Convert a Todd-Coxeter to a Knuth-Bendix. +Julia implementation of libsemigroups' `to(kind, tc)` +conversion. It constructs a Knuth-Bendix instance from `tc.presentation()` +and represents the trivial congruence over the semigroup defined by `tc`. +`kind` selects whether the resulting congruence is one-sided or two-sided. """ to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) = LibSemigroups.to_knuth_bendix_from_tc(kind, tc) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index 717516a..81eb136 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -5,41 +5,64 @@ to-presentation.jl - to Presentation conversion """ to(p::Presentation) -> Presentation -Convert a Presentation to a Presentation. +Julia implementation of the identity conversion for `Presentation`. The +returned presentation is a copy of `p`. """ to(p::Presentation) = Presentation(p) """ to(c::Congruence) -> Presentation -Convert a Congruence to a Presentation. +Julia implementation of libsemigroups' `to(c)` conversion. +Return the presentation used to construct or initialise `c`. + +This conversion does not enumerate `c`, so the returned presentation may not +yet describe the completed congruence. Run `c` first if a fully processed +presentation is required. """ to(c::Congruence) = LibSemigroups.to_presentation_word(c) """ to(k::Kambites) -> Presentation -Convert a Kambites to a Presentation. +Julia implementation of libsemigroups' `to(k)` conversion. +Return a presentation equivalent to the presentation used to construct or +initialise `k`. + +This conversion does not enumerate `k`. """ to(k::Kambites) = LibSemigroups.to_presentation_word(k) """ to(tc::ToddCoxeter) -> Presentation -Convert a Todd-Coxeter to a Presentation. +Julia implementation of libsemigroups' `to(tc)` conversion. +Return the presentation used to construct or initialise `tc`. + +This conversion does not enumerate `tc`. """ to(tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) """ to(kb::KnuthBendix) -> Presentation -Convert a Knuth-Bendix to a Presentation. +Julia implementation of libsemigroups' `to(kb)` conversion. +Return a presentation using the currently active rules of `kb`. + +This conversion does not enumerate `kb`, so the returned presentation may not +yet describe the completed semigroup or monoid. Run `kb` first if a fully +processed presentation is required. """ to(kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) """ to(fp::FroidurePin) -> Presentation -Convert a Froidure-Pin to a Presentation. +Julia implementation of libsemigroups' `to(fp)` conversion. +Return a presentation using the currently known rules of `fp`. + +This conversion does not enumerate `fp`, so the returned presentation may not +yet describe the semigroup or monoid represented by `fp`. Run `fp` first if a +fully processed presentation is required. """ to(fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) diff --git a/src/to-todd-coxeter.jl b/src/to-todd-coxeter.jl index 02c9c2b..8c248f7 100644 --- a/src/to-todd-coxeter.jl +++ b/src/to-todd-coxeter.jl @@ -7,14 +7,40 @@ to-todd-coxeter.jl - conversions to ToddCoxeter wg::WordGraph) -> ToddCoxeter Convert a Froidure-Pin graph to a Todd-Coxeter. + +Julia implementation of libsemigroups' `to(kind, fp, wg)` +conversion. `wg` must be either the left or right Cayley graph of `fp`. +The result represents the trivial congruence over the semigroup defined by +`fp`. +`kind` selects whether the resulting congruence is one-sided or two-sided. + +# Throws + +- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if `wg` is not + the left or right Cayley graph of `fp`. """ to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg) = - LibSemigroups.to_todd_coxeter_from_fpb(kind, fp.cxx_obj, wg) + @wrap_libsemigroups_call LibSemigroups.to_todd_coxeter_from_fpb( + kind, + fp.cxx_obj, + wg, + ) """ to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) -> ToddCoxeter Convert a Knuth-Bendix to a Todd-Coxeter. + +Julia implementation of libsemigroups' `to(kind, kb)` +conversion. It uses the right Cayley graph of the semigroup represented by +`kb` and returns the corresponding trivial congruence. +`kind` selects whether the resulting congruence is one-sided or two-sided. + +# Throws + +- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if `kb` does not + represent a two-sided congruence or has infinitely many classes. In the + infinite case, use `ToddCoxeter(kind, presentation(kb))` instead. """ to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) = - LibSemigroups.to_todd_coxeter_from_kb(kind, kb) + @wrap_libsemigroups_call LibSemigroups.to_todd_coxeter_from_kb(kind, kb) diff --git a/test/test_to_congruence.jl b/test/test_to_congruence.jl index 1f08e4f..807532d 100644 --- a/test/test_to_congruence.jl +++ b/test/test_to_congruence.jl @@ -19,6 +19,9 @@ using Semigroups @test c isa Congruence @test c isa CongruenceCommon + @test number_of_classes(c) == 6 + @test kind(c) == twosided + @test alphabet(presentation(c)) == [1, 2] end @testset "from WordGraph" begin @@ -31,5 +34,18 @@ using Semigroups @test c isa Congruence @test c isa CongruenceCommon + @test number_of_classes(c) == 1 + @test kind(c) == twosided + @test alphabet(presentation(c)) == [1] + end + + @testset "errors are translated" begin + fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) + @test_throws LibsemigroupsError Semigroups.to( + Congruence, + twosided, + fp, + WordGraph(2, 1), + ) end end diff --git a/test/test_to_froidure_pin.jl b/test/test_to_froidure_pin.jl index a88a850..6458ffb 100644 --- a/test/test_to_froidure_pin.jl +++ b/test/test_to_froidure_pin.jl @@ -3,20 +3,22 @@ using Semigroups @testset verbose = true "to FroidurePin" begin @testset "scaffolding" begin - # Check the WordGraph conversion method is available. @test hasmethod(Semigroups.to, Tuple{Type{FroidurePin},WordGraph}) end @testset "from WordGraph" begin - # The current binding produces the UInt8 transformation variant. wg = WordGraph(2, 1) target!(wg, 1, 1, 2) target!(wg, 2, 1, 2) fp = Semigroups.to(FroidurePin, wg) - @test fp isa FroidurePin{Transf{UInt8}} + @test fp isa FroidurePin{Transf{UInt32}} @test number_of_generators(fp) == 1 @test degree(fp) == 2 end + + @testset "errors are translated" begin + @test_throws LibsemigroupsError Semigroups.to(FroidurePin, WordGraph(2, 1)) + end end diff --git a/test/test_to_todd_coxeter.jl b/test/test_to_todd_coxeter.jl index cb2d292..5e2976a 100644 --- a/test/test_to_todd_coxeter.jl +++ b/test/test_to_todd_coxeter.jl @@ -20,6 +20,16 @@ using Semigroups @test alphabet(presentation(tc)) == [1, 2] end + @testset "errors are translated" begin + fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) + @test_throws LibsemigroupsError Semigroups.to( + ToddCoxeter, + twosided, + fp, + WordGraph(2, 1), + ) + end + @testset "from KnuthBendix" begin # Convert a Knuth-Bendix instance built from a presentation. p = Presentation() From d2061087f522a24268527c11612aa90e4005f104 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Tue, 6 Oct 2026 16:05:27 +0100 Subject: [PATCH 13/28] Formatting --- src/cong-common.jl | 2 +- src/to-cong.jl | 6 +----- src/to-todd-coxeter.jl | 6 +----- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/cong-common.jl b/src/cong-common.jl index 33fc472..93c13fb 100644 --- a/src/cong-common.jl +++ b/src/cong-common.jl @@ -31,7 +31,7 @@ Return the number of classes of `cong`, triggering a full enumeration if needed. """ number_of_classes(cong::CongruenceCommon) = - @wrap_libsemigroups_call LibSemigroups.number_of_classes(cong) + @wrap_libsemigroups_call LibSemigroups.number_of_classes(cong) """ presentation(cong::CongruenceCommon) -> Presentation diff --git a/src/to-cong.jl b/src/to-cong.jl index 8172349..d8af8bd 100644 --- a/src/to-cong.jl +++ b/src/to-cong.jl @@ -21,11 +21,7 @@ The result represents the trivial congruence over the semigroup defined by the left or right Cayley graph of `fp`. """ to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) = - @wrap_libsemigroups_call LibSemigroups.to_congruence_from_fpb( - kind, - fp.cxx_obj, - wg, - ) + @wrap_libsemigroups_call LibSemigroups.to_congruence_from_fpb(kind, fp.cxx_obj, wg) """ to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) -> Congruence diff --git a/src/to-todd-coxeter.jl b/src/to-todd-coxeter.jl index 8c248f7..4dfc4b2 100644 --- a/src/to-todd-coxeter.jl +++ b/src/to-todd-coxeter.jl @@ -20,11 +20,7 @@ The result represents the trivial congruence over the semigroup defined by the left or right Cayley graph of `fp`. """ to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg) = - @wrap_libsemigroups_call LibSemigroups.to_todd_coxeter_from_fpb( - kind, - fp.cxx_obj, - wg, - ) + @wrap_libsemigroups_call LibSemigroups.to_todd_coxeter_from_fpb(kind, fp.cxx_obj, wg) """ to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) -> ToddCoxeter From 54eb3634e51f645bd689335aa5bf89f845ed5d53 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan <117601359+jmacgowan@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:41:03 +0100 Subject: [PATCH 14/28] Update src/to-cong.jl Co-authored-by: Jake Swent <94636565+jswent@users.noreply.github.com> --- src/to-cong.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/to-cong.jl b/src/to-cong.jl index d8af8bd..985ce65 100644 --- a/src/to-cong.jl +++ b/src/to-cong.jl @@ -36,5 +36,5 @@ conversion. The result represents the trivial congruence over `wg`. The graph is added to the constructed congruence as-is; no checks are made that the resulting Todd-Coxeter or Congruence object is valid. """ -to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) = +@cxxdereference to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) = @wrap_libsemigroups_call LibSemigroups.to_congruence_from_wg(kind, wg) From dc8d011a87c062e1981a22e4827ea4a51977d859 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan <117601359+jmacgowan@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:41:30 +0100 Subject: [PATCH 15/28] Update src/to-froidure-pin.jl Co-authored-by: Jake Swent <94636565+jswent@users.noreply.github.com> --- src/to-froidure-pin.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/to-froidure-pin.jl b/src/to-froidure-pin.jl index 059101f..5dfa941 100644 --- a/src/to-froidure-pin.jl +++ b/src/to-froidure-pin.jl @@ -18,7 +18,7 @@ The graph must be complete, and every target must be a node of the graph. - [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if the graph is incomplete or contains an out-of-range target. """ -to(::Type{FroidurePin}, wg::WordGraph) = +@cxxdereference to(::Type{FroidurePin}, wg::WordGraph) = FroidurePin{Transf{UInt32}}(@wrap_libsemigroups_call begin LibSemigroups.to_froidure_pin_from_wg(wg) end) From 861b6621607763e096e807812a8f5313e9acae25 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan <117601359+jmacgowan@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:41:50 +0100 Subject: [PATCH 16/28] Update src/to-inverse-presentation.jl Co-authored-by: Jake Swent <94636565+jswent@users.noreply.github.com> --- src/to-inverse-presentation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl index 33eb25b..11dad62 100644 --- a/src/to-inverse-presentation.jl +++ b/src/to-inverse-presentation.jl @@ -13,7 +13,7 @@ rules as `p`, with inverse data initialised for the inverse-presentation representation. """ to(::Type{InversePresentation}, p::Presentation) = - LibSemigroups.to_inverse_presentation_word(p) + @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(p) """ to(ip::InversePresentation) -> InversePresentation From cbcd909a132a2e6d2e3d29c5ea38ed02e491e8bb Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Thu, 8 Oct 2026 10:33:44 +0100 Subject: [PATCH 17/28] Fixed jswent review --- src/to-inverse-presentation.jl | 5 +-- src/to-knuth-bendix.jl | 4 +-- src/to-presentation.jl | 39 +++++++++++---------- test/test_to_inverse_presentation.jl | 12 +++++-- test/test_to_presentation.jl | 51 +++++++++++++++++----------- 5 files changed, 66 insertions(+), 45 deletions(-) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl index 33eb25b..9ba664b 100644 --- a/src/to-inverse-presentation.jl +++ b/src/to-inverse-presentation.jl @@ -16,10 +16,11 @@ to(::Type{InversePresentation}, p::Presentation) = LibSemigroups.to_inverse_presentation_word(p) """ - to(ip::InversePresentation) -> InversePresentation + to(::Type{InversePresentation}, ip::InversePresentation) -> InversePresentation Convert an InversePresentation to an InversePresentation. Return a copy of `ip`. """ -to(ip::InversePresentation) = InversePresentation(ip) +to(::Type{InversePresentation}, ip::InversePresentation) = + InversePresentation(ip) diff --git a/src/to-knuth-bendix.jl b/src/to-knuth-bendix.jl index c296a63..afccb67 100644 --- a/src/to-knuth-bendix.jl +++ b/src/to-knuth-bendix.jl @@ -10,9 +10,7 @@ conversion. It constructs a Knuth-Bendix instance from the presentation obtained from `fp` and represents the trivial congruence over that semigroup. `kind` selects whether the resulting congruence is one-sided or two-sided. -The conversion does not enumerate `fp` before obtaining its presentation. -Run `fp` first if the presentation must describe the fully enumerated -semigroup. +This conversion enumerates `fp` before obtaining its presentation. """ to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = LibSemigroups.to_knuth_bendix_from_fpb(kind, fp.cxx_obj) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index 81eb136..d881d79 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -3,27 +3,28 @@ to-presentation.jl - to Presentation conversion """ """ - to(p::Presentation) -> Presentation + to(::Type{Presentation}, p::Presentation) -> Presentation Julia implementation of the identity conversion for `Presentation`. The returned presentation is a copy of `p`. """ -to(p::Presentation) = Presentation(p) +to(::Type{Presentation}, p::Presentation) = Presentation(p) """ - to(c::Congruence) -> Presentation + to(::Type{Presentation}, c::Congruence) -> Presentation Julia implementation of libsemigroups' `to(c)` conversion. Return the presentation used to construct or initialise `c`. -This conversion does not enumerate `c`, so the returned presentation may not -yet describe the completed congruence. Run `c` first if a fully processed -presentation is required. +This conversion returns the presentation used to construct or initialise `c`. +Congruence-generating pairs are not added to the returned presentation by +running `c`. """ -to(c::Congruence) = LibSemigroups.to_presentation_word(c) +to(::Type{Presentation}, c::Congruence) = + LibSemigroups.to_presentation_word(c) """ - to(k::Kambites) -> Presentation + to(::Type{Presentation}, k::Kambites) -> Presentation Julia implementation of libsemigroups' `to(k)` conversion. Return a presentation equivalent to the presentation used to construct or @@ -31,20 +32,22 @@ initialise `k`. This conversion does not enumerate `k`. """ -to(k::Kambites) = LibSemigroups.to_presentation_word(k) +to(::Type{Presentation}, k::Kambites) = + LibSemigroups.to_presentation_word(k) """ - to(tc::ToddCoxeter) -> Presentation + to(::Type{Presentation}, tc::ToddCoxeter) -> Presentation Julia implementation of libsemigroups' `to(tc)` conversion. Return the presentation used to construct or initialise `tc`. This conversion does not enumerate `tc`. """ -to(tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) +to(::Type{Presentation}, tc::ToddCoxeter) = + LibSemigroups.to_presentation_word(tc) """ - to(kb::KnuthBendix) -> Presentation + to(::Type{Presentation}, kb::KnuthBendix) -> Presentation Julia implementation of libsemigroups' `to(kb)` conversion. Return a presentation using the currently active rules of `kb`. @@ -53,16 +56,16 @@ This conversion does not enumerate `kb`, so the returned presentation may not yet describe the completed semigroup or monoid. Run `kb` first if a fully processed presentation is required. """ -to(kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) +to(::Type{Presentation}, kb::KnuthBendix) = + LibSemigroups.to_presentation_word(kb) """ - to(fp::FroidurePin) -> Presentation + to(::Type{Presentation}, fp::FroidurePin) -> Presentation Julia implementation of libsemigroups' `to(fp)` conversion. Return a presentation using the currently known rules of `fp`. -This conversion does not enumerate `fp`, so the returned presentation may not -yet describe the semigroup or monoid represented by `fp`. Run `fp` first if a -fully processed presentation is required. +This conversion enumerates `fp` before obtaining its presentation. """ -to(fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) +to(::Type{Presentation}, fp::FroidurePin) = + LibSemigroups.to_presentation_word(fp.cxx_obj) diff --git a/test/test_to_inverse_presentation.jl b/test/test_to_inverse_presentation.jl index fb829e0..6610bb4 100644 --- a/test/test_to_inverse_presentation.jl +++ b/test/test_to_inverse_presentation.jl @@ -4,8 +4,14 @@ using Semigroups @testset verbose = true "to InversePresentation" begin @testset "scaffolding" begin # Check conversion from Presentation and copying an inverse presentation. - @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},Presentation}) - @test hasmethod(Semigroups.to, Tuple{InversePresentation}) + @test hasmethod( + Semigroups.to, + Tuple{Type{InversePresentation}, Presentation}, + ) + @test hasmethod( + Semigroups.to, + Tuple{Type{InversePresentation}, InversePresentation}, + ) end @testset "from Presentation" begin @@ -30,7 +36,7 @@ using Semigroups ip = InversePresentation(p) set_inverses!(ip, [1]) - copy = Semigroups.to(ip) + copy = Semigroups.to(InversePresentation, ip) @test copy isa InversePresentation @test copy !== ip diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index 1696677..ac7a0a1 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -5,12 +5,24 @@ using Semigroups @testset "scaffolding" begin # Check every public conversion to Presentation. @test isdefined(Semigroups, :to) - @test hasmethod(Semigroups.to, Tuple{Presentation}) - @test hasmethod(Semigroups.to, Tuple{Congruence}) - @test hasmethod(Semigroups.to, Tuple{Kambites}) - @test hasmethod(Semigroups.to, Tuple{ToddCoxeter}) - @test hasmethod(Semigroups.to, Tuple{KnuthBendix}) - @test hasmethod(Semigroups.to, Tuple{FroidurePin}) + @test hasmethod( + Semigroups.to, Tuple{Type{Presentation}, Presentation} + ) + @test hasmethod( + Semigroups.to, Tuple{Type{Presentation}, Congruence} + ) + @test hasmethod( + Semigroups.to, Tuple{Type{Presentation}, Kambites} + ) + @test hasmethod( + Semigroups.to, Tuple{Type{Presentation}, ToddCoxeter} + ) + @test hasmethod( + Semigroups.to, Tuple{Type{Presentation}, KnuthBendix} + ) + @test hasmethod( + Semigroups.to, Tuple{Type{Presentation}, FroidurePin} + ) end @testset "Presentation conversion" begin @@ -20,7 +32,7 @@ using Semigroups add_rule_no_checks!(p, [1, 1], [2]) set_contains_empty_word!(p, true) - q = Semigroups.to(p) + q = Semigroups.to(Presentation, p) @test q isa Presentation @test q == p @@ -37,7 +49,7 @@ using Semigroups add_rule_no_checks!(p, [1, 1], [2]) c = Congruence(twosided, p) - q = Semigroups.to(c) + q = Semigroups.to(Presentation, c) @test q isa Presentation @test q == p @@ -49,7 +61,7 @@ using Semigroups set_alphabet!(p, 3) k = Kambites(twosided, p) - q = Semigroups.to(k) + q = Semigroups.to(Presentation, k) @test q isa Presentation @test q == presentation(k) @@ -65,7 +77,7 @@ using Semigroups add_rule_no_checks!(p, [3, 3], [1]) k = Kambites(twosided, p) - q = Semigroups.to(k) + q = Semigroups.to(Presentation, k) @test q == presentation(k) @test q == p @@ -80,7 +92,7 @@ using Semigroups set_contains_empty_word!(p, true) k = Kambites(twosided, p) - q = Semigroups.to(k) + q = Semigroups.to(Presentation, k) @test q == presentation(k) @test contains_empty_word(q) @@ -93,7 +105,7 @@ using Semigroups add_rule_no_checks!(p, [1, 1], [2]) k = Kambites(twosided, p) - q = Semigroups.to(k) + q = Semigroups.to(Presentation, k) @test q == presentation(k) @test alphabet(q) == alphabet(presentation(k)) @@ -107,7 +119,7 @@ using Semigroups add_rule_no_checks!(p, [1, 1], [2]) tc = ToddCoxeter(twosided, p) - q = Semigroups.to(tc) + q = Semigroups.to(Presentation, tc) @test q isa Presentation @test q == presentation(tc) @@ -122,7 +134,7 @@ using Semigroups set_contains_empty_word!(p, true) tc = ToddCoxeter(twosided, p) - @test contains_empty_word(Semigroups.to(tc)) + @test contains_empty_word(Semigroups.to(Presentation, tc)) end @testset "KnuthBendix conversion" begin @@ -133,7 +145,7 @@ using Semigroups kb = KnuthBendix(twosided, p) run!(kb) - q = Semigroups.to(kb) + q = Semigroups.to(Presentation, kb) @test q isa Presentation @test alphabet(q) == alphabet(presentation(kb)) @@ -148,20 +160,21 @@ using Semigroups set_contains_empty_word!(p, true) kb = KnuthBendix(twosided, p) - @test contains_empty_word(Semigroups.to(kb)) + @test contains_empty_word(Semigroups.to(Presentation, kb)) end @testset "FroidurePin conversion" begin # Conversion produces a valid presentation from Froidure-Pin. fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - run!(fp) - q = Semigroups.to(fp) + @test !finished(fp) + q = Semigroups.to(Presentation, fp) + + @test finished(fp) @test q isa Presentation @test alphabet(q) == [1, 2] @test number_of_rules(q) > 0 throw_if_bad_alphabet_or_rules(q) end - end From cbc4a5b3da1edcebfdbc4bd98b0875ac4636a016 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Thu, 8 Oct 2026 10:41:10 +0100 Subject: [PATCH 18/28] Format --- src/to-inverse-presentation.jl | 9 ++++----- src/to-presentation.jl | 15 +++++---------- test/test_to_inverse_presentation.jl | 10 ++-------- test/test_to_presentation.jl | 24 ++++++------------------ 4 files changed, 17 insertions(+), 41 deletions(-) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl index 5deabec..a4726b1 100644 --- a/src/to-inverse-presentation.jl +++ b/src/to-inverse-presentation.jl @@ -8,9 +8,9 @@ to-inverse-presentation.jl - conversions to InversePresentation Convert a Presentation to an InversePresentation. Julia implementation of libsemigroups' `to(p)` -conversion. The resulting inverse presentation has the same alphabet and -rules as `p`, with inverse data initialised for the inverse-presentation -representation. +conversion. The resulting inverse presentation has rules equivalent to +those of `p`, over a normalised alphabet with a corresponding inverse +letter for each letter of `p`. """ to(::Type{InversePresentation}, p::Presentation) = @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(p) @@ -22,5 +22,4 @@ Convert an InversePresentation to an InversePresentation. Return a copy of `ip`. """ -to(::Type{InversePresentation}, ip::InversePresentation) = - InversePresentation(ip) +to(::Type{InversePresentation}, ip::InversePresentation) = InversePresentation(ip) diff --git a/src/to-presentation.jl b/src/to-presentation.jl index d881d79..2c9d9d3 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -20,8 +20,7 @@ This conversion returns the presentation used to construct or initialise `c`. Congruence-generating pairs are not added to the returned presentation by running `c`. """ -to(::Type{Presentation}, c::Congruence) = - LibSemigroups.to_presentation_word(c) +to(::Type{Presentation}, c::Congruence) = LibSemigroups.to_presentation_word(c) """ to(::Type{Presentation}, k::Kambites) -> Presentation @@ -32,8 +31,7 @@ initialise `k`. This conversion does not enumerate `k`. """ -to(::Type{Presentation}, k::Kambites) = - LibSemigroups.to_presentation_word(k) +to(::Type{Presentation}, k::Kambites) = LibSemigroups.to_presentation_word(k) """ to(::Type{Presentation}, tc::ToddCoxeter) -> Presentation @@ -43,8 +41,7 @@ Return the presentation used to construct or initialise `tc`. This conversion does not enumerate `tc`. """ -to(::Type{Presentation}, tc::ToddCoxeter) = - LibSemigroups.to_presentation_word(tc) +to(::Type{Presentation}, tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) """ to(::Type{Presentation}, kb::KnuthBendix) -> Presentation @@ -56,8 +53,7 @@ This conversion does not enumerate `kb`, so the returned presentation may not yet describe the completed semigroup or monoid. Run `kb` first if a fully processed presentation is required. """ -to(::Type{Presentation}, kb::KnuthBendix) = - LibSemigroups.to_presentation_word(kb) +to(::Type{Presentation}, kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) """ to(::Type{Presentation}, fp::FroidurePin) -> Presentation @@ -67,5 +63,4 @@ Return a presentation using the currently known rules of `fp`. This conversion enumerates `fp` before obtaining its presentation. """ -to(::Type{Presentation}, fp::FroidurePin) = - LibSemigroups.to_presentation_word(fp.cxx_obj) +to(::Type{Presentation}, fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) diff --git a/test/test_to_inverse_presentation.jl b/test/test_to_inverse_presentation.jl index 6610bb4..eb65ce2 100644 --- a/test/test_to_inverse_presentation.jl +++ b/test/test_to_inverse_presentation.jl @@ -4,14 +4,8 @@ using Semigroups @testset verbose = true "to InversePresentation" begin @testset "scaffolding" begin # Check conversion from Presentation and copying an inverse presentation. - @test hasmethod( - Semigroups.to, - Tuple{Type{InversePresentation}, Presentation}, - ) - @test hasmethod( - Semigroups.to, - Tuple{Type{InversePresentation}, InversePresentation}, - ) + @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},Presentation}) + @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},InversePresentation}) end @testset "from Presentation" begin diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl index ac7a0a1..9701413 100644 --- a/test/test_to_presentation.jl +++ b/test/test_to_presentation.jl @@ -5,24 +5,12 @@ using Semigroups @testset "scaffolding" begin # Check every public conversion to Presentation. @test isdefined(Semigroups, :to) - @test hasmethod( - Semigroups.to, Tuple{Type{Presentation}, Presentation} - ) - @test hasmethod( - Semigroups.to, Tuple{Type{Presentation}, Congruence} - ) - @test hasmethod( - Semigroups.to, Tuple{Type{Presentation}, Kambites} - ) - @test hasmethod( - Semigroups.to, Tuple{Type{Presentation}, ToddCoxeter} - ) - @test hasmethod( - Semigroups.to, Tuple{Type{Presentation}, KnuthBendix} - ) - @test hasmethod( - Semigroups.to, Tuple{Type{Presentation}, FroidurePin} - ) + @test hasmethod(Semigroups.to, Tuple{Type{Presentation},Presentation}) + @test hasmethod(Semigroups.to, Tuple{Type{Presentation},Congruence}) + @test hasmethod(Semigroups.to, Tuple{Type{Presentation},Kambites}) + @test hasmethod(Semigroups.to, Tuple{Type{Presentation},ToddCoxeter}) + @test hasmethod(Semigroups.to, Tuple{Type{Presentation},KnuthBendix}) + @test hasmethod(Semigroups.to, Tuple{Type{Presentation},FroidurePin}) end @testset "Presentation conversion" begin From dc0ea6393a0e92d1b1d51873214860bb9dca96bc Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Thu, 15 Oct 2026 20:32:38 +0100 Subject: [PATCH 19/28] Update to inital presentaitont ype --- deps/src/presentation.cpp | 532 ++++++++++++++++++-------------------- 1 file changed, 251 insertions(+), 281 deletions(-) diff --git a/deps/src/presentation.cpp b/deps/src/presentation.cpp index 71ac3fb..da18607 100644 --- a/deps/src/presentation.cpp +++ b/deps/src/presentation.cpp @@ -28,11 +28,19 @@ #include #include +//////////////////////////////////////////////////////////////////////// +// CxxWrap type traits IsMirroredType and SuperType +//////////////////////////////////////////////////////////////////////// + namespace jlcxx { template <> struct IsMirroredType> : std::false_type {}; + template <> + struct IsMirroredType> + : std::false_type {}; + template <> struct IsMirroredType< libsemigroups::InversePresentation> @@ -47,290 +55,252 @@ namespace jlcxx { namespace libsemigroups_julia { + namespace { + + //////////////////////////////////////////////////////////////////////// + // WordT — bridges jlcxx argument types for word_type vs std::string + //////////////////////////////////////////////////////////////////////// + + template + struct WordT; + + template <> + struct WordT { + using ArgType = jlcxx::ArrayRef; + static libsemigroups::word_type convert(ArgType arr) { + return libsemigroups::word_type(arr.begin(), arr.end()); + } + }; + + template <> + struct WordT { + using ArgType = std::string const&; + static std::string const& convert(ArgType str) { + return str; + } + }; + + //////////////////////////////////////////////////////////////////////// + // bind_presentation — template helper that registers all + // word-type methods for a single Presentation instantiation. + //////////////////////////////////////////////////////////////////////// + + template + void bind_presentation(jl::Module& m, std::string const& name) { + using P = libsemigroups::Presentation; + using Letter = typename P::letter_type; + using Traits = WordT; + using Arg = typename Traits::ArgType; + + auto type = m.add_type

(name); + + //////////////////////////////////////////////////////////////////// + // Constructors & Common Methods + //////////////////////////////////////////////////////////////////// + + type.template constructor<>(); + type.template constructor

(); + + type.method("init!", [](P& self) { self.init(); }); + type.method("alphabet", + [](P const& self) -> Word const& { return self.alphabet(); }); + type.method("set_alphabet_size!", + [](P& self, size_t n) { self.alphabet(n); }); + type.method("alphabet_from_rules!", + [](P& self) { self.alphabet_from_rules(); }); + type.method("letter", [](P const& self, size_t i) -> Letter { + return self.letter(i); + }); + type.method("index_of", [](P const& self, Letter x) -> size_t { + return self.index(x); + }); + type.method("in_alphabet", [](P const& self, Letter x) -> bool { + return self.in_alphabet(x); + }); + type.method("contains_empty_word", [](P const& self) -> bool { + return self.contains_empty_word(); + }); + type.method("set_contains_empty_word!", + [](P& self, bool v) { self.contains_empty_word(v); }); + + type.method("add_generator_no_arg!", + [](P& self) -> Letter { return self.add_generator(); }); + type.method("add_generator!", + [](P& self, Letter x) { self.add_generator(x); }); + type.method("remove_generator!", + [](P& self, Letter x) { self.remove_generator(x); }); + + type.method("number_of_rules", [](P const& self) -> size_t { + return self.rules.size() / 2; + }); + type.method("rule_lhs", [](P const& self, size_t i) -> Word const& { + return self.rules.at(2 * i); + }); + type.method("rule_rhs", [](P const& self, size_t i) -> Word const& { + return self.rules.at(2 * i + 1); + }); + type.method("clear_rules!", [](P& self) { self.rules.clear(); }); + + type.method("throw_if_alphabet_has_duplicates", [](P const& self) { + self.throw_if_alphabet_has_duplicates(); + }); + type.method("throw_if_letter_not_in_alphabet", + [](P const& self, Letter x) { + self.throw_if_letter_not_in_alphabet(x); + }); + type.method("throw_if_bad_rules", + [](P const& self) { self.throw_if_bad_rules(); }); + type.method("throw_if_bad_alphabet_or_rules", + [](P const& self) { self.throw_if_bad_alphabet_or_rules(); }); + + m.method("length_of", [](P const& p) -> size_t { + return libsemigroups::presentation::length(p); + }); + m.method("longest_rule_length", [](P const& p) -> size_t { + return libsemigroups::presentation::longest_rule_length(p); + }); + m.method("shortest_rule_length", [](P const& p) -> size_t { + return libsemigroups::presentation::shortest_rule_length(p); + }); + m.method("is_normalized", [](P const& p) -> bool { + return libsemigroups::presentation::is_normalized(p); + }); + m.method("are_rules_sorted", [](P const& p) -> bool { + return libsemigroups::presentation::are_rules_sorted(p); + }); + m.method("throw_if_odd_number_of_rules", [](P const& p) { + libsemigroups::presentation::throw_if_odd_number_of_rules(p); + }); + m.method("normalize_alphabet!", [](P& p) { + libsemigroups::presentation::normalize_alphabet(p); + }); + m.method("reverse_rules!", + [](P& p) { libsemigroups::presentation::reverse(p); }); + m.method("sort_rules!", + [](P& p) { libsemigroups::presentation::sort_rules(p); }); + m.method("sort_each_rule!", [](P& p) -> bool { + return libsemigroups::presentation::sort_each_rule(p); + }); + m.method("add_identity_rules!", [](P& p, Letter e) { + libsemigroups::presentation::add_identity_rules(p, e); + }); + m.method("add_zero_rules!", [](P& p, Letter z) { + libsemigroups::presentation::add_zero_rules(p, z); + }); + m.method("remove_duplicate_rules!", [](P& p) { + libsemigroups::presentation::remove_duplicate_rules(p); + }); + m.method("remove_trivial_rules!", [](P& p) { + libsemigroups::presentation::remove_trivial_rules(p); + }); + m.method("add_rules!", [](P& p, P const& q) { + libsemigroups::presentation::add_rules(p, q); + }); + m.method("first_unused_letter", [](P const& p) -> Letter { + return libsemigroups::presentation::first_unused_letter(p); + }); + m.method("longest_rule_index", [](P const& p) -> size_t { + auto it = libsemigroups::presentation::longest_rule(p); + return static_cast(std::distance(p.rules.cbegin(), it)); + }); + m.method("shortest_rule_index", [](P const& p) -> size_t { + auto it = libsemigroups::presentation::shortest_rule(p); + return static_cast(std::distance(p.rules.cbegin(), it)); + }); + m.method("to_gap_string", + [](P const& p, std::string const& var_name) -> std::string { + return libsemigroups::presentation::to_gap_string(p, var_name); + }); + m.method("rules_vector", [](P const& p) -> std::vector { + return std::vector(p.rules.cbegin(), p.rules.cend()); + }); + type.method("is_equal", + [](P const& a, P const& b) -> bool { return a == b; }); + type.method("to_human_readable_repr", [](P const& p) -> std::string { + return libsemigroups::to_human_readable_repr(p); + }); + + //////////////////////////////////////////////////////////////////// + // 2. Word-argument specific methods + //////////////////////////////////////////////////////////////////// + + type.method("set_alphabet!", + [](P& self, Arg a) { self.alphabet(Traits::convert(a)); }); + + m.method("add_rule_no_checks!", [](P& p, Arg lhs, Arg rhs) { + libsemigroups::presentation::add_rule_no_checks( + p, Traits::convert(lhs), Traits::convert(rhs)); + }); + + m.method("add_rule!", [](P& p, Arg lhs, Arg rhs) { + libsemigroups::presentation::add_rule( + p, Traits::convert(lhs), Traits::convert(rhs)); + }); + + m.method("contains_rule", [](P& p, Arg lhs, Arg rhs) -> bool { + return libsemigroups::presentation::contains_rule( + p, Traits::convert(lhs), Traits::convert(rhs)); + }); + + m.method("change_alphabet!", [](P& p, Arg a) { + libsemigroups::presentation::change_alphabet(p, Traits::convert(a)); + }); + + m.method("add_inverse_rules!", [](P& p, Arg inverses) { + libsemigroups::presentation::add_inverse_rules( + p, Traits::convert(inverses)); + }); + + m.method("add_inverse_rules_with_identity!", + [](P& p, Arg inverses, Letter e) { + libsemigroups::presentation::add_inverse_rules( + p, Traits::convert(inverses), e); + }); + + m.method("replace_subword!", [](P& p, Arg existing, Arg replacement) { + libsemigroups::presentation::replace_subword( + p, Traits::convert(existing), Traits::convert(replacement)); + }); + + m.method("replace_word!", [](P& p, Arg existing, Arg replacement) { + libsemigroups::presentation::replace_word( + p, Traits::convert(existing), Traits::convert(replacement)); + }); + + m.method("replace_word_with_new_generator!", [](P& p, Arg w) -> Letter { + return libsemigroups::presentation::replace_word_with_new_generator( + p, Traits::convert(w)); + }); + + m.method("index_rule", [](P const& p, Arg lhs, Arg rhs) -> size_t { + return libsemigroups::presentation::index_rule( + p, Traits::convert(lhs), Traits::convert(rhs)); + }); + + m.method("is_rule", [](P const& p, Arg lhs, Arg rhs) -> bool { + return libsemigroups::presentation::is_rule( + p, Traits::convert(lhs), Traits::convert(rhs)); + }); + + m.method("throw_if_bad_inverses", [](P const& p, Arg inverses) { + libsemigroups::presentation::throw_if_bad_inverses( + p, Traits::convert(inverses)); + }); + } + + } // namespace + + //////////////////////////////////////////////////////////////////////// + // define_presentation — register Presentation & InversePresentation + //////////////////////////////////////////////////////////////////////// + void define_presentation(jl::Module& m) { using libsemigroups::Presentation; using libsemigroups::word_type; - auto type = m.add_type>("Presentation"); - type.constructor<>(); - type.constructor const&>(); // copy ctor - - type.method("init!", [](Presentation& self) { self.init(); }); - - type.method("alphabet", - [](Presentation const& self) -> word_type { - return self.alphabet(); - }); - - type.method( - "set_alphabet_size!", - [](Presentation& self, size_t n) { self.alphabet(n); }); - - type.method("set_alphabet!", - [](Presentation& self, jlcxx::ArrayRef a) { - self.alphabet(word_type(a.begin(), a.end())); - }); - - type.method("alphabet_from_rules!", [](Presentation& self) { - self.alphabet_from_rules(); - }); - - type.method("letter", - [](Presentation const& self, size_t i) -> size_t { - return self.letter(i); - }); - - type.method("index_of", - [](Presentation const& self, size_t x) -> size_t { - return self.index(x); - }); - - type.method("in_alphabet", - [](Presentation const& self, size_t x) -> bool { - return self.in_alphabet(x); - }); - - type.method("contains_empty_word", - [](Presentation const& self) -> bool { - return self.contains_empty_word(); - }); - type.method("set_contains_empty_word!", - [](Presentation& self, bool v) { - self.contains_empty_word(v); - }); - - type.method("add_generator_no_arg!", - [](Presentation& self) -> size_t { - return self.add_generator(); - }); - type.method("add_generator!", [](Presentation& self, size_t x) { - self.add_generator(x); - }); - type.method("remove_generator!", - [](Presentation& self, size_t x) { - self.remove_generator(x); - }); - - m.method("add_rule_no_checks!", - [](Presentation& p, - jlcxx::ArrayRef lhs, - jlcxx::ArrayRef rhs) { - word_type l(lhs.begin(), lhs.end()); - word_type r(rhs.begin(), rhs.end()); - libsemigroups::presentation::add_rule_no_checks(p, l, r); - }); - - m.method("add_rule!", - [](Presentation& p, - jlcxx::ArrayRef lhs, - jlcxx::ArrayRef rhs) { - word_type l(lhs.begin(), lhs.end()); - word_type r(rhs.begin(), rhs.end()); - libsemigroups::presentation::add_rule(p, l, r); - }); - - type.method("number_of_rules", - [](Presentation const& self) -> size_t { - return self.rules.size() / 2; - }); - - type.method("rule_lhs", - [](Presentation const& self, size_t i) -> word_type { - return self.rules.at(2 * i); - }); - - type.method("rule_rhs", - [](Presentation const& self, size_t i) -> word_type { - return self.rules.at(2 * i + 1); - }); - - type.method("clear_rules!", - [](Presentation& self) { self.rules.clear(); }); - - type.method("throw_if_alphabet_has_duplicates", - [](Presentation const& self) { - self.throw_if_alphabet_has_duplicates(); - }); - type.method("throw_if_letter_not_in_alphabet", - [](Presentation const& self, size_t x) { - self.throw_if_letter_not_in_alphabet(x); - }); - type.method("throw_if_bad_rules", [](Presentation const& self) { - self.throw_if_bad_rules(); - }); - type.method("throw_if_bad_alphabet_or_rules", - [](Presentation const& self) { - self.throw_if_bad_alphabet_or_rules(); - }); - - m.method("length_of", [](Presentation const& p) -> size_t { - return libsemigroups::presentation::length(p); - }); - m.method("longest_rule_length", - [](Presentation const& p) -> size_t { - return libsemigroups::presentation::longest_rule_length(p); - }); - m.method("shortest_rule_length", - [](Presentation const& p) -> size_t { - return libsemigroups::presentation::shortest_rule_length(p); - }); - m.method("is_normalized", [](Presentation const& p) -> bool { - return libsemigroups::presentation::is_normalized(p); - }); - m.method("are_rules_sorted", [](Presentation const& p) -> bool { - return libsemigroups::presentation::are_rules_sorted(p); - }); - m.method("contains_rule", - [](Presentation& p, - jlcxx::ArrayRef lhs, - jlcxx::ArrayRef rhs) -> bool { - word_type l(lhs.begin(), lhs.end()); - word_type r(rhs.begin(), rhs.end()); - return libsemigroups::presentation::contains_rule(p, l, r); - }); - m.method("throw_if_odd_number_of_rules", - [](Presentation const& p) { - // Fully qualified: the iterator-pair overload is also in scope. - libsemigroups::presentation::throw_if_odd_number_of_rules(p); - }); - - m.method("normalize_alphabet!", [](Presentation& p) { - libsemigroups::presentation::normalize_alphabet(p); - }); - m.method("change_alphabet!", - [](Presentation& p, jlcxx::ArrayRef a) { - libsemigroups::presentation::change_alphabet( - p, word_type(a.begin(), a.end())); - }); - m.method("reverse_rules!", [](Presentation& p) { - libsemigroups::presentation::reverse(p); - }); - m.method("sort_rules!", [](Presentation& p) { - libsemigroups::presentation::sort_rules(p); - }); - m.method("sort_each_rule!", [](Presentation& p) -> bool { - return libsemigroups::presentation::sort_each_rule(p); - }); - - m.method("add_identity_rules!", [](Presentation& p, size_t e) { - libsemigroups::presentation::add_identity_rules(p, e); - }); - m.method("add_zero_rules!", [](Presentation& p, size_t z) { - libsemigroups::presentation::add_zero_rules(p, z); - }); - m.method("remove_duplicate_rules!", [](Presentation& p) { - libsemigroups::presentation::remove_duplicate_rules(p); - }); - m.method("remove_trivial_rules!", [](Presentation& p) { - libsemigroups::presentation::remove_trivial_rules(p); - }); - - m.method("add_rules!", - [](Presentation& p, Presentation const& q) { - libsemigroups::presentation::add_rules(p, q); - }); - - m.method("add_inverse_rules!", - [](Presentation& p, jlcxx::ArrayRef inverses) { - word_type v(inverses.begin(), inverses.end()); - libsemigroups::presentation::add_inverse_rules(p, v); - }); - - m.method("add_inverse_rules_with_identity!", - [](Presentation& p, - jlcxx::ArrayRef inverses, - size_t e) { - word_type v(inverses.begin(), inverses.end()); - libsemigroups::presentation::add_inverse_rules(p, v, e); - }); - - m.method("replace_subword!", - [](Presentation& p, - jlcxx::ArrayRef existing, - jlcxx::ArrayRef replacement) { - word_type e(existing.begin(), existing.end()); - word_type r(replacement.begin(), replacement.end()); - libsemigroups::presentation::replace_subword(p, e, r); - }); - - m.method("replace_word!", - [](Presentation& p, - jlcxx::ArrayRef existing, - jlcxx::ArrayRef replacement) { - word_type e(existing.begin(), existing.end()); - word_type r(replacement.begin(), replacement.end()); - libsemigroups::presentation::replace_word(p, e, r); - }); - - m.method( - "replace_word_with_new_generator!", - [](Presentation& p, jlcxx::ArrayRef w) -> size_t { - word_type v(w.begin(), w.end()); - return libsemigroups::presentation::replace_word_with_new_generator( - p, v); - }); - - m.method("first_unused_letter", - [](Presentation const& p) -> size_t { - return libsemigroups::presentation::first_unused_letter(p); - }); - - m.method("index_rule", - [](Presentation const& p, - jlcxx::ArrayRef lhs, - jlcxx::ArrayRef rhs) -> size_t { - word_type l(lhs.begin(), lhs.end()); - word_type r(rhs.begin(), rhs.end()); - return libsemigroups::presentation::index_rule(p, l, r); - }); - - m.method("is_rule", - [](Presentation const& p, - jlcxx::ArrayRef lhs, - jlcxx::ArrayRef rhs) -> bool { - word_type l(lhs.begin(), lhs.end()); - word_type r(rhs.begin(), rhs.end()); - return libsemigroups::presentation::is_rule(p, l, r); - }); - - m.method("longest_rule_index", - [](Presentation const& p) -> size_t { - auto it = libsemigroups::presentation::longest_rule(p); - return static_cast(std::distance(p.rules.cbegin(), it)); - }); - - m.method("shortest_rule_index", - [](Presentation const& p) -> size_t { - auto it = libsemigroups::presentation::shortest_rule(p); - return static_cast(std::distance(p.rules.cbegin(), it)); - }); - - m.method( - "throw_if_bad_inverses", - [](Presentation const& p, jlcxx::ArrayRef inverses) { - word_type v(inverses.begin(), inverses.end()); - libsemigroups::presentation::throw_if_bad_inverses(p, v); - }); - - m.method("to_gap_string", - [](Presentation const& p, - std::string const& var_name) -> std::string { - return libsemigroups::presentation::to_gap_string(p, var_name); - }); - - m.method("rules_vector", - [](Presentation const& p) -> std::vector { - return std::vector(p.rules.cbegin(), p.rules.cend()); - }); - - type.method( - "is_equal", - [](Presentation const& a, - Presentation const& b) -> bool { return a == b; }); - type.method("to_human_readable_repr", - [](Presentation const& p) -> std::string { - return libsemigroups::to_human_readable_repr(p); - }); + // Bind Presentation and Presentation + bind_presentation(m, "PresentationWord"); + bind_presentation(m, "PresentationString"); // ----------------------------------------------------------------------- // InversePresentation @@ -342,7 +312,7 @@ namespace libsemigroups_julia { jlcxx::julia_base_type>()); itype.constructor const&>(); - itype.constructor const&>(); // copy ctor + itype.constructor const&>(); itype.method( "set_inverses!", From 3ced98c5650f0657630f7c07cad3d4cce7992a6d Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Thu, 15 Oct 2026 21:39:54 +0100 Subject: [PATCH 20/28] added support for presentaiton, refactor of presentaiotn exmaples to explicitly return `Presentation{Vector{Int}}` , added test, created some helpers to stop being used where it should not be right now --- src/cong-common.jl | 11 +- src/kambites.jl | 4 +- src/knuth-bendix.jl | 23 +- src/presentation-examples.jl | 151 +++- src/presentation.jl | 1351 ++++++++++++-------------------- src/to-inverse-presentation.jl | 2 +- src/to-presentation.jl | 15 +- src/todd-coxeter.jl | 11 +- test/test_presentation.jl | 30 +- 9 files changed, 666 insertions(+), 932 deletions(-) diff --git a/src/cong-common.jl b/src/cong-common.jl index 93c13fb..6475b3b 100644 --- a/src/cong-common.jl +++ b/src/cong-common.jl @@ -24,6 +24,15 @@ const CongruenceCommon = LibSemigroups.CongruenceCommon const Congruence = LibSemigroups.CongruenceWord +# The C++ binding takes the congruence kind as an Integer and a raw +# `PresentationWord`; adapt the public Julia types to that. +function Congruence(kind::congruence_kind, p::Presentation) + return @wrap_libsemigroups_call LibSemigroups.CongruenceWord( + _kind_to_cpp(kind), + _cxx_word(p), + ) +end + """ number_of_classes(cong::CongruenceCommon) -> UInt64 @@ -38,7 +47,7 @@ number_of_classes(cong::CongruenceCommon) = Return the presentation associated with `cong`. """ -presentation(cong::CongruenceCommon) = LibSemigroups.presentation(cong) +presentation(cong::CongruenceCommon) = _wrap_presentation(LibSemigroups.presentation(cong)) """ kind(cong::CongruenceCommon) -> congruence_kind diff --git a/src/kambites.jl b/src/kambites.jl index 1d5e794..08c4b4b 100644 --- a/src/kambites.jl +++ b/src/kambites.jl @@ -149,7 +149,7 @@ function init!(k::Kambites) end function init!(k::Kambites, kind::congruence_kind, p::Presentation) - @wrap_libsemigroups_call LibSemigroups.init!(k, kind, p) + @wrap_libsemigroups_call LibSemigroups.init!(k, kind, _cxx_word(p)) return k end @@ -168,7 +168,7 @@ presentation is returned. The Julia binding returns by value (an independent copy) rather than by reference; mutating the returned object does not affect `k`. """ -presentation(k::Kambites) = LibSemigroups.presentation(k) +presentation(k::Kambites) = _wrap_presentation(LibSemigroups.presentation(k)) """ generating_pairs(k::Kambites) -> Vector{Tuple{Vector{Int}, Vector{Int}}} diff --git a/src/knuth-bendix.jl b/src/knuth-bendix.jl index 5f57f2e..15812c4 100644 --- a/src/knuth-bendix.jl +++ b/src/knuth-bendix.jl @@ -75,6 +75,17 @@ number_of_classes(kb) # POSITIVE_INFINITY """ const KnuthBendix = LibSemigroups.KnuthBendixRewriteTrie +# The C++ binding takes the congruence kind as an Integer and a raw +# `PresentationWord`; adapt the public Julia types to that. +_kind_to_cpp(kind) = Int(kind) + +function KnuthBendix(kind::congruence_kind, p::Presentation) + return @wrap_libsemigroups_call LibSemigroups.KnuthBendixRewriteTrie( + _kind_to_cpp(kind), + _cxx_word(p), + ) +end + """ overlap_ABC @@ -141,8 +152,8 @@ function init!(kb::KnuthBendix) return kb end -function init!(kb::KnuthBendix, kind::congruence_kind, p::Presentation) - @wrap_libsemigroups_call LibSemigroups.init!(kb, kind, p) +function init!(kb::KnuthBendix, kind::congruence_kind, p::Presentation{Vector{Int}}) + @wrap_libsemigroups_call LibSemigroups.init!(kb, _kind_to_cpp(kind), p.cxx_obj) return kb end @@ -499,7 +510,7 @@ Return a copy of the presentation used by `kb`. required by the algorithm. This extra letter also appears in the output of [`active_rules`](@ref Semigroups.active_rules). """ -presentation(kb::KnuthBendix) = LibSemigroups.presentation(kb) +presentation(kb::KnuthBendix) = _wrap_presentation(LibSemigroups.presentation(kb)) # ============================================================================ # Rules access @@ -657,8 +668,10 @@ no redundant rule is identified within the timeout. """ function redundant_rule(p::Presentation, timeout::TimePeriod) ns = convert(Nanosecond, timeout) - idx = - @wrap_libsemigroups_call LibSemigroups.kb_redundant_rule(p, Int64(Dates.value(ns))) + idx = @wrap_libsemigroups_call LibSemigroups.kb_redundant_rule( + _cxx_word(p), + Int64(Dates.value(ns)), + ) n_flat = 2 * number_of_rules(p) # If idx == n_flat, no redundant rule was found if idx >= n_flat diff --git a/src/presentation-examples.jl b/src/presentation-examples.jl index 0d22ca9..4190b31 100644 --- a/src/presentation-examples.jl +++ b/src/presentation-examples.jl @@ -40,7 +40,8 @@ symmetric group of degree `n`. """ function symmetric_group(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_symmetric_group(m) + p = @wrap_libsemigroups_call LibSemigroups.example_symmetric_group(m) + Presentation{Vector{Int}}(p) end """ @@ -60,7 +61,9 @@ group of degree `n`. """ function alternating_group(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_alternating_group(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_alternating_group(m) + ) end """ @@ -80,7 +83,7 @@ This function returns a monoid presentation defining the braid group with """ function braid_group(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_braid_group(m) + Presentation{Vector{Int}}(@wrap_libsemigroups_call LibSemigroups.example_braid_group(m)) end """ @@ -101,7 +104,9 @@ the symmetric group of degree `n`, but does not. """ function not_symmetric_group(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_not_symmetric_group(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_not_symmetric_group(m) + ) end """ @@ -121,7 +126,9 @@ transformation monoid of degree `n`. """ function full_transformation_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_full_transformation_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_full_transformation_monoid(m) + ) end """ @@ -141,7 +148,9 @@ partial transformation monoid of degree `n`. """ function partial_transformation_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_partial_transformation_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_partial_transformation_monoid(m) + ) end """ @@ -161,7 +170,9 @@ symmetric inverse monoid of degree `n`. """ function symmetric_inverse_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_symmetric_inverse_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_symmetric_inverse_monoid(m) + ) end """ @@ -181,7 +192,9 @@ cyclic inverse monoid of degree `n`. """ function cyclic_inverse_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_cyclic_inverse_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_cyclic_inverse_monoid(m) + ) end """ @@ -201,7 +214,9 @@ transformations of degree `n`. """ function order_preserving_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_order_preserving_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_order_preserving_monoid(m) + ) end """ @@ -222,7 +237,11 @@ cyclic inverse monoid of degree `n`. """ function order_preserving_cyclic_inverse_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_order_preserving_cyclic_inverse_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_order_preserving_cyclic_inverse_monoid( + m, + ) + ) end """ @@ -243,7 +262,9 @@ transformation monoid of degree `n`. """ function orientation_preserving_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_orientation_preserving_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_orientation_preserving_monoid(m) + ) end """ @@ -264,8 +285,10 @@ orientation-reversing transformation monoid of degree `n`. """ function orientation_preserving_reversing_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_orientation_preserving_reversing_monoid( - m, + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_orientation_preserving_reversing_monoid( + m, + ) ) end @@ -286,7 +309,9 @@ partition monoid of degree `n`. """ function partition_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_partition_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_partition_monoid(m) + ) end """ @@ -306,7 +331,9 @@ partial Brauer monoid of degree `n`. """ function partial_brauer_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_partial_brauer_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_partial_brauer_monoid(m) + ) end """ @@ -326,7 +353,9 @@ Brauer monoid of degree `n`. """ function brauer_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_brauer_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_brauer_monoid(m) + ) end """ @@ -346,7 +375,9 @@ Brauer monoid of degree `n`. """ function singular_brauer_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_singular_brauer_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_singular_brauer_monoid(m) + ) end """ @@ -366,7 +397,9 @@ monoid with `n - 1` generators. """ function temperley_lieb_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_temperley_lieb_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_temperley_lieb_monoid(m) + ) end """ @@ -386,7 +419,9 @@ degree `n`. """ function motzkin_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_motzkin_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_motzkin_monoid(m) + ) end """ @@ -406,7 +441,11 @@ monoid of partial isometries of an `n`-cycle graph. """ function partial_isometries_cycle_graph_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_partial_isometries_cycle_graph_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_partial_isometries_cycle_graph_monoid( + m, + ) + ) end """ @@ -426,7 +465,9 @@ uniform block bijection monoid of degree `n`. """ function uniform_block_bijection_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_uniform_block_bijection_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_uniform_block_bijection_monoid(m) + ) end """ @@ -446,7 +487,9 @@ dual symmetric inverse monoid of degree `n`. """ function dual_symmetric_inverse_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_dual_symmetric_inverse_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_dual_symmetric_inverse_monoid(m) + ) end """ @@ -466,7 +509,9 @@ with `l` generators. """ function stellar_monoid(l::Integer) ll = UInt(l) - @wrap_libsemigroups_call LibSemigroups.example_stellar_monoid(ll) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_stellar_monoid(ll) + ) end """ @@ -486,7 +531,9 @@ This function returns the default libsemigroups presentation of the """ function zero_rook_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_zero_rook_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_zero_rook_monoid(m) + ) end """ @@ -510,7 +557,9 @@ monoid of degree `n`, where each arc carries at most `d - 1` beads. function abacus_jones_monoid(n::Integer, d::Integer) nn = UInt(n) dd = UInt(d) - @wrap_libsemigroups_call LibSemigroups.example_abacus_jones_monoid(nn, dd) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_abacus_jones_monoid(nn, dd) + ) end # ---- batch C: plactic / misc ---- @@ -532,7 +581,9 @@ with `n` generators. """ function plactic_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_plactic_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_plactic_monoid(m) + ) end """ @@ -552,7 +603,9 @@ with `n` generators. """ function chinese_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_chinese_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_chinese_monoid(m) + ) end """ @@ -572,7 +625,9 @@ generators. It is a quotient monoid of the plactic monoid. """ function hypo_plactic_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_hypo_plactic_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_hypo_plactic_monoid(m) + ) end """ @@ -592,7 +647,9 @@ with `n` generators. """ function stylic_monoid(n::Integer) m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_stylic_monoid(m) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_stylic_monoid(m) + ) end """ @@ -613,7 +670,9 @@ presentation to define the claimed group. """ function special_linear_group_2(q::Integer) qq = UInt(q) - @wrap_libsemigroups_call LibSemigroups.example_special_linear_group_2(qq) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_special_linear_group_2(qq) + ) end """ @@ -636,7 +695,9 @@ semigroup `F(r, n)`. function fibonacci_semigroup(r::Integer, n::Integer) rr = UInt(r) nn = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_fibonacci_semigroup(rr, nn) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_fibonacci_semigroup(rr, nn) + ) end """ @@ -659,7 +720,9 @@ semigroup presentation. function monogenic_semigroup(m::Integer, r::Integer) mm = UInt(m) rr = UInt(r) - @wrap_libsemigroups_call LibSemigroups.example_monogenic_semigroup(mm, rr) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_monogenic_semigroup(mm, rr) + ) end """ @@ -682,7 +745,9 @@ rectangular band. function rectangular_band(m::Integer, n::Integer) mm = UInt(m) nn = UInt(n) - @wrap_libsemigroups_call LibSemigroups.example_rectangular_band(mm, nn) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_rectangular_band(mm, nn) + ) end """ @@ -710,7 +775,9 @@ congruence containing the relations ``a^{\\sigma(a)} = a``. """ function sigma_plactic_monoid(sigma::AbstractVector{<:Integer}) s = UInt[UInt(x) for x in sigma] - @wrap_libsemigroups_call LibSemigroups.example_sigma_plactic_monoid(s) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_sigma_plactic_monoid(s) + ) end """ @@ -733,7 +800,9 @@ rank `l` and Iwahori-Hecke deformation `q`. function renner_type_B_monoid(l::Integer, q::Integer) ll = UInt(l) qq = Int32(q) - @wrap_libsemigroups_call LibSemigroups.example_renner_type_B_monoid(ll, qq) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_renner_type_B_monoid(ll, qq) + ) end """ @@ -756,7 +825,9 @@ rank `l` and Iwahori-Hecke deformation `q`. function renner_type_D_monoid(l::Integer, q::Integer) ll = UInt(l) qq = Int32(q) - @wrap_libsemigroups_call LibSemigroups.example_renner_type_D_monoid(ll, qq) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_renner_type_D_monoid(ll, qq) + ) end """ @@ -781,7 +852,9 @@ the Renner monoid of type B with rank `l` and Iwahori-Hecke deformation function not_renner_type_B_monoid(l::Integer, q::Integer) ll = UInt(l) qq = Int32(q) - @wrap_libsemigroups_call LibSemigroups.example_not_renner_type_B_monoid(ll, qq) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_not_renner_type_B_monoid(ll, qq) + ) end """ @@ -806,5 +879,7 @@ the Renner monoid of type D with rank `l` and Iwahori-Hecke deformation function not_renner_type_D_monoid(l::Integer, q::Integer) ll = UInt(l) qq = Int32(q) - @wrap_libsemigroups_call LibSemigroups.example_not_renner_type_D_monoid(ll, qq) + Presentation{Vector{Int}}( + @wrap_libsemigroups_call LibSemigroups.example_not_renner_type_D_monoid(ll, qq) + ) end diff --git a/src/presentation.jl b/src/presentation.jl index 432b817..e25bab3 100644 --- a/src/presentation.jl +++ b/src/presentation.jl @@ -3,12 +3,33 @@ # Distributed under the terms of the GPL license version 3. """ -presentation.jl - Presentation + InversePresentation wrappers +presentation.jl - Presentation{W} + InversePresentation wrappers + +Provides a parametric `Presentation{W}` type wrapping the CxxWrap-bound +C++ `Presentation` template instantiations (`Vector{Int}` or `String`). +Follows the same three-layer pattern as `froidure-pin.jl`: CxxWrap type aliases, +union type, private helpers, mutable struct, constructors, and method wrappers. """ +# ============================================================================ +# CxxWrap type aliases — concrete Presentation instantiations +# ============================================================================ + +const PresentationWord = LibSemigroups.PresentationWord +const PresentationString = LibSemigroups.PresentationString + +# ============================================================================ +# Union type for cxx_obj field +# ============================================================================ + +const _PresentationCxx = Union{PresentationWord,PresentationString} + +# ============================================================================ +# Presentation{W} mutable struct +# ============================================================================ + """ - Presentation() -> Presentation - Presentation(other::Presentation) -> Presentation + Presentation{W} Type for semigroup or monoid presentations. @@ -27,1183 +48,732 @@ live as module-level functions in `Semigroups`. The zero-argument form constructs an empty presentation with no rules and no alphabet; the one-argument form copies `other`. - -!!! warning "v1 limitation" - v1 of Semigroups.jl binds `Presentation` only. Alphabets - and rules are expressed as `Vector{Int}` with 1-based letter indices. -""" -const Presentation = LibSemigroups.Presentation - """ - init!(p::Presentation) -> Presentation +mutable struct Presentation{W} + cxx_obj::_PresentationCxx +end -Remove the alphabet and all rules from `p`. +# ============================================================================ +# Private helpers +# ============================================================================ -This function clears the alphabet and all rules of `p`, putting it back -into the state it would be in if it was newly constructed. """ -init!(p::Presentation) = (LibSemigroups.init!(p); p) + _cxx_presentation_type(::Type{W}) -> Type +Map a high-level Julia word type to its CxxWrap Presentation constructor type. """ - alphabet(p::Presentation) -> Vector{Int} +_cxx_presentation_type(::Type{Vector{Int}}) = PresentationWord +_cxx_presentation_type(::Type{String}) = PresentationString -Return the alphabet of `p`. +_presentation_word_to_cpp(::Type{Vector{Int}}, w::AbstractVector{<:Integer}) = + _word_to_cpp(w) +_presentation_word_to_cpp(::Type{String}, w::AbstractString) = String(w) +_presentation_word_to_cpp(::Type{String}, w::AbstractVector{<:AbstractString}) = join(w) -Returns the alphabet of `p` as a `Vector{Int}` of 1-based letter indices. +_presentation_word_from_cpp(::Type{Vector{Int}}, w) = _word_from_cpp(w) +_presentation_word_from_cpp(::Type{String}, w) = String(w) -# Complexity -Constant. -""" -alphabet(p::Presentation) = _word_from_cpp(LibSemigroups.alphabet(p)) +_presentation_letter_to_cpp(::Type{Vector{Int}}, x::Integer) = _letter_to_cpp(x) +_presentation_letter_to_cpp(::Type{String}, x::Char) = x -# Route `Base.deepcopy` through the C++ copy constructor. Default -# deepcopy_internal for CxxWrap-wrapped types may shallow-copy the handle. -Base.deepcopy_internal(p::Presentation, ::IdDict) = Presentation(p) +_presentation_letter_from_cpp(::Type{Vector{Int}}, x) = _letter_from_cpp(x) +_presentation_letter_from_cpp(::Type{String}, x) = Char(x) -""" - set_alphabet!(p::Presentation, n::Integer) -> Presentation +# ============================================================================ +# Wrap / unwrap helpers +# ============================================================================ -Set the alphabet of `p` by size. - -Sets the alphabet of `p` to be the first `n` positive integers -`[1, 2, ..., n]`. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `n::Integer`: the size of the alphabet. +const _CxxRefs{T} = + Union{T,CxxWrap.CxxWrapCore.ConstCxxRef{<:T},CxxWrap.CxxWrapCore.CxxRef{<:T}} -# Throws -- `LibsemigroupsError`: if `n` is greater than the maximum number of - letters supported. +# Always copy via the C++ copy constructor: presentation(kb) and friends +# return a reference into kb, which must not be aliased by a Julia object. +_wrap_presentation(x::_CxxRefs{PresentationWord}) = + Presentation{Vector{Int}}(PresentationWord(x)) +_wrap_presentation(x::_CxxRefs{PresentationString}) = + Presentation{String}(PresentationString(x)) +_wrap_presentation(x::Presentation) = x -# Warning -This function does not verify that the rules in `p` (if any) consist of -letters belonging to the new alphabet. +_cxx(p::Presentation) = p.cxx_obj -# See also -- [`throw_if_alphabet_has_duplicates`](@ref) -- [`throw_if_bad_rules`](@ref) -- [`throw_if_bad_alphabet_or_rules`](@ref) -""" -function set_alphabet!(p::Presentation, n::Integer) - m = UInt(n) - @wrap_libsemigroups_call LibSemigroups.set_alphabet_size!(p, m) - return p +# Presentations accepted by algorithms that are only bound for word_type. +function _cxx_word(p::Presentation{W}) where {W} + W === Vector{Int} || throw( + ArgumentError( + "this algorithm is only available for Presentation{Vector{Int}}; " * + "got Presentation{$W}. Use `to_presentation_word(p)` first.", + ), + ) + return p.cxx_obj end -""" - set_alphabet!(p::Presentation, a::AbstractVector{<:Integer}) -> Presentation +# ============================================================================ +# Constructors +# ============================================================================ -Set the alphabet of `p` to the letters in `a`. - -Sets the alphabet of `p` to be the (1-based) letters in `a`. +function Presentation{W}() where {W} + T = _cxx_presentation_type(W) + return Presentation{W}(@wrap_libsemigroups_call T()) +end -# Arguments -- `p::Presentation`: the presentation to modify. -- `a::AbstractVector{<:Integer}`: the alphabet. +Presentation() = Presentation{Vector{Int}}() -# Throws -- `LibsemigroupsError`: if `a` contains duplicate letters. +function Presentation(p::Presentation{W}) where {W} + T = _cxx_presentation_type(W) + return Presentation{W}(@wrap_libsemigroups_call T(p.cxx_obj)) +end -# Warning -This function does not verify that the rules in `p` (if any) consist of -letters belonging to the new alphabet. +""" + Presentation(rules::AbstractVector{<:Union{Tuple{W,W}, Pair{W,W}}}) where {W<:Union{Vector{Int}, String}} -# See also -- [`throw_if_bad_rules`](@ref) -- [`throw_if_bad_alphabet_or_rules`](@ref) +Construct a [`Presentation{W}`](@ref Semigroups.Presentation) from a vector +of rules (given as `(lhs, rhs)` tuples or `lhs => rhs` pairs), inferring the alphabet +from the letters present in the rules. """ -function set_alphabet!(p::Presentation, a::AbstractVector{<:Integer}) - w = _word_to_cpp(a) - @wrap_libsemigroups_call LibSemigroups.set_alphabet!(p, w) +function Presentation( + rules::AbstractVector{<:Union{Tuple{W,W}, Pair{W,W}}}, +) where {W<:Union{Vector{Int}, String}} + p = Presentation{W}() + for (lhs, rhs) in rules + add_rule_no_checks!(p, lhs, rhs) + end + alphabet_from_rules!(p) return p end """ - alphabet_from_rules!(p::Presentation) -> Presentation + Presentation(alphabet) -Set the alphabet of `p` to be the letters that appear in its rules. +Construct an empty [`Presentation{W}`](@ref Semigroups.Presentation) with an initial +alphabet defined by `alphabet`. +""" +function Presentation(alphabet::Union{Vector{Int}, String, Vector{String}, Vector{Char}}) + if alphabet isa Vector{String} + p = Presentation{String}() + set_alphabet!(p, join(alphabet)) + return p + end + W = (alphabet isa String || eltype(alphabet) === Char) ? String : Vector{Int} + p = Presentation{W}() + set_alphabet!(p, alphabet) + return p +end -# Complexity -At most ``O(mn)`` where ``m`` is the number of rules and ``n`` is the -length of the longest rule. +# ============================================================================ +# Conversions +# ============================================================================ -# See also -- [`throw_if_bad_rules`](@ref) -- [`throw_if_bad_alphabet_or_rules`](@ref) """ -alphabet_from_rules!(p::Presentation) = (LibSemigroups.alphabet_from_rules!(p); p) + to_presentation_string(p::Presentation{Vector{Int}}) -> Presentation{String} +Convert a word-backed presentation into a string-backed presentation. """ - letter(p::Presentation, i::Integer) -> Int - -Return a letter in the alphabet of `p` by index. - -Returns the letter of the alphabet in position `i` (1-based). +function to_presentation_string(p::Presentation{Vector{Int}}) + return _wrap_presentation( + @wrap_libsemigroups_call LibSemigroups.to_presentation_string(p.cxx_obj) + ) +end -# Arguments -- `p::Presentation`: the presentation. -- `i::Integer`: the 1-based index of the letter to return. +""" + to_presentation_word(p::Presentation{String}) -> Presentation{Vector{Int}} -# Throws -- `LibsemigroupsError`: if `i` is not in the range ``[1, n]``, where - ``n`` is the length of the alphabet of `p`. +Convert a string-backed presentation into a word-backed presentation. """ -function letter(p::Presentation, i::Integer) - idx = UInt(i - 1) - _letter_from_cpp(@wrap_libsemigroups_call LibSemigroups.letter(p, idx)) +function to_presentation_word(p::Presentation{String}) + return _wrap_presentation( + @wrap_libsemigroups_call LibSemigroups.to_presentation_word(p.cxx_obj) + ) end +Base.convert(::Type{Presentation{String}}, p::Presentation{Vector{Int}}) = + to_presentation_string(p) +Base.convert(::Type{Presentation{Vector{Int}}}, p::Presentation{String}) = + to_presentation_word(p) + +# ============================================================================ +# Core presentation methods +# ============================================================================ + """ - index_of(p::Presentation, x::Integer) -> Int + init!(p::Presentation{W}) -> Presentation -Return the index of a letter in the alphabet of `p`. +Remove the alphabet and all rules from `p`. -Returns the 1-based position of the letter `x` in the alphabet of `p`. +This function clears the alphabet and all rules of `p`, putting it back +into the state it would be in if it was newly constructed. +""" +init!(p::Presentation{W}) where {W} = (LibSemigroups.init!(p.cxx_obj); p) -# Arguments -- `p::Presentation`: the presentation. -- `x::Integer`: the letter whose index is sought. +""" + alphabet(p::Presentation{W}) -> Vector{Int} -# Throws -- `LibsemigroupsError`: if `x` does not belong to the alphabet of `p`. +Return the alphabet of `p`. # Complexity Constant. +""" +alphabet(p::Presentation{W}) where {W} = + _presentation_word_from_cpp(W, LibSemigroups.alphabet(p.cxx_obj)) + +Base.deepcopy_internal(p::Presentation{W}, ::IdDict) where {W} = Presentation(p) -# Note -This function mirrors `Presentation::index` in libsemigroups, renamed to -`index_of` to avoid clashing with `Base.index`-style conventions in Julia. """ -function index_of(p::Presentation, x::Integer) - y = _letter_to_cpp(x) - Int(@wrap_libsemigroups_call LibSemigroups.index_of(p, y)) + 1 -end + set_alphabet!(p::Presentation{W}, n::Integer) -> Presentation +Set the alphabet of `p` by size. """ - in_alphabet(p::Presentation, x::Integer) -> Bool +function set_alphabet!(p::Presentation{W}, n::Integer) where {W} + m = UInt(n) + @wrap_libsemigroups_call LibSemigroups.set_alphabet_size!(p.cxx_obj, m) + return p +end -Check if a letter belongs to the alphabet of `p`. +""" + set_alphabet!(p::Presentation{W}, a) -> Presentation -# Arguments -- `p::Presentation`: the presentation. -- `x::Integer`: the letter to check. +Set the alphabet of `p` to the letters in `a`. +""" +function set_alphabet!(p::Presentation{W}, a) where {W} + w = _presentation_word_to_cpp(W, a) + @wrap_libsemigroups_call LibSemigroups.set_alphabet!(p.cxx_obj, w) + return p +end -# Complexity -Constant on average, worst case linear in the size of the alphabet. """ -in_alphabet(p::Presentation, x::Integer) = LibSemigroups.in_alphabet(p, _letter_to_cpp(x)) + alphabet_from_rules!(p::Presentation{W}) -> Presentation +Set the alphabet of `p` to be the letters that appear in its rules. """ - contains_empty_word(p::Presentation) -> Bool +alphabet_from_rules!(p::Presentation{W}) where {W} = + (LibSemigroups.alphabet_from_rules!(p.cxx_obj); p) -Return whether the empty word is a valid relation word in `p`. +""" + letter(p::Presentation{W}, i::Integer) -Returns `true` if the empty word is a valid relation word in `p`, and -`false` otherwise. +Return a letter in the alphabet of `p` by index. +""" +function letter(p::Presentation{W}, i::Integer) where {W} + idx = UInt(i - 1) + _presentation_letter_from_cpp( + W, + @wrap_libsemigroups_call LibSemigroups.letter(p.cxx_obj, idx) + ) +end -If `p` is not allowed to contain the empty word (according to this -function), then `p` may still be isomorphic to a monoid, but is not given -as a quotient of a free monoid. +""" + index_of(p::Presentation{W}, x) -> Int -# Complexity -Constant. +Return the 1-based index of a letter in the alphabet of `p`. """ -contains_empty_word(p::Presentation) = LibSemigroups.contains_empty_word(p) +function index_of(p::Presentation{W}, x) where {W} + y = _presentation_letter_to_cpp(W, x) + Int(@wrap_libsemigroups_call LibSemigroups.index_of(p.cxx_obj, y)) + 1 +end """ - set_contains_empty_word!(p::Presentation, val::Bool) -> Presentation + in_alphabet(p::Presentation{W}, x) -> Bool -Set whether the empty word is a valid relation word in `p`. +Check if a letter belongs to the alphabet of `p`. +""" +in_alphabet(p::Presentation{W}, x) where {W} = + LibSemigroups.in_alphabet(p.cxx_obj, _presentation_letter_to_cpp(W, x)) -Specify whether the empty word should be a valid relation word -(corresponding to `val` being `true`), or not (corresponding to `val` -being `false`). +""" + contains_empty_word(p::Presentation{W}) -> Bool -If `p` is not allowed to contain the empty word (according to the value -specified here), then `p` may still be isomorphic to a monoid, but is not -given as a quotient of a free monoid. +Return whether the empty word is a valid relation word in `p`. +""" +contains_empty_word(p::Presentation{W}) where {W} = + LibSemigroups.contains_empty_word(p.cxx_obj) -# Arguments -- `p::Presentation`: the presentation to modify. -- `val::Bool`: whether `p` can contain the empty word. +""" + set_contains_empty_word!(p::Presentation{W}, val::Bool) -> Presentation -# Complexity -Constant. +Set whether the empty word is a valid relation word in `p`. """ -set_contains_empty_word!(p::Presentation, val::Bool) = - (LibSemigroups.set_contains_empty_word!(p, val); p) +set_contains_empty_word!(p::Presentation{W}, val::Bool) where {W} = + (LibSemigroups.set_contains_empty_word!(p.cxx_obj, val); p) """ - add_generator!(p::Presentation) -> Int - add_generator!(p::Presentation, x::Integer) -> Presentation + add_generator!(p::Presentation{W}) + add_generator!(p::Presentation{W}, x) -> Presentation Add a generator to `p`. - -The zero-argument form adds the first letter not already in the alphabet -of `p` as a generator and returns this letter (1-based). - -The one-argument form adds the letter `x` as a generator of `p`. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `x::Integer`: (one-argument form) the letter to add as a generator. - -# Throws -- `LibsemigroupsError`: (zero-argument form) if the alphabet is already - of the maximum possible size supported by the letter type. -- `LibsemigroupsError`: (one-argument form) if `x` is already in the - alphabet of `p`. """ -function add_generator!(p::Presentation) - x = @wrap_libsemigroups_call LibSemigroups.add_generator_no_arg!(p) - return _letter_from_cpp(x) +function add_generator!(p::Presentation{W}) where {W} + x = @wrap_libsemigroups_call LibSemigroups.add_generator_no_arg!(p.cxx_obj) + return _presentation_letter_from_cpp(W, x) end -function add_generator!(p::Presentation, x::Integer) - y = _letter_to_cpp(x) - @wrap_libsemigroups_call LibSemigroups.add_generator!(p, y) +function add_generator!(p::Presentation{W}, x) where {W} + y = _presentation_letter_to_cpp(W, x) + @wrap_libsemigroups_call LibSemigroups.add_generator!(p.cxx_obj, y) return p end """ - remove_generator!(p::Presentation, x::Integer) -> Presentation + remove_generator!(p::Presentation{W}, x) -> Presentation Remove the letter `x` as a generator of `p`. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `x::Integer`: the letter to remove as a generator. - -# Throws -- `LibsemigroupsError`: if `x` is not in the alphabet of `p`. - -# Complexity -Average case: linear in the length of the alphabet; worst case: quadratic -in the length of the alphabet. """ -function remove_generator!(p::Presentation, x::Integer) - y = _letter_to_cpp(x) - @wrap_libsemigroups_call LibSemigroups.remove_generator!(p, y) +function remove_generator!(p::Presentation{W}, x) where {W} + y = _presentation_letter_to_cpp(W, x) + @wrap_libsemigroups_call LibSemigroups.remove_generator!(p.cxx_obj, y) return p end """ - add_rule!(p::Presentation, lhs::AbstractVector{<:Integer}, rhs::AbstractVector{<:Integer}) -> Presentation - -Add the rule `lhs = rhs` to `p`, after checking that `lhs` and `rhs` -only contain letters in the alphabet of `p`. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `lhs::AbstractVector{<:Integer}`: the left-hand side of the rule. -- `rhs::AbstractVector{<:Integer}`: the right-hand side of the rule. - -# Throws -- `LibsemigroupsError`: if `lhs` or `rhs` contains any letters not - belonging to the alphabet of `p`. -- `LibsemigroupsError`: if [`contains_empty_word`](@ref)`(p)` returns - `false` and either `lhs` or `rhs` is empty. + add_rule!(p::Presentation{W}, lhs, rhs) -> Presentation -# See also -- [`add_rule_no_checks!`](@ref) +Add the rule `lhs = rhs` to `p`, after validation checks. """ -function add_rule!( - p::Presentation, - lhs::AbstractVector{<:Integer}, - rhs::AbstractVector{<:Integer}, -) - l = _word_to_cpp(lhs) - r = _word_to_cpp(rhs) - @wrap_libsemigroups_call LibSemigroups.add_rule!(p, l, r) +function add_rule!(p::Presentation{W}, lhs, rhs) where {W} + l = _presentation_word_to_cpp(W, lhs) + r = _presentation_word_to_cpp(W, rhs) + @wrap_libsemigroups_call LibSemigroups.add_rule!(p.cxx_obj, l, r) return p end """ - add_rule_no_checks!(p::Presentation, lhs::AbstractVector{<:Integer}, rhs::AbstractVector{<:Integer}) -> Presentation + add_rule_no_checks!(p::Presentation{W}, lhs, rhs) -> Presentation Add the rule `lhs = rhs` to `p` without checking the arguments. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `lhs::AbstractVector{<:Integer}`: the left-hand side of the rule. -- `rhs::AbstractVector{<:Integer}`: the right-hand side of the rule. - -# Complexity -Amortized constant. - -# Warning -No checks that the arguments describe words over the alphabet of `p` -are performed. - -# See also -- [`add_rule!`](@ref) """ -function add_rule_no_checks!( - p::Presentation, - lhs::AbstractVector{<:Integer}, - rhs::AbstractVector{<:Integer}, -) - l = _word_to_cpp(lhs) - r = _word_to_cpp(rhs) - LibSemigroups.add_rule_no_checks!(p, l, r) +function add_rule_no_checks!(p::Presentation{W}, lhs, rhs) where {W} + l = _presentation_word_to_cpp(W, lhs) + r = _presentation_word_to_cpp(W, rhs) + LibSemigroups.add_rule_no_checks!(p.cxx_obj, l, r) return p end """ - number_of_rules(p::Presentation) -> Int + number_of_rules(p::Presentation{W}) -> Int Return the number of rules in `p`. - -The rules of a [`Presentation`](@ref Semigroups.Presentation) are stored -internally as a vector of words, with each rule occupying two consecutive -entries (its left-hand and right-hand sides); the number of rules is -therefore half the length of this vector. - -# Complexity -Constant. """ -number_of_rules(p::Presentation) = Int(LibSemigroups.number_of_rules(p)) +number_of_rules(p::Presentation{W}) where {W} = + Int(LibSemigroups.number_of_rules(p.cxx_obj)) """ - rule_lhs(p::Presentation, i::Integer) -> Vector{Int} + rule_lhs(p::Presentation{W}, i::Integer) Return the left-hand side of the `i`-th rule of `p` (1-based rule index). - -# Arguments -- `p::Presentation`: the presentation. -- `i::Integer`: the 1-based index of the rule. - -# Throws -- `LibsemigroupsError`: if `i` is not in the range ``[1, n]``, where - ``n`` is [`number_of_rules`](@ref)`(p)`. - -# See also -- [`rule_rhs`](@ref) -- [`rules`](@ref) """ -function rule_lhs(p::Presentation, i::Integer) +function rule_lhs(p::Presentation{W}, i::Integer) where {W} idx = UInt(i - 1) - _word_from_cpp(@wrap_libsemigroups_call LibSemigroups.rule_lhs(p, idx)) + _presentation_word_from_cpp( + W, + @wrap_libsemigroups_call LibSemigroups.rule_lhs(p.cxx_obj, idx) + ) end """ - rule_rhs(p::Presentation, i::Integer) -> Vector{Int} + rule_rhs(p::Presentation{W}, i::Integer) Return the right-hand side of the `i`-th rule of `p` (1-based rule index). - -# Arguments -- `p::Presentation`: the presentation. -- `i::Integer`: the 1-based index of the rule. - -# Throws -- `LibsemigroupsError`: if `i` is not in the range ``[1, n]``, where - ``n`` is [`number_of_rules`](@ref)`(p)`. - -# See also -- [`rule_lhs`](@ref) -- [`rules`](@ref) """ -function rule_rhs(p::Presentation, i::Integer) +function rule_rhs(p::Presentation{W}, i::Integer) where {W} idx = UInt(i - 1) - _word_from_cpp(@wrap_libsemigroups_call LibSemigroups.rule_rhs(p, idx)) + _presentation_word_from_cpp( + W, + @wrap_libsemigroups_call LibSemigroups.rule_rhs(p.cxx_obj, idx) + ) end """ - rule(p::Presentation, i::Integer) -> Tuple{Vector{Int},Vector{Int}} + rule(p::Presentation{W}, i::Integer) Return the `i`-th rule of `p` as a `(lhs, rhs)` tuple (1-based rule index). - -Thin wrapper combining [`rule_lhs`](@ref) and [`rule_rhs`](@ref). For bulk -access prefer [`rules`](@ref), which makes a single C++ call. - -# Arguments -- `p::Presentation`: the presentation. -- `i::Integer`: the 1-based index of the rule. - -# Throws -- `LibsemigroupsError`: if `i` is not in the range ``[1, n]``, where ``n`` - is [`number_of_rules`](@ref)`(p)`. - -# See also -- [`rule_lhs`](@ref) -- [`rule_rhs`](@ref) -- [`rules`](@ref) """ -rule(p::Presentation, i::Integer) = (rule_lhs(p, i), rule_rhs(p, i)) +rule(p::Presentation{W}, i::Integer) where {W} = (rule_lhs(p, i), rule_rhs(p, i)) """ - rules(p::Presentation) -> Vector{Tuple{Vector{Int},Vector{Int}}} + rules(p::Presentation{W}) Return all rules of `p` as a vector of `(lhs, rhs)` tuples. - -Mirrors `p.rules` in libsemigroups. This is a single C++ call into -`LibSemigroups.rules_vector` followed by a Julia-side pairing, so is -appreciably faster than iterating [`rule_lhs`](@ref) / [`rule_rhs`](@ref) -for large presentations. """ -function rules(p::Presentation) - flat = LibSemigroups.rules_vector(p) +function rules(p::Presentation{W}) where {W} + flat = LibSemigroups.rules_vector(p.cxx_obj) n = length(flat) - return [(_word_from_cpp(flat[i]), _word_from_cpp(flat[i+1])) for i = 1:2:n] + return [ + ( + _presentation_word_from_cpp(W, flat[i]), + _presentation_word_from_cpp(W, flat[i+1]), + ) for i = 1:2:n + ] end """ - clear_rules!(p::Presentation) -> Presentation + clear_rules!(p::Presentation{W}) -> Presentation Remove all rules from `p`. - -The alphabet of `p` is left untouched. """ -clear_rules!(p::Presentation) = (LibSemigroups.clear_rules!(p); p) +clear_rules!(p::Presentation{W}) where {W} = (LibSemigroups.clear_rules!(p.cxx_obj); p) + +# ============================================================================ +# Validation guards +# ============================================================================ """ - throw_if_alphabet_has_duplicates(p::Presentation) + throw_if_alphabet_has_duplicates(p::Presentation{W}) Check if the alphabet of `p` is valid. - -# Throws -- `LibsemigroupsError`: if there are duplicate letters in the alphabet - of `p`. - -# Complexity -Linear in the length of the alphabet. """ -throw_if_alphabet_has_duplicates(p::Presentation) = - (@wrap_libsemigroups_call LibSemigroups.throw_if_alphabet_has_duplicates(p); nothing) +throw_if_alphabet_has_duplicates(p::Presentation{W}) where {W} = ( + @wrap_libsemigroups_call LibSemigroups.throw_if_alphabet_has_duplicates(p.cxx_obj); + nothing +) """ - throw_if_letter_not_in_alphabet(p::Presentation, x::Integer) + throw_if_letter_not_in_alphabet(p::Presentation{W}, x) Check if a letter belongs to the alphabet of `p`. - -# Arguments -- `p::Presentation`: the presentation. -- `x::Integer`: the letter to check. - -# Throws -- `LibsemigroupsError`: if `x` does not belong to the alphabet of `p`. - -# Complexity -Constant on average, worst case linear in the size of the alphabet. """ -function throw_if_letter_not_in_alphabet(p::Presentation, x::Integer) - y = _letter_to_cpp(x) - @wrap_libsemigroups_call LibSemigroups.throw_if_letter_not_in_alphabet(p, y) +function throw_if_letter_not_in_alphabet(p::Presentation{W}, x) where {W} + y = _presentation_letter_to_cpp(W, x) + @wrap_libsemigroups_call LibSemigroups.throw_if_letter_not_in_alphabet(p.cxx_obj, y) return nothing end """ - throw_if_bad_rules(p::Presentation) - -Check if every word in every rule of `p` consists only of letters -belonging to the alphabet. - -Also checks that there are an even number of words in `p`'s rule list -(i.e. that every rule has both a left- and right-hand side). + throw_if_bad_rules(p::Presentation{W}) -# Throws -- `LibsemigroupsError`: if any word contains a letter not in the - alphabet of `p`. -- `LibsemigroupsError`: if the number of words in `p`'s rule list is odd. - -# Complexity -Worst case ``O(mnt)`` where ``m`` is the length of the longest word, -``n`` is the size of the alphabet and ``t`` is the number of rules. +Check if every word in every rule of `p` consists only of letters belonging to the alphabet. """ -throw_if_bad_rules(p::Presentation) = - (@wrap_libsemigroups_call LibSemigroups.throw_if_bad_rules(p); nothing) +throw_if_bad_rules(p::Presentation{W}) where {W} = + (@wrap_libsemigroups_call LibSemigroups.throw_if_bad_rules(p.cxx_obj); nothing) """ - throw_if_bad_alphabet_or_rules(p::Presentation) + throw_if_bad_alphabet_or_rules(p::Presentation{W}) Check if the alphabet and rules of `p` are valid. - -# Throws -- `LibsemigroupsError`: if [`throw_if_alphabet_has_duplicates`](@ref) or - [`throw_if_bad_rules`](@ref) does. - -# Complexity -Worst case ``O(mnp)`` where ``m`` is the length of the longest word, -``n`` is the size of the alphabet, and ``p`` is the number of rules. """ -throw_if_bad_alphabet_or_rules(p::Presentation) = - (@wrap_libsemigroups_call LibSemigroups.throw_if_bad_alphabet_or_rules(p); nothing) +throw_if_bad_alphabet_or_rules(p::Presentation{W}) where {W} = ( + @wrap_libsemigroups_call LibSemigroups.throw_if_bad_alphabet_or_rules(p.cxx_obj); + nothing +) + +# ============================================================================ +# Algorithms & standalone helpers +# ============================================================================ """ - length_of(p::Presentation) -> Int + length_of(p::Presentation{W}) -> Int Return the sum of the lengths of all rule words in `p`. - -That is, ``\\sum (|u| + |v|)`` over all rules ``u = v`` of `p`. - -This function mirrors `presentation::length` in libsemigroups. It is -named `length_of` (rather than extending `Base.length`) to avoid ambiguity -with [`number_of_rules`](@ref), since neither choice is uniformly -natural. """ -length_of(p::Presentation) = Int(LibSemigroups.length_of(p)) +length_of(p::Presentation{W}) where {W} = Int(LibSemigroups.length_of(p.cxx_obj)) """ - longest_rule_length(p::Presentation) -> Int + longest_rule_length(p::Presentation{W}) -> Int Return the maximum length of a rule in `p`. - -The *length* of a rule is defined to be the sum of the lengths of its -left-hand and right-hand sides. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. """ -longest_rule_length(p::Presentation) = Int(LibSemigroups.longest_rule_length(p)) +longest_rule_length(p::Presentation{W}) where {W} = + Int(LibSemigroups.longest_rule_length(p.cxx_obj)) """ - shortest_rule_length(p::Presentation) -> Int + shortest_rule_length(p::Presentation{W}) -> Int Return the minimum length of a rule in `p`. - -The *length* of a rule is defined to be the sum of the lengths of its -left-hand and right-hand sides. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. """ -shortest_rule_length(p::Presentation) = Int(LibSemigroups.shortest_rule_length(p)) +shortest_rule_length(p::Presentation{W}) where {W} = + Int(LibSemigroups.shortest_rule_length(p.cxx_obj)) """ - is_normalized(p::Presentation) -> Bool + is_normalized(p::Presentation{W}) -> Bool Check if the presentation `p` is normalized. - -Returns `true` if the alphabet of `p` is `[1, 2, ..., n]` (where ``n`` is -the size of the alphabet), and `false` otherwise. """ -is_normalized(p::Presentation) = LibSemigroups.is_normalized(p) +is_normalized(p::Presentation{W}) where {W} = LibSemigroups.is_normalized(p.cxx_obj) """ - are_rules_sorted(p::Presentation) -> Bool + are_rules_sorted(p::Presentation{W}) -> Bool Check if the rules of `p` are sorted in short-lex order. - -Returns `true` if the rules ``u_1 = v_1, \\ldots, u_n = v_n`` of `p` -satisfy ``u_1 v_1 < \\cdots < u_n v_n`` where ``<`` is the short-lex -order. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. - -# See also -- [`sort_rules!`](@ref) """ -are_rules_sorted(p::Presentation) = LibSemigroups.are_rules_sorted(p) +are_rules_sorted(p::Presentation{W}) where {W} = LibSemigroups.are_rules_sorted(p.cxx_obj) """ - contains_rule(p::Presentation, lhs::AbstractVector{<:Integer}, rhs::AbstractVector{<:Integer}) -> Bool + contains_rule(p::Presentation{W}, lhs, rhs) -> Bool Check if `p` contains the rule `lhs = rhs`. - -# Arguments -- `p::Presentation`: the presentation. -- `lhs::AbstractVector{<:Integer}`: the left-hand side of the rule. -- `rhs::AbstractVector{<:Integer}`: the right-hand side of the rule. - -# Complexity -Linear in [`number_of_rules`](@ref)`(p)`. """ -function contains_rule( - p::Presentation, - lhs::AbstractVector{<:Integer}, - rhs::AbstractVector{<:Integer}, -) - l = _word_to_cpp(lhs) - r = _word_to_cpp(rhs) - LibSemigroups.contains_rule(p, l, r) +function contains_rule(p::Presentation{W}, lhs, rhs) where {W} + l = _presentation_word_to_cpp(W, lhs) + r = _presentation_word_to_cpp(W, rhs) + LibSemigroups.contains_rule(p.cxx_obj, l, r) end """ - throw_if_odd_number_of_rules(p::Presentation) + throw_if_odd_number_of_rules(p::Presentation{W}) Throw if the number of words in the rule list of `p` is odd. - -# Throws -- `LibsemigroupsError`: if the number of words in the rule list of `p` - is odd (i.e. there is a dangling left-hand side with no matching - right-hand side). """ -throw_if_odd_number_of_rules(p::Presentation) = - (@wrap_libsemigroups_call LibSemigroups.throw_if_odd_number_of_rules(p); nothing) +throw_if_odd_number_of_rules(p::Presentation{W}) where {W} = ( + @wrap_libsemigroups_call LibSemigroups.throw_if_odd_number_of_rules(p.cxx_obj); + nothing +) """ - normalize_alphabet!(p::Presentation) -> Presentation - -Normalize the alphabet of `p` to `[1, ..., n]`. + normalize_alphabet!(p::Presentation{W}) -> Presentation -Modifies `p` in-place so that the alphabet is `[1, ..., n]` (or -equivalent), rewriting the rules to use this alphabet. If the alphabet is -already normalized, no changes are made. - -# Throws -- `LibsemigroupsError`: if [`throw_if_bad_alphabet_or_rules`](@ref) - throws on `p` before modification. +Normalize the alphabet of `p`. """ -normalize_alphabet!(p::Presentation) = - (@wrap_libsemigroups_call LibSemigroups.normalize_alphabet!(p); p) +normalize_alphabet!(p::Presentation{W}) where {W} = + (@wrap_libsemigroups_call LibSemigroups.normalize_alphabet!(p.cxx_obj); p) """ - change_alphabet!(p::Presentation, new_alphabet::AbstractVector{<:Integer}) -> Presentation + change_alphabet!(p::Presentation{W}, new_alphabet) -> Presentation Change or re-order the alphabet of `p`. - -Replaces the alphabet of `p` with `new_alphabet` where possible, and -re-writes the rules of `p` using the new alphabet. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `new_alphabet::AbstractVector{<:Integer}`: the replacement alphabet. - -# Throws -- `LibsemigroupsError`: if the size of the alphabet of `p` does not - equal the length of `new_alphabet`. """ -function change_alphabet!(p::Presentation, new_alphabet::AbstractVector{<:Integer}) - w = _word_to_cpp(new_alphabet) - @wrap_libsemigroups_call LibSemigroups.change_alphabet!(p, w) +function change_alphabet!(p::Presentation{W}, new_alphabet) where {W} + w = _presentation_word_to_cpp(W, new_alphabet) + @wrap_libsemigroups_call LibSemigroups.change_alphabet!(p.cxx_obj, w) return p end -""" - Base.reverse!(p::Presentation) -> Presentation - -Reverse every rule of `p`, in place. +Base.reverse!(p::Presentation{W}) where {W} = (LibSemigroups.reverse_rules!(p.cxx_obj); p) -Extends `Base.reverse!` so that `reverse!(p)` reverses the left- and -right-hand sides of every rule of `p`. Binding-level extensions of -`Base` functions to presentation-like types are documented on the -`Presentation` doc page. """ -Base.reverse!(p::Presentation) = (LibSemigroups.reverse_rules!(p); p) - -""" - sort_rules!(p::Presentation) -> Presentation + sort_rules!(p::Presentation{W}) -> Presentation Sort the rules of `p` in short-lex order. - -Sorts the rules ``u_1 = v_1, \\ldots, u_n = v_n`` so that -``u_1 v_1 < \\cdots < u_n v_n`` where ``<`` is the short-lex order. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. - -# See also -- [`are_rules_sorted`](@ref) """ -sort_rules!(p::Presentation) = (LibSemigroups.sort_rules!(p); p) +sort_rules!(p::Presentation{W}) where {W} = (LibSemigroups.sort_rules!(p.cxx_obj); p) """ - sort_each_rule!(p::Presentation) -> Bool + sort_each_rule!(p::Presentation{W}) -> Bool Sort the two sides of each rule in short-lex order. - -Reorders each rule ``u = v`` of `p` so that the left-hand side is -short-lex greater than or equal to the right-hand side (i.e. the longer -/ lexicographically-larger side is on the left). - -Returns `true` if any rule was reordered, and `false` otherwise. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. - -# Complexity -Linear in the number of rules. """ -sort_each_rule!(p::Presentation) = LibSemigroups.sort_each_rule!(p) +sort_each_rule!(p::Presentation{W}) where {W} = LibSemigroups.sort_each_rule!(p.cxx_obj) """ - add_identity_rules!(p::Presentation, e::Integer) -> Presentation + add_identity_rules!(p::Presentation{W}, e) -> Presentation Add rules for an identity element. - -Adds rules of the form ``ae = ea = a`` for every letter ``a`` in the -alphabet of `p`, where ``e`` is the letter given by the second argument. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `e::Integer`: the identity element. - -# Throws -- `LibsemigroupsError`: if `e` is not a letter in the alphabet of `p`. - -# Complexity -Linear in the number of rules. """ -function add_identity_rules!(p::Presentation, e::Integer) - y = _letter_to_cpp(e) - @wrap_libsemigroups_call LibSemigroups.add_identity_rules!(p, y) +function add_identity_rules!(p::Presentation{W}, e) where {W} + y = _presentation_letter_to_cpp(W, e) + @wrap_libsemigroups_call LibSemigroups.add_identity_rules!(p.cxx_obj, y) return p end """ - add_zero_rules!(p::Presentation, z::Integer) -> Presentation + add_zero_rules!(p::Presentation{W}, z) -> Presentation Add rules for a zero element. - -Adds rules of the form ``az = za = z`` for every letter ``a`` in the -alphabet of `p`, where ``z`` is the letter given by the second argument. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `z::Integer`: the zero element. - -# Throws -- `LibsemigroupsError`: if `z` is not a letter in the alphabet of `p`. - -# Complexity -Linear in the number of rules. """ -function add_zero_rules!(p::Presentation, z::Integer) - y = _letter_to_cpp(z) - @wrap_libsemigroups_call LibSemigroups.add_zero_rules!(p, y) +function add_zero_rules!(p::Presentation{W}, z) where {W} + y = _presentation_letter_to_cpp(W, z) + @wrap_libsemigroups_call LibSemigroups.add_zero_rules!(p.cxx_obj, y) return p end """ - remove_duplicate_rules!(p::Presentation) -> Presentation + remove_duplicate_rules!(p::Presentation{W}) -> Presentation Remove duplicate rules from `p`. - -Removes all but one instance of any duplicate rules (if any). Note that -rules of the form ``u = v`` and ``v = u`` (if any) are considered -duplicates. The rules may be reordered by this function even if there -are no duplicate rules. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. - -# Complexity -Linear in the number of rules. """ -remove_duplicate_rules!(p::Presentation) = (LibSemigroups.remove_duplicate_rules!(p); p) +remove_duplicate_rules!(p::Presentation{W}) where {W} = + (LibSemigroups.remove_duplicate_rules!(p.cxx_obj); p) """ - remove_trivial_rules!(p::Presentation) -> Presentation + remove_trivial_rules!(p::Presentation{W}) -> Presentation Remove rules consisting of identical words. - -Removes all instances of rules (if any) where the left-hand side and -right-hand side are identical (i.e. rules of the form ``u = u``). - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd. - -# Complexity -Linear in the number of rules. """ -remove_trivial_rules!(p::Presentation) = (LibSemigroups.remove_trivial_rules!(p); p) +remove_trivial_rules!(p::Presentation{W}) where {W} = + (LibSemigroups.remove_trivial_rules!(p.cxx_obj); p) """ - add_rules!(p::Presentation, q::Presentation) -> Presentation + add_rules!(p::Presentation{W}, q::Presentation{W}) -> Presentation Add the rules of `q` to `p`. - -Each rule of `q` is checked to contain only letters of `alphabet(p)` before -being added; if the ``n``-th rule would fail this check, the first ``n-1`` -rules are still added to `p`. - -Mirrors `libsemigroups::presentation::add_rules`. - -# Arguments -- `p::Presentation`: the presentation to add rules to. -- `q::Presentation`: the presentation whose rules should be copied into `p`. - -# Throws -- `LibsemigroupsError`: if any rule of `q` contains a letter not in - `alphabet(p)`. """ -function add_rules!(p::Presentation, q::Presentation) - @wrap_libsemigroups_call LibSemigroups.add_rules!(p, q) +function add_rules!(p::Presentation{W}, q::Presentation{W}) where {W} + @wrap_libsemigroups_call LibSemigroups.add_rules!(p.cxx_obj, q.cxx_obj) return p end """ - add_inverse_rules!(p::Presentation, inverses::AbstractVector{<:Integer}) -> Presentation - add_inverse_rules!(p::Presentation, inverses::AbstractVector{<:Integer}, e::Integer) -> Presentation + add_inverse_rules!(p::Presentation{W}, inverses) -> Presentation + add_inverse_rules!(p::Presentation{W}, inverses, e) -> Presentation Add rules for inverses. - -The letter with index `i` in `inverses` is taken to be the inverse of the -letter `alphabet(p)[i]`. The rules added are ``a_i b_i = e`` where -``\\{a_1, \\ldots, a_n\\}`` is `alphabet(p)`, ``\\{b_1, \\ldots, b_n\\}`` -is `inverses`, and `e` is the identity letter. If `e` is omitted, the -identity is taken to be the empty word. - -Mirrors `libsemigroups::presentation::add_inverse_rules`. - -# Arguments -- `p::Presentation`: the presentation to add rules to. -- `inverses::AbstractVector{<:Integer}`: the inverses of the letters in - `alphabet(p)`. -- `e::Integer`: (3-arg form) the identity letter. - -# Throws -- `LibsemigroupsError`: if `length(inverses) != length(alphabet(p))`, if - `inverses` does not contain the same letters as `alphabet(p)`, if - ``(a_i^{-1})^{-1} = a_i`` fails for some `i`, or if - ``e^{-1} = e`` fails. -""" -function add_inverse_rules!(p::Presentation, inverses::AbstractVector{<:Integer}) - v = _word_to_cpp(inverses) - @wrap_libsemigroups_call LibSemigroups.add_inverse_rules!(p, v) +""" +function add_inverse_rules!(p::Presentation{W}, inverses) where {W} + v = _presentation_word_to_cpp(W, inverses) + @wrap_libsemigroups_call LibSemigroups.add_inverse_rules!(p.cxx_obj, v) return p end -function add_inverse_rules!( - p::Presentation, - inverses::AbstractVector{<:Integer}, - e::Integer, -) - v = _word_to_cpp(inverses) - y = _letter_to_cpp(e) - @wrap_libsemigroups_call LibSemigroups.add_inverse_rules_with_identity!(p, v, y) +function add_inverse_rules!(p::Presentation{W}, inverses, e) where {W} + v = _presentation_word_to_cpp(W, inverses) + y = _presentation_letter_to_cpp(W, e) + @wrap_libsemigroups_call LibSemigroups.add_inverse_rules_with_identity!(p.cxx_obj, v, y) return p end """ - replace_subword!(p::Presentation, existing::AbstractVector{<:Integer}, replacement::AbstractVector{<:Integer}) -> Presentation - -Replace every non-overlapping occurrence of the word `existing` in every -rule of `p` with the word `replacement`. `p` is modified in place. - -Mirrors `libsemigroups::presentation::replace_subword`. + replace_subword!(p::Presentation{W}, existing, replacement) -> Presentation -# Arguments -- `p::Presentation`: the presentation to modify. -- `existing::AbstractVector{<:Integer}`: the subword to replace. -- `replacement::AbstractVector{<:Integer}`: the replacement word. - -# Throws -- `LibsemigroupsError`: if `existing` is empty. - -# See also -- [`replace_word!`](@ref) -- [`replace_word_with_new_generator!`](@ref) +Replace every non-overlapping occurrence of `existing` with `replacement`. """ -function replace_subword!( - p::Presentation, - existing::AbstractVector{<:Integer}, - replacement::AbstractVector{<:Integer}, -) - e = _word_to_cpp(existing) - r = _word_to_cpp(replacement) - @wrap_libsemigroups_call LibSemigroups.replace_subword!(p, e, r) +function replace_subword!(p::Presentation{W}, existing, replacement) where {W} + e = _presentation_word_to_cpp(W, existing) + r = _presentation_word_to_cpp(W, replacement) + @wrap_libsemigroups_call LibSemigroups.replace_subword!(p.cxx_obj, e, r) return p end """ - replace_word!(p::Presentation, existing::AbstractVector{<:Integer}, replacement::AbstractVector{<:Integer}) -> Presentation - -Replace every instance of the word `existing` that appears as a full side -of some rule with the word `replacement`. Specifically, every rule of the -form ``existing = w`` or ``w = existing`` has `existing` replaced by -`replacement`. `p` is modified in place. + replace_word!(p::Presentation{W}, existing, replacement) -> Presentation -Differs from [`replace_subword!`](@ref), which replaces any non-overlapping -occurrence of `existing` anywhere inside any rule. - -Mirrors `libsemigroups::presentation::replace_word`. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `existing::AbstractVector{<:Integer}`: the word to replace. -- `replacement::AbstractVector{<:Integer}`: the replacement word. - -# See also -- [`replace_subword!`](@ref) +Replace full-side instances of `existing` with `replacement`. """ -function replace_word!( - p::Presentation, - existing::AbstractVector{<:Integer}, - replacement::AbstractVector{<:Integer}, -) - e = _word_to_cpp(existing) - r = _word_to_cpp(replacement) - @wrap_libsemigroups_call LibSemigroups.replace_word!(p, e, r) +function replace_word!(p::Presentation{W}, existing, replacement) where {W} + e = _presentation_word_to_cpp(W, existing) + r = _presentation_word_to_cpp(W, replacement) + @wrap_libsemigroups_call LibSemigroups.replace_word!(p.cxx_obj, e, r) return p end """ - replace_word_with_new_generator!(p::Presentation, w::AbstractVector{<:Integer}) -> Int + replace_word_with_new_generator!(p::Presentation{W}, w) -Replace every non-overlapping (left-to-right) instance of the word `w` in -every rule of `p` with a new generator `z`, and add the rule ``w = z``. -The new generator and rule are added even if `w` is not a subword of any -rule. Returns the new generator `z` as a 1-based letter index. - -Mirrors `libsemigroups::presentation::replace_word_with_new_generator`. - -# Arguments -- `p::Presentation`: the presentation to modify. -- `w::AbstractVector{<:Integer}`: the word to replace. - -# Throws -- `LibsemigroupsError`: if `w` is empty. +Replace occurrences of `w` with a new generator `z` and add rule `w = z`. """ -function replace_word_with_new_generator!(p::Presentation, w::AbstractVector{<:Integer}) - v = _word_to_cpp(w) - z = @wrap_libsemigroups_call LibSemigroups.replace_word_with_new_generator!(p, v) - return _letter_from_cpp(z) +function replace_word_with_new_generator!(p::Presentation{W}, w) where {W} + v = _presentation_word_to_cpp(W, w) + z = @wrap_libsemigroups_call LibSemigroups.replace_word_with_new_generator!( + p.cxx_obj, + v, + ) + return _presentation_letter_from_cpp(W, z) end """ - first_unused_letter(p::Presentation) -> Int + first_unused_letter(p::Presentation{W}) Return the smallest letter not already in the alphabet of `p`. - -Mirrors `libsemigroups::presentation::first_unused_letter`. - -# Throws -- `LibsemigroupsError`: if the alphabet of `p` is already of the maximum - possible size supported by the underlying letter type. """ -function first_unused_letter(p::Presentation) - return _letter_from_cpp(@wrap_libsemigroups_call LibSemigroups.first_unused_letter(p)) +function first_unused_letter(p::Presentation{W}) where {W} + return _presentation_letter_from_cpp( + W, + @wrap_libsemigroups_call LibSemigroups.first_unused_letter(p.cxx_obj) + ) end """ - index_rule(p::Presentation, lhs::AbstractVector{<:Integer}, rhs::AbstractVector{<:Integer}) -> Union{Int, UndefinedType} - -Return the 1-based index of the first rule of `p` equal to `lhs = rhs`, -or [`UNDEFINED`](@ref Semigroups.UNDEFINED) if no such rule exists. + index_rule(p::Presentation{W}, lhs, rhs) -> Union{Int, UndefinedType} -Mirrors `libsemigroups::presentation::index_rule`. The returned index is -the rule-pair index - the same index accepted by [`rule_lhs`](@ref), -[`rule_rhs`](@ref), and [`rule`](@ref). - -# Arguments -- `p::Presentation`: the presentation. -- `lhs::AbstractVector{<:Integer}`: the left-hand side of the rule. -- `rhs::AbstractVector{<:Integer}`: the right-hand side of the rule. - -# Throws -- `LibsemigroupsError`: if [`throw_if_bad_alphabet_or_rules`](@ref) throws - on `p`. - -# See also -- [`is_rule`](@ref) +Return the 1-based index of the first rule equal to `lhs = rhs`. """ -function index_rule( - p::Presentation, - lhs::AbstractVector{<:Integer}, - rhs::AbstractVector{<:Integer}, -) - l = _word_to_cpp(lhs) - r = _word_to_cpp(rhs) - i = UInt(@wrap_libsemigroups_call LibSemigroups.index_rule(p, l, r)) +function index_rule(p::Presentation{W}, lhs, rhs) where {W} + l = _presentation_word_to_cpp(W, lhs) + r = _presentation_word_to_cpp(W, rhs) + i = UInt(@wrap_libsemigroups_call LibSemigroups.index_rule(p.cxx_obj, l, r)) i == typemax(UInt) && return UNDEFINED return div(Int(i), 2) + 1 end """ - is_rule(p::Presentation, lhs::AbstractVector{<:Integer}, rhs::AbstractVector{<:Integer}) -> Bool + is_rule(p::Presentation{W}, lhs, rhs) -> Bool -Return `true` if `lhs = rhs` is a rule of `p`, and `false` otherwise. - -Mirrors `libsemigroups::presentation::is_rule`. - -# Throws -- `LibsemigroupsError`: if [`throw_if_bad_alphabet_or_rules`](@ref) throws - on `p`. - -# See also -- [`index_rule`](@ref) +Return `true` if `lhs = rhs` is a rule of `p`. """ -function is_rule( - p::Presentation, - lhs::AbstractVector{<:Integer}, - rhs::AbstractVector{<:Integer}, -) - l = _word_to_cpp(lhs) - r = _word_to_cpp(rhs) - return @wrap_libsemigroups_call LibSemigroups.is_rule(p, l, r) +function is_rule(p::Presentation{W}, lhs, rhs) where {W} + l = _presentation_word_to_cpp(W, lhs) + r = _presentation_word_to_cpp(W, rhs) + return @wrap_libsemigroups_call LibSemigroups.is_rule(p.cxx_obj, l, r) end """ - longest_rule_index(p::Presentation) -> Int + longest_rule_index(p::Presentation{W}) -> Int -Return the 1-based index of the first rule of `p` of maximal length. - -The *length* of a rule is the sum of the lengths of its left- and -right-hand sides. Mirrors `libsemigroups::presentation::longest_rule`, -returning a rule-pair index instead of the C++ iterator - so the result is -suitable to pass to [`rule`](@ref), [`rule_lhs`](@ref), or -[`rule_rhs`](@ref). - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd (which - includes the case of no rules). - -# See also -- [`shortest_rule_index`](@ref) -- [`longest_rule_length`](@ref) +Return the 1-based index of the first rule of maximal length. """ -function longest_rule_index(p::Presentation) - flat = UInt(@wrap_libsemigroups_call LibSemigroups.longest_rule_index(p)) +function longest_rule_index(p::Presentation{W}) where {W} + flat = UInt(@wrap_libsemigroups_call LibSemigroups.longest_rule_index(p.cxx_obj)) return div(Int(flat), 2) + 1 end """ - shortest_rule_index(p::Presentation) -> Int - -Return the 1-based index of the first rule of `p` of minimal length. + shortest_rule_index(p::Presentation{W}) -> Int -The *length* of a rule is the sum of the lengths of its left- and -right-hand sides. Mirrors `libsemigroups::presentation::shortest_rule`, -returning a rule-pair index instead of the C++ iterator. - -# Throws -- `LibsemigroupsError`: if the number of rule words in `p` is odd (which - includes the case of no rules). - -# See also -- [`longest_rule_index`](@ref) -- [`shortest_rule_length`](@ref) +Return the 1-based index of the first rule of minimal length. """ -function shortest_rule_index(p::Presentation) - flat = UInt(@wrap_libsemigroups_call LibSemigroups.shortest_rule_index(p)) +function shortest_rule_index(p::Presentation{W}) where {W} + flat = UInt(@wrap_libsemigroups_call LibSemigroups.shortest_rule_index(p.cxx_obj)) return div(Int(flat), 2) + 1 end """ - throw_if_bad_inverses(p::Presentation, inverses::AbstractVector{<:Integer}) - -Throw a `LibsemigroupsError` if `inverses` does not define a valid list of -semigroup inverses for `alphabet(p)`. - -Mirrors `libsemigroups::presentation::throw_if_bad_inverses`. Specifically, -this function checks that `alphabet(p)` and `inverses` contain the same -letters, that `inverses` is duplicate-free, and that if `a_i = b_j` (where -`a` is the alphabet and `b` is `inverses`) then `a_j = b_i` - i.e. taking -an inverse is an involution on the letters. - -# Arguments -- `p::Presentation`: the presentation. -- `inverses::AbstractVector{<:Integer}`: the proposed inverses. + throw_if_bad_inverses(p::Presentation{W}, inverses) -# Throws -- `LibsemigroupsError`: if any of the above conditions does not hold. +Throw if `inverses` does not define a valid list of semigroup inverses. """ -function throw_if_bad_inverses(p::Presentation, inverses::AbstractVector{<:Integer}) - v = _word_to_cpp(inverses) - @wrap_libsemigroups_call LibSemigroups.throw_if_bad_inverses(p, v) +function throw_if_bad_inverses(p::Presentation{W}, inverses) where {W} + v = _presentation_word_to_cpp(W, inverses) + @wrap_libsemigroups_call LibSemigroups.throw_if_bad_inverses(p.cxx_obj, v) return nothing end """ - to_gap_string(p::Presentation, var_name::AbstractString = "p") -> String + to_gap_string(p::Presentation{W}, var_name::AbstractString = "p") -> String -Return the GAP source code that would construct a presentation with the -same alphabet and rules as `p`. Presentations in GAP are created by taking -quotients of free semigroups or monoids. - -Mirrors `libsemigroups::presentation::to_gap_string`. - -# Arguments -- `p::Presentation`: the presentation. -- `var_name::AbstractString`: the name of the GAP variable to assign to - (defaults to `"p"`). - -# Throws -- `LibsemigroupsError`: if `p` has more than 49 generators (the cap on - GAP's default alphabet). +Return GAP source code that constructs `p`. """ -function to_gap_string(p::Presentation, var_name::AbstractString = "p") - s = @wrap_libsemigroups_call LibSemigroups.to_gap_string(p, String(var_name)) +function to_gap_string(p::Presentation{W}, var_name::AbstractString = "p") where {W} + s = @wrap_libsemigroups_call LibSemigroups.to_gap_string(p.cxx_obj, String(var_name)) return String(s) end -Base.:(==)(a::Presentation, b::Presentation) = LibSemigroups.is_equal(a, b) +Base.:(==)(a::Presentation{W}, b::Presentation{W}) where {W} = + LibSemigroups.is_equal(a.cxx_obj, b.cxx_obj) -""" - Base.isempty(p::Presentation) -> Bool +Base.isempty(p::Presentation{W}) where {W} = isempty(alphabet(p)) && number_of_rules(p) == 0 -Return `true` iff `p` has an empty alphabet and no rules - the state it -would be in immediately after [`Presentation`](@ref Semigroups.Presentation)`()` -or [`init!`](@ref). -""" -Base.isempty(p::Presentation) = isempty(alphabet(p)) && number_of_rules(p) == 0 - -""" - Base.hash(p::Presentation, h::UInt) -> UInt - -Stable hash suitable for dictionary keys: presentations equal under -`==` hash to the same value. The hash combines the -alphabet, the flat rules list, and the `contains_empty_word` flag. -""" -function Base.hash(p::Presentation, h::UInt) +function Base.hash(p::Presentation{W}, h::UInt) where {W} return hash( ( alphabet(p), - [_word_from_cpp(w) for w in LibSemigroups.rules_vector(p)], + [ + _presentation_word_from_cpp(W, w) for + w in LibSemigroups.rules_vector(p.cxx_obj) + ], contains_empty_word(p), ), h, ) end -function Base.show(io::IO, p::Presentation) - print(io, LibSemigroups.to_human_readable_repr(p)) +function Base.show(io::IO, p::Presentation{W}) where {W} + print(io, LibSemigroups.to_human_readable_repr(p.cxx_obj)) end -# ---------------------------------------------------------------------------- +# ============================================================================ # InversePresentation -# ---------------------------------------------------------------------------- +# ============================================================================ """ InversePresentation(p::Presentation) -> InversePresentation InversePresentation(ip::InversePresentation) -> InversePresentation Type for inverse-semigroup or inverse-monoid presentations. - -An [`InversePresentation`](@ref Semigroups.InversePresentation) is a -[`Presentation`](@ref Semigroups.Presentation) together with a word -recording a semigroup inverse for each letter of the alphabet. It is a -subtype of [`Presentation`](@ref Semigroups.Presentation) and is intended -to be used as the input to other algorithms in `libsemigroups`. - -Constructed from a [`Presentation`](@ref Semigroups.Presentation) (with -inverses initially empty), or as a copy of another -[`InversePresentation`](@ref Semigroups.InversePresentation). - -!!! warning "v1 limitation" - v1 of Semigroups.jl binds `InversePresentation` only. - Alphabets, rules, and inverses are expressed as `Vector{Int}` with - 1-based letter indices. """ const InversePresentation = LibSemigroups.InversePresentation +InversePresentation(p::Presentation{Vector{Int}}) = + LibSemigroups.InversePresentation(p.cxx_obj) + """ set_inverses!(ip::InversePresentation, w::AbstractVector{<:Integer}) -> InversePresentation Set the inverse of each letter in the alphabet of `ip`. - -The `i`-th entry of `w` is taken to be the inverse of the `i`-th letter -of `alphabet(ip)`. - -# Arguments -- `ip::InversePresentation`: the inverse presentation to modify. -- `w::AbstractVector{<:Integer}`: a word containing the inverses. - -# Throws -- `LibsemigroupsError`: if the alphabet contains duplicate letters, or - if `w` does not define a valid semigroup inverse for the alphabet. - -# Note -Although the alphabet is not an explicit argument to this function, the -alphabet must be checked here since a specification of inverses cannot -make sense if the alphabet contains duplicate letters. - -# See also -- [`throw_if_alphabet_has_duplicates`](@ref) -- [`throw_if_bad_alphabet_rules_or_inverses`](@ref) """ function set_inverses!(ip::InversePresentation, w::AbstractVector{<:Integer}) v = _word_to_cpp(w) @@ -1215,9 +785,6 @@ end inverses(ip::InversePresentation) -> Vector{Int} Return the inverse of each letter in the alphabet of `ip`. - -The `i`-th entry of the returned vector is the inverse of the `i`-th -letter of `alphabet(ip)`. """ inverses(ip::InversePresentation) = _word_from_cpp(LibSemigroups.inverses(ip)) @@ -1225,18 +792,6 @@ inverses(ip::InversePresentation) = _word_from_cpp(LibSemigroups.inverses(ip)) inverse_of(ip::InversePresentation, x::Integer) -> Int Return the inverse of the letter `x` in `ip`. - -# Arguments -- `ip::InversePresentation`: the inverse presentation. -- `x::Integer`: the letter whose inverse is sought. - -# Throws -- `LibsemigroupsError`: if no inverses have been set, or if `x` is not - in the alphabet of `ip`. - -# Note -This function mirrors `InversePresentation::inverse` in libsemigroups, -renamed to `inverse_of` to avoid shadowing Julia's `Base.inv`. """ function inverse_of(ip::InversePresentation, x::Integer) y = _letter_to_cpp(x) @@ -1247,27 +802,12 @@ end throw_if_bad_alphabet_rules_or_inverses(ip::InversePresentation) Check if `ip` is a valid inverse presentation. - -Specifically, checks that the alphabet of `ip` does not contain duplicate -letters, that all rules only contain letters defined in the alphabet, and -that the inverses act as semigroup inverses. - -# Throws -- `LibsemigroupsError`: if the alphabet contains duplicate letters. -- `LibsemigroupsError`: if the rules contain letters not defined in the - alphabet. -- `LibsemigroupsError`: if the inverses do not act as semigroup - inverses. - -# See also -- [`throw_if_bad_alphabet_or_rules`](@ref) """ throw_if_bad_alphabet_rules_or_inverses(ip::InversePresentation) = ( @wrap_libsemigroups_call LibSemigroups.throw_if_bad_alphabet_rules_or_inverses(ip); nothing ) -# Equality that accounts for inverses (not the base `Presentation ==`). Base.:(==)(a::InversePresentation, b::InversePresentation) = LibSemigroups.is_equal_inv(a, b) @@ -1275,5 +815,74 @@ function Base.show(io::IO, ip::InversePresentation) print(io, LibSemigroups.to_human_readable_repr(ip)) end -# Extend deepcopy via the copy ctor. Base.deepcopy_internal(ip::InversePresentation, ::IdDict) = InversePresentation(ip) + +# ============================================================================ +# InversePresentation Delegation +# ============================================================================ + +_presentation_view(ip::InversePresentation) = Presentation{Vector{Int}}(ip) + +function _forward_to_presentation(f, ip::InversePresentation, args...) + v = _presentation_view(ip) + r = f(v, args...) + return r === v ? ip : r +end + +for _f in ( + :add_generator!, + :add_identity_rules!, + :add_inverse_rules!, + :add_rule!, + :add_rule_no_checks!, + :add_rules!, + :add_zero_rules!, + :alphabet, + :alphabet_from_rules!, + :are_rules_sorted, + :change_alphabet!, + :clear_rules!, + :contains_empty_word, + :contains_rule, + :first_unused_letter, + :in_alphabet, + :index_of, + :index_rule, + :init!, + :is_normalized, + :is_rule, + :length_of, + :letter, + :longest_rule_index, + :longest_rule_length, + :normalize_alphabet!, + :number_of_rules, + :remove_duplicate_rules!, + :remove_generator!, + :remove_trivial_rules!, + :replace_subword!, + :replace_word!, + :replace_word_with_new_generator!, + :rule, + :rule_lhs, + :rule_rhs, + :rules, + :set_alphabet!, + :set_contains_empty_word!, + :shortest_rule_index, + :shortest_rule_length, + :sort_each_rule!, + :sort_rules!, + :throw_if_alphabet_has_duplicates, + :throw_if_bad_alphabet_or_rules, + :throw_if_bad_inverses, + :throw_if_bad_rules, + :throw_if_letter_not_in_alphabet, + :throw_if_odd_number_of_rules, + :to_gap_string, +) + @eval $_f(ip::InversePresentation, args...) = _forward_to_presentation($_f, ip, args...) +end + +Base.isempty(ip::InversePresentation) = isempty(_presentation_view(ip)) +Base.reverse!(ip::InversePresentation) = (reverse!(_presentation_view(ip)); ip) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl index a4726b1..5a08bea 100644 --- a/src/to-inverse-presentation.jl +++ b/src/to-inverse-presentation.jl @@ -13,7 +13,7 @@ those of `p`, over a normalised alphabet with a corresponding inverse letter for each letter of `p`. """ to(::Type{InversePresentation}, p::Presentation) = - @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(p) + @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(_cxx_word(p)) """ to(::Type{InversePresentation}, ip::InversePresentation) -> InversePresentation diff --git a/src/to-presentation.jl b/src/to-presentation.jl index 2c9d9d3..68f27e5 100644 --- a/src/to-presentation.jl +++ b/src/to-presentation.jl @@ -20,7 +20,8 @@ This conversion returns the presentation used to construct or initialise `c`. Congruence-generating pairs are not added to the returned presentation by running `c`. """ -to(::Type{Presentation}, c::Congruence) = LibSemigroups.to_presentation_word(c) +to(::Type{Presentation}, c::Congruence) = + _wrap_presentation(LibSemigroups.to_presentation_word(c)) """ to(::Type{Presentation}, k::Kambites) -> Presentation @@ -31,7 +32,8 @@ initialise `k`. This conversion does not enumerate `k`. """ -to(::Type{Presentation}, k::Kambites) = LibSemigroups.to_presentation_word(k) +to(::Type{Presentation}, k::Kambites) = + _wrap_presentation(LibSemigroups.to_presentation_word(k)) """ to(::Type{Presentation}, tc::ToddCoxeter) -> Presentation @@ -41,7 +43,8 @@ Return the presentation used to construct or initialise `tc`. This conversion does not enumerate `tc`. """ -to(::Type{Presentation}, tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) +to(::Type{Presentation}, tc::ToddCoxeter) = + _wrap_presentation(LibSemigroups.to_presentation_word(tc)) """ to(::Type{Presentation}, kb::KnuthBendix) -> Presentation @@ -53,7 +56,8 @@ This conversion does not enumerate `kb`, so the returned presentation may not yet describe the completed semigroup or monoid. Run `kb` first if a fully processed presentation is required. """ -to(::Type{Presentation}, kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) +to(::Type{Presentation}, kb::KnuthBendix) = + _wrap_presentation(LibSemigroups.to_presentation_word(kb)) """ to(::Type{Presentation}, fp::FroidurePin) -> Presentation @@ -63,4 +67,5 @@ Return a presentation using the currently known rules of `fp`. This conversion enumerates `fp` before obtaining its presentation. """ -to(::Type{Presentation}, fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) +to(::Type{Presentation}, fp::FroidurePin) = + _wrap_presentation(LibSemigroups.to_presentation_word(fp.cxx_obj)) diff --git a/src/todd-coxeter.jl b/src/todd-coxeter.jl index c0fc9c7..e2d9e8b 100644 --- a/src/todd-coxeter.jl +++ b/src/todd-coxeter.jl @@ -51,6 +51,9 @@ objects. """ const ToddCoxeter = LibSemigroups.ToddCoxeterWord +ToddCoxeter(kind::congruence_kind, p::Presentation) = + LibSemigroups.ToddCoxeterWord(kind, _cxx_word(p)) + # --- strategy --------------------------------------------------------------- """ @@ -273,11 +276,13 @@ function init!(tc::ToddCoxeter) return tc end + function init!(tc::ToddCoxeter, kind::congruence_kind, p::Presentation) - @wrap_libsemigroups_call LibSemigroups.init!(tc, kind, p) + @wrap_libsemigroups_call LibSemigroups.init!(tc, kind, _cxx_word(p)) return tc end + function init!(tc::ToddCoxeter, kind::congruence_kind, other::ToddCoxeter) @wrap_libsemigroups_call LibSemigroups.init!(tc, kind, other) return tc @@ -887,7 +892,7 @@ If `tc` was constructed or initialised from a [`Presentation`](@ref Semigroups.Presentation), the returned presentation is empty. """ -presentation(tc::ToddCoxeter) = LibSemigroups.presentation(tc) +presentation(tc::ToddCoxeter) = _wrap_presentation(LibSemigroups.presentation(tc)) # ============================================================================ # Base.* overloads @@ -1002,7 +1007,7 @@ Knuth-Bendix-based helper. function tc_redundant_rule(p::Presentation, timeout::TimePeriod) # See note on is_non_trivial: keep coercions out of the macro arglist. ns = Int64(Dates.value(convert(Nanosecond, timeout))) - idx = @wrap_libsemigroups_call LibSemigroups.tc_redundant_rule(p, ns) + idx = @wrap_libsemigroups_call LibSemigroups.tc_redundant_rule(_cxx_word(p), ns) n_flat = 2 * number_of_rules(p) if idx >= n_flat return nothing diff --git a/test/test_presentation.jl b/test/test_presentation.jl index fc3a929..6e4431a 100644 --- a/test/test_presentation.jl +++ b/test/test_presentation.jl @@ -7,6 +7,24 @@ using Semigroups @test hasmethod(Presentation, Tuple{}) end + @testset "parametric types and string support" begin + # Default constructor resolves to Vector{Int} + p_default = Presentation() + @test p_default isa Presentation{Vector{Int}} + + p_vec = Presentation([([1, 2], [2, 1])]) + + p_str = Presentation([("a", "b")]) + # String constructor inference + p_str_inferred = Presentation(["a", "b"]) + @test p_str_inferred isa Presentation{String} + + if isdefined(Semigroups, :_cxx_word) + @test_throws ArgumentError Semigroups._cxx_word(p_str) + @test Semigroups._cxx_word(p_default) === p_default.cxx_obj + end + end + @testset "constructors + init! + deepcopy + alphabet getter" begin p = Presentation() @test p isa Presentation @@ -247,7 +265,7 @@ using Semigroups set_alphabet!(p, [1, 2]) ip = InversePresentation(p) @test ip isa InversePresentation - @test ip isa Presentation # CxxWrap inheritance chain + @test ip isa Semigroups.LibSemigroups.PresentationWord # CxxWrap inheritance chain @test alphabet(ip) == [1, 2] # Copy ctor @@ -475,7 +493,7 @@ using Semigroups @test rules(p) == [([1, 1], [1]), ([2, 2], [2])] # Binding-surface: rules_vector is a callable C++ method on Presentation. - flat = Semigroups.LibSemigroups.rules_vector(p) + flat = Semigroups.LibSemigroups.rules_vector(p.cxx_obj) @test length(flat) == 2 * number_of_rules(p) end @@ -519,10 +537,10 @@ using Semigroups @testset "binding surface" begin LS = Semigroups.LibSemigroups # Scalar / reference signatures - hasmethod with concrete types works. - @test hasmethod(LS.first_unused_letter, Tuple{Presentation}) - @test hasmethod(LS.longest_rule_index, Tuple{Presentation}) - @test hasmethod(LS.shortest_rule_index, Tuple{Presentation}) - @test hasmethod(LS.rules_vector, Tuple{Presentation}) + @test hasmethod(LS.first_unused_letter, Tuple{LS.PresentationWord}) + @test hasmethod(LS.longest_rule_index, Tuple{LS.PresentationWord}) + @test hasmethod(LS.shortest_rule_index, Tuple{LS.PresentationWord}) + @test hasmethod(LS.rules_vector, Tuple{LS.PresentationWord}) # Vector-input methods: CxxWrap's ArrayRef maps to a concrete # Julia method signature that is tricky to spell as a `Tuple{...}`, so From 2fede1e12021958fe94a1ac3f0aae198b56a23b4 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Thu, 15 Oct 2026 22:06:24 +0100 Subject: [PATCH 21/28] to.jl consolidation --- .vscode/settings.json | 3 + OR | 36 ++++++ src/to-cong.jl | 40 ------- src/to-froidure-pin.jl | 24 ---- src/to-inverse-presentation.jl | 25 ---- src/to-knuth-bendix.jl | 27 ----- src/to-presentation.jl | 71 ----------- src/to-todd-coxeter.jl | 42 ------- src/to.jl | 17 ++- test/test_to_congruence.jl | 51 -------- test/test_to_froidure_pin.jl | 24 ---- test/test_to_inverse_presentation.jl | 40 ------- test/test_to_knuth_bendix.jl | 35 ------ test/test_to_presentation.jl | 168 --------------------------- test/test_to_todd_coxeter.jl | 46 -------- 15 files changed, 50 insertions(+), 599 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 OR delete mode 100644 src/to-cong.jl delete mode 100644 src/to-froidure-pin.jl delete mode 100644 src/to-inverse-presentation.jl delete mode 100644 src/to-knuth-bendix.jl delete mode 100644 src/to-presentation.jl delete mode 100644 src/to-todd-coxeter.jl delete mode 100644 test/test_to_congruence.jl delete mode 100644 test/test_to_froidure_pin.jl delete mode 100644 test/test_to_inverse_presentation.jl delete mode 100644 test/test_to_knuth_bendix.jl delete mode 100644 test/test_to_presentation.jl delete mode 100644 test/test_to_todd_coxeter.jl diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2b031df --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.sourceDirectory": "/home/joem/VIP/Semigroups.jl/deps/src" +} diff --git a/OR b/OR new file mode 100644 index 0000000..e7853ce --- /dev/null +++ b/OR @@ -0,0 +1,36 @@ +### Summary + +**Add parametric `Presentation{W}` support for `Vector{Int}` and `String` representations.** + +* **Parametric Wrapper**: Updated `Presentation` to a parametric `mutable struct Presentation{W}` to support both integer (`Vector{Int}`) and character (`String`) representations. +* **Unified Constructors**: Simplified outer constructors to two main methods that pick `W` automatically based on your rules or alphabet inputs. +* **API Forwarding**: Added a short loop to forward `InversePresentation` methods through a `Presentation{Vector{Int}}` view, avoiding duplicated code. +* **Examples Refactor**: Updated all built-in example functions in `presentation-examples.jl` to explicitly return `Presentation{Vector{Int}}` so they match the new parametric type structure. +* **Legacy Safety**: Added `_cxx_word` to stop `Presentation{String}` from entering single-type C++ routines, giving a clear error instead. +* **C++ Conversion & Memory Helpers**: + * `_cxx_word`: Safely extracts the inner C++ object, blocking string presentations from reaching unsupported algorithms. + * `_wrap_presentation`: Wraps C++ outputs back into Julia `Presentation{W}` structs while making a safe memory copy. + +--- + +### Key Changes + +1. **`src/presentation.jl`**: + * Changed `Presentation` from a fixed CxxWrap alias to `mutable struct Presentation{W}`. + * Created outer constructors that infer `W` from inputs like `[("a", "b")]` or `["a", "b"]`. + * Replaced manual `InversePresentation` methods with an `@eval` delegation loop. + +2. **`src/presentation-examples.jl`**: + * Updated example functions (like `symmetric_group` and `full_transformation_monoid`) to construct `Presentation{Vector{Int}}(p)`. + +3. **`test/test_presentation.jl`**: + * Added tests covering default construction, rule type inference (`Vector{Int}` vs `String`), alphabet initialization, and the `_cxx_word` safety check. + +--- + +### Testing + +Ran the full test suite via `Pkg.test()`: +* Verified default `Presentation()` creates `Presentation{Vector{Int}}`. +* Verified string presentations create `Presentation{String}`. +* Verified `_cxx_word` throws an `ArgumentError` when given a string presentation. diff --git a/src/to-cong.jl b/src/to-cong.jl deleted file mode 100644 index 985ce65..0000000 --- a/src/to-cong.jl +++ /dev/null @@ -1,40 +0,0 @@ -""" -to-cong.jl - conversions to Congruence -""" - -""" - to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, - wg) -> Congruence - -Convert a Froidure-Pin graph to a Congruence. - -Julia implementation of libsemigroups' `to(kind, fp, wg)` -conversion. `wg` must be either the left or right Cayley graph of `fp`. -The result represents the trivial congruence over the semigroup defined by -`fp`. - -`kind` selects whether the resulting congruence is one-sided or two-sided. - -# Throws - -- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if `wg` is not - the left or right Cayley graph of `fp`. -""" -to(::Type{Congruence}, kind::congruence_kind, fp::FroidurePin, wg) = - @wrap_libsemigroups_call LibSemigroups.to_congruence_from_fpb(kind, fp.cxx_obj, wg) - -""" - to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) -> Congruence - -Convert a WordGraph to a Congruence. - -Julia implementation of libsemigroups' `to(kind, wg)` -conversion. The result represents the trivial congruence over `wg`. - -`kind` selects whether the resulting congruence is one-sided or two-sided. - -The graph is added to the constructed congruence as-is; no checks are made -that the resulting Todd-Coxeter or Congruence object is valid. -""" -@cxxdereference to(::Type{Congruence}, kind::congruence_kind, wg::WordGraph) = - @wrap_libsemigroups_call LibSemigroups.to_congruence_from_wg(kind, wg) diff --git a/src/to-froidure-pin.jl b/src/to-froidure-pin.jl deleted file mode 100644 index 5dfa941..0000000 --- a/src/to-froidure-pin.jl +++ /dev/null @@ -1,24 +0,0 @@ -""" -to-froidure-pin.jl - conversions to FroidurePin -""" - -""" - to(::Type{FroidurePin}, wg::WordGraph) -> FroidurePin{Transf{UInt32}} - -Convert a complete `WordGraph` to a Froidure-Pin whose elements are -`Transf{UInt32}`. This is the Julia implementation of -libsemigroups' `to(wg)` conversion. Each graph label becomes a -transformation generator, with the transformation image determined by the -edge target for each node. - -The graph must be complete, and every target must be a node of the graph. - -# Throws - -- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if the graph is - incomplete or contains an out-of-range target. -""" -@cxxdereference to(::Type{FroidurePin}, wg::WordGraph) = - FroidurePin{Transf{UInt32}}(@wrap_libsemigroups_call begin - LibSemigroups.to_froidure_pin_from_wg(wg) - end) diff --git a/src/to-inverse-presentation.jl b/src/to-inverse-presentation.jl deleted file mode 100644 index 5a08bea..0000000 --- a/src/to-inverse-presentation.jl +++ /dev/null @@ -1,25 +0,0 @@ -""" -to-inverse-presentation.jl - conversions to InversePresentation -""" - -""" - to(::Type{InversePresentation}, p::Presentation) -> InversePresentation - -Convert a Presentation to an InversePresentation. - -Julia implementation of libsemigroups' `to(p)` -conversion. The resulting inverse presentation has rules equivalent to -those of `p`, over a normalised alphabet with a corresponding inverse -letter for each letter of `p`. -""" -to(::Type{InversePresentation}, p::Presentation) = - @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(_cxx_word(p)) - -""" - to(::Type{InversePresentation}, ip::InversePresentation) -> InversePresentation - -Convert an InversePresentation to an InversePresentation. - -Return a copy of `ip`. -""" -to(::Type{InversePresentation}, ip::InversePresentation) = InversePresentation(ip) diff --git a/src/to-knuth-bendix.jl b/src/to-knuth-bendix.jl deleted file mode 100644 index afccb67..0000000 --- a/src/to-knuth-bendix.jl +++ /dev/null @@ -1,27 +0,0 @@ -""" -to-knuth-bendix.jl - conversions to KnuthBendix -""" - -""" - to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) -> KnuthBendix - -Julia implementation of libsemigroups' `to(kind, fp)` -conversion. It constructs a Knuth-Bendix instance from the presentation -obtained from `fp` and represents the trivial congruence over that semigroup. -`kind` selects whether the resulting congruence is one-sided or two-sided. - -This conversion enumerates `fp` before obtaining its presentation. -""" -to(::Type{KnuthBendix}, kind::congruence_kind, fp::FroidurePin) = - LibSemigroups.to_knuth_bendix_from_fpb(kind, fp.cxx_obj) - -""" - to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) -> KnuthBendix - -Julia implementation of libsemigroups' `to(kind, tc)` -conversion. It constructs a Knuth-Bendix instance from `tc.presentation()` -and represents the trivial congruence over the semigroup defined by `tc`. -`kind` selects whether the resulting congruence is one-sided or two-sided. -""" -to(::Type{KnuthBendix}, kind::congruence_kind, tc::ToddCoxeter) = - LibSemigroups.to_knuth_bendix_from_tc(kind, tc) diff --git a/src/to-presentation.jl b/src/to-presentation.jl deleted file mode 100644 index 68f27e5..0000000 --- a/src/to-presentation.jl +++ /dev/null @@ -1,71 +0,0 @@ -""" -to-presentation.jl - to Presentation conversion -""" - -""" - to(::Type{Presentation}, p::Presentation) -> Presentation - -Julia implementation of the identity conversion for `Presentation`. The -returned presentation is a copy of `p`. -""" -to(::Type{Presentation}, p::Presentation) = Presentation(p) - -""" - to(::Type{Presentation}, c::Congruence) -> Presentation - -Julia implementation of libsemigroups' `to(c)` conversion. -Return the presentation used to construct or initialise `c`. - -This conversion returns the presentation used to construct or initialise `c`. -Congruence-generating pairs are not added to the returned presentation by -running `c`. -""" -to(::Type{Presentation}, c::Congruence) = - _wrap_presentation(LibSemigroups.to_presentation_word(c)) - -""" - to(::Type{Presentation}, k::Kambites) -> Presentation - -Julia implementation of libsemigroups' `to(k)` conversion. -Return a presentation equivalent to the presentation used to construct or -initialise `k`. - -This conversion does not enumerate `k`. -""" -to(::Type{Presentation}, k::Kambites) = - _wrap_presentation(LibSemigroups.to_presentation_word(k)) - -""" - to(::Type{Presentation}, tc::ToddCoxeter) -> Presentation - -Julia implementation of libsemigroups' `to(tc)` conversion. -Return the presentation used to construct or initialise `tc`. - -This conversion does not enumerate `tc`. -""" -to(::Type{Presentation}, tc::ToddCoxeter) = - _wrap_presentation(LibSemigroups.to_presentation_word(tc)) - -""" - to(::Type{Presentation}, kb::KnuthBendix) -> Presentation - -Julia implementation of libsemigroups' `to(kb)` conversion. -Return a presentation using the currently active rules of `kb`. - -This conversion does not enumerate `kb`, so the returned presentation may not -yet describe the completed semigroup or monoid. Run `kb` first if a fully -processed presentation is required. -""" -to(::Type{Presentation}, kb::KnuthBendix) = - _wrap_presentation(LibSemigroups.to_presentation_word(kb)) - -""" - to(::Type{Presentation}, fp::FroidurePin) -> Presentation - -Julia implementation of libsemigroups' `to(fp)` conversion. -Return a presentation using the currently known rules of `fp`. - -This conversion enumerates `fp` before obtaining its presentation. -""" -to(::Type{Presentation}, fp::FroidurePin) = - _wrap_presentation(LibSemigroups.to_presentation_word(fp.cxx_obj)) diff --git a/src/to-todd-coxeter.jl b/src/to-todd-coxeter.jl deleted file mode 100644 index 4dfc4b2..0000000 --- a/src/to-todd-coxeter.jl +++ /dev/null @@ -1,42 +0,0 @@ -""" -to-todd-coxeter.jl - conversions to ToddCoxeter -""" - -""" - to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, - wg::WordGraph) -> ToddCoxeter - -Convert a Froidure-Pin graph to a Todd-Coxeter. - -Julia implementation of libsemigroups' `to(kind, fp, wg)` -conversion. `wg` must be either the left or right Cayley graph of `fp`. -The result represents the trivial congruence over the semigroup defined by -`fp`. -`kind` selects whether the resulting congruence is one-sided or two-sided. - -# Throws - -- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if `wg` is not - the left or right Cayley graph of `fp`. -""" -to(::Type{ToddCoxeter}, kind::congruence_kind, fp::FroidurePin, wg) = - @wrap_libsemigroups_call LibSemigroups.to_todd_coxeter_from_fpb(kind, fp.cxx_obj, wg) - -""" - to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) -> ToddCoxeter - -Convert a Knuth-Bendix to a Todd-Coxeter. - -Julia implementation of libsemigroups' `to(kind, kb)` -conversion. It uses the right Cayley graph of the semigroup represented by -`kb` and returns the corresponding trivial congruence. -`kind` selects whether the resulting congruence is one-sided or two-sided. - -# Throws - -- [`LibsemigroupsError`](@ref Semigroups.LibsemigroupsError) if `kb` does not - represent a two-sided congruence or has infinitely many classes. In the - infinite case, use `ToddCoxeter(kind, presentation(kb))` instead. -""" -to(::Type{ToddCoxeter}, kind::congruence_kind, kb::KnuthBendix) = - @wrap_libsemigroups_call LibSemigroups.to_todd_coxeter_from_kb(kind, kb) diff --git a/src/to.jl b/src/to.jl index ca2c0ea..0a0a82a 100644 --- a/src/to.jl +++ b/src/to.jl @@ -104,7 +104,8 @@ This conversion returns the presentation used to construct or initialise `c`. Congruence-generating pairs are not added to the returned presentation by running `c`. """ -to(::Type{Presentation}, c::Congruence) = LibSemigroups.to_presentation_word(c) +to(::Type{Presentation}, c::Congruence) = + _wrap_presentation(LibSemigroups.to_presentation_word(c)) """ to(::Type{Presentation}, k::Kambites) -> Presentation @@ -115,7 +116,8 @@ initialise `k`. This conversion does not enumerate `k`. """ -to(::Type{Presentation}, k::Kambites) = LibSemigroups.to_presentation_word(k) +to(::Type{Presentation}, k::Kambites) = + _wrap_presentation(LibSemigroups.to_presentation_word(k)) """ to(::Type{Presentation}, tc::ToddCoxeter) -> Presentation @@ -125,7 +127,8 @@ Return the presentation used to construct or initialise `tc`. This conversion does not enumerate `tc`. """ -to(::Type{Presentation}, tc::ToddCoxeter) = LibSemigroups.to_presentation_word(tc) +to(::Type{Presentation}, tc::ToddCoxeter) = + _wrap_presentation(LibSemigroups.to_presentation_word(tc)) """ to(::Type{Presentation}, kb::KnuthBendix) -> Presentation @@ -137,7 +140,8 @@ This conversion does not enumerate `kb`, so the returned presentation may not yet describe the completed semigroup or monoid. Run `kb` first if a fully processed presentation is required. """ -to(::Type{Presentation}, kb::KnuthBendix) = LibSemigroups.to_presentation_word(kb) +to(::Type{Presentation}, kb::KnuthBendix) = + _wrap_presentation(LibSemigroups.to_presentation_word(kb)) """ to(::Type{Presentation}, fp::FroidurePin) -> Presentation @@ -147,7 +151,8 @@ Return a presentation using the currently known rules of `fp`. This conversion enumerates `fp` before obtaining its presentation. """ -to(::Type{Presentation}, fp::FroidurePin) = LibSemigroups.to_presentation_word(fp.cxx_obj) +to(::Type{Presentation}, fp::FroidurePin) = + _wrap_presentation(LibSemigroups.to_presentation_word(fp.cxx_obj)) # ============================================================================ # Conversions to KnuthBendix @@ -217,7 +222,7 @@ those of `p`, over a normalised alphabet with a corresponding inverse letter for each letter of `p`. """ to(::Type{InversePresentation}, p::Presentation) = - @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(p) + @wrap_libsemigroups_call LibSemigroups.to_inverse_presentation_word(_cxx_word(p)) """ to(::Type{InversePresentation}, ip::InversePresentation) -> InversePresentation diff --git a/test/test_to_congruence.jl b/test/test_to_congruence.jl deleted file mode 100644 index 807532d..0000000 --- a/test/test_to_congruence.jl +++ /dev/null @@ -1,51 +0,0 @@ -using Test -using Semigroups - -@testset verbose = true "to Congruence" begin - @testset "scaffolding" begin - # Check both public conversion signatures. - @test hasmethod( - Semigroups.to, - Tuple{Type{Congruence},congruence_kind,FroidurePin,Any}, - ) - @test hasmethod(Semigroups.to, Tuple{Type{Congruence},congruence_kind,WordGraph}) - end - - @testset "from FroidurePin" begin - # Convert the right Cayley graph of a completed Froidure-Pin. - fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - run!(fp) - c = Semigroups.to(Congruence, twosided, fp, right_cayley_graph(fp)) - - @test c isa Congruence - @test c isa CongruenceCommon - @test number_of_classes(c) == 6 - @test kind(c) == twosided - @test alphabet(presentation(c)) == [1, 2] - end - - @testset "from WordGraph" begin - # Convert a graph directly without a Froidure-Pin wrapper. - wg = WordGraph(2, 1) - target!(wg, 1, 1, 2) - target!(wg, 2, 1, 2) - - c = Semigroups.to(Congruence, twosided, wg) - - @test c isa Congruence - @test c isa CongruenceCommon - @test number_of_classes(c) == 1 - @test kind(c) == twosided - @test alphabet(presentation(c)) == [1] - end - - @testset "errors are translated" begin - fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - @test_throws LibsemigroupsError Semigroups.to( - Congruence, - twosided, - fp, - WordGraph(2, 1), - ) - end -end diff --git a/test/test_to_froidure_pin.jl b/test/test_to_froidure_pin.jl deleted file mode 100644 index 6458ffb..0000000 --- a/test/test_to_froidure_pin.jl +++ /dev/null @@ -1,24 +0,0 @@ -using Test -using Semigroups - -@testset verbose = true "to FroidurePin" begin - @testset "scaffolding" begin - @test hasmethod(Semigroups.to, Tuple{Type{FroidurePin},WordGraph}) - end - - @testset "from WordGraph" begin - wg = WordGraph(2, 1) - target!(wg, 1, 1, 2) - target!(wg, 2, 1, 2) - - fp = Semigroups.to(FroidurePin, wg) - - @test fp isa FroidurePin{Transf{UInt32}} - @test number_of_generators(fp) == 1 - @test degree(fp) == 2 - end - - @testset "errors are translated" begin - @test_throws LibsemigroupsError Semigroups.to(FroidurePin, WordGraph(2, 1)) - end -end diff --git a/test/test_to_inverse_presentation.jl b/test/test_to_inverse_presentation.jl deleted file mode 100644 index eb65ce2..0000000 --- a/test/test_to_inverse_presentation.jl +++ /dev/null @@ -1,40 +0,0 @@ -using Test -using Semigroups - -@testset verbose = true "to InversePresentation" begin - @testset "scaffolding" begin - # Check conversion from Presentation and copying an inverse presentation. - @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},Presentation}) - @test hasmethod(Semigroups.to, Tuple{Type{InversePresentation},InversePresentation}) - end - - @testset "from Presentation" begin - # Conversion adds inverse generators and preserves the original rules. - p = Presentation() - set_alphabet!(p, 2) - add_rule_no_checks!(p, [1, 2], [2, 1]) - - ip = Semigroups.to(InversePresentation, p) - - @test ip isa InversePresentation - @test alphabet(ip) == [1, 2, 3, 4] - @test rules(ip) == rules(p) - @test inverses(ip) == [3, 4, 1, 2] - throw_if_bad_alphabet_rules_or_inverses(ip) - end - - @testset "copy" begin - # Copy conversion must produce an equal but distinct wrapper. - p = Presentation() - set_alphabet!(p, 1) - ip = InversePresentation(p) - set_inverses!(ip, [1]) - - copy = Semigroups.to(InversePresentation, ip) - - @test copy isa InversePresentation - @test copy !== ip - @test copy == ip - @test inverses(copy) == [1] - end -end diff --git a/test/test_to_knuth_bendix.jl b/test/test_to_knuth_bendix.jl deleted file mode 100644 index b47beff..0000000 --- a/test/test_to_knuth_bendix.jl +++ /dev/null @@ -1,35 +0,0 @@ -using Test -using Semigroups - -@testset verbose = true "to KnuthBendix" begin - @testset "scaffolding" begin - # Check conversions from Froidure-Pin and Todd-Coxeter. - @test hasmethod(Semigroups.to, Tuple{Type{KnuthBendix},congruence_kind,FroidurePin}) - @test hasmethod(Semigroups.to, Tuple{Type{KnuthBendix},congruence_kind,ToddCoxeter}) - end - - @testset "from FroidurePin" begin - # Conversion from a completed Froidure-Pin creates active rules. - fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - run!(fp) - kb = Semigroups.to(KnuthBendix, twosided, fp) - - @test kb isa KnuthBendix - @test alphabet(presentation(kb)) == [1, 2] - @test number_of_rules(presentation(kb)) > 0 - throw_if_bad_alphabet_or_rules(presentation(kb)) - end - - @testset "from ToddCoxeter" begin - # Conversion preserves the Todd-Coxeter presentation. - p = Presentation() - set_alphabet!(p, 1) - add_rule_no_checks!(p, [1, 1], [1]) - tc = ToddCoxeter(twosided, p) - - kb = Semigroups.to(KnuthBendix, twosided, tc) - - @test kb isa KnuthBendix - @test presentation(kb) == presentation(tc) - end -end diff --git a/test/test_to_presentation.jl b/test/test_to_presentation.jl deleted file mode 100644 index 9701413..0000000 --- a/test/test_to_presentation.jl +++ /dev/null @@ -1,168 +0,0 @@ -using Test -using Semigroups - -@testset verbose = true "to Presentation" begin - @testset "scaffolding" begin - # Check every public conversion to Presentation. - @test isdefined(Semigroups, :to) - @test hasmethod(Semigroups.to, Tuple{Type{Presentation},Presentation}) - @test hasmethod(Semigroups.to, Tuple{Type{Presentation},Congruence}) - @test hasmethod(Semigroups.to, Tuple{Type{Presentation},Kambites}) - @test hasmethod(Semigroups.to, Tuple{Type{Presentation},ToddCoxeter}) - @test hasmethod(Semigroups.to, Tuple{Type{Presentation},KnuthBendix}) - @test hasmethod(Semigroups.to, Tuple{Type{Presentation},FroidurePin}) - end - - @testset "Presentation conversion" begin - # Copying preserves presentation data without aliasing the wrapper. - p = Presentation() - set_alphabet!(p, 2) - add_rule_no_checks!(p, [1, 1], [2]) - set_contains_empty_word!(p, true) - - q = Semigroups.to(Presentation, p) - - @test q isa Presentation - @test q == p - @test q !== p - @test alphabet(q) == alphabet(p) - @test rules(q) == rules(p) - @test contains_empty_word(q) == contains_empty_word(p) - end - - @testset "Congruence conversion" begin - # A congruence converts back to its defining presentation. - p = Presentation() - set_alphabet!(p, 2) - add_rule_no_checks!(p, [1, 1], [2]) - c = Congruence(twosided, p) - - q = Semigroups.to(Presentation, c) - - @test q isa Presentation - @test q == p - end - - @testset "conversion with no rules" begin - # Conversion also preserves an alphabet when there are no rules. - p = Presentation() - set_alphabet!(p, 3) - k = Kambites(twosided, p) - - q = Semigroups.to(Presentation, k) - - @test q isa Presentation - @test q == presentation(k) - @test alphabet(q) == alphabet(p) - @test rules(q) == rules(p) - end - - @testset "conversion of alphabet + rules" begin - # Conversion preserves both alphabet and rules. - p = Presentation() - set_alphabet!(p, 3) - add_rule_no_checks!(p, [1, 2, 1], [2, 3]) - add_rule_no_checks!(p, [3, 3], [1]) - k = Kambites(twosided, p) - - q = Semigroups.to(Presentation, k) - - @test q == presentation(k) - @test q == p - @test alphabet(q) == alphabet(p) - @test rules(q) == rules(p) - end - - @testset "contains empty word" begin - # The empty-word flag is retained by conversion. - p = Presentation() - set_alphabet!(p, 1) - set_contains_empty_word!(p, true) - k = Kambites(twosided, p) - - q = Semigroups.to(Presentation, k) - - @test q == presentation(k) - @test contains_empty_word(q) - end - - @testset "matches Kambites presentation" begin - # The converted presentation matches Kambites exactly. - p = Presentation() - set_alphabet!(p, 2) - add_rule_no_checks!(p, [1, 1], [2]) - k = Kambites(twosided, p) - - q = Semigroups.to(Presentation, k) - - @test q == presentation(k) - @test alphabet(q) == alphabet(presentation(k)) - @test rules(q) == rules(presentation(k)) - end - - @testset "ToddCoxeter conversion" begin - # Conversion preserves the Todd-Coxeter presentation data. - p = Presentation() - set_alphabet!(p, 2) - add_rule_no_checks!(p, [1, 1], [2]) - tc = ToddCoxeter(twosided, p) - - q = Semigroups.to(Presentation, tc) - - @test q isa Presentation - @test q == presentation(tc) - @test alphabet(q) == alphabet(p) - @test rules(q) == rules(p) - end - - @testset "ToddCoxeter conversion contains empty word" begin - # Check the empty-word flag for Todd-Coxeter conversion. - p = Presentation() - set_alphabet!(p, 1) - set_contains_empty_word!(p, true) - tc = ToddCoxeter(twosided, p) - - @test contains_empty_word(Semigroups.to(Presentation, tc)) - end - - @testset "KnuthBendix conversion" begin - # Conversion exposes the active Knuth-Bendix rules. - p = Presentation() - set_alphabet!(p, 2) - add_rule_no_checks!(p, [1, 1], [1]) - kb = KnuthBendix(twosided, p) - run!(kb) - - q = Semigroups.to(Presentation, kb) - - @test q isa Presentation - @test alphabet(q) == alphabet(presentation(kb)) - @test rules(q) == active_rules(kb) - @test number_of_rules(q) == length(active_rules(kb)) - end - - @testset "KnuthBendix conversion contains empty word" begin - # Check the empty-word flag for Knuth-Bendix conversion. - p = Presentation() - set_alphabet!(p, 1) - set_contains_empty_word!(p, true) - kb = KnuthBendix(twosided, p) - - @test contains_empty_word(Semigroups.to(Presentation, kb)) - end - - @testset "FroidurePin conversion" begin - # Conversion produces a valid presentation from Froidure-Pin. - fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - - @test !finished(fp) - - q = Semigroups.to(Presentation, fp) - - @test finished(fp) - @test q isa Presentation - @test alphabet(q) == [1, 2] - @test number_of_rules(q) > 0 - throw_if_bad_alphabet_or_rules(q) - end -end diff --git a/test/test_to_todd_coxeter.jl b/test/test_to_todd_coxeter.jl deleted file mode 100644 index 5e2976a..0000000 --- a/test/test_to_todd_coxeter.jl +++ /dev/null @@ -1,46 +0,0 @@ -using Test -using Semigroups - -@testset verbose = true "to ToddCoxeter" begin - @testset "scaffolding" begin - # Check conversions from Froidure-Pin and Knuth-Bendix. - @test hasmethod( - Semigroups.to, - Tuple{Type{ToddCoxeter},congruence_kind,FroidurePin,Any}, - ) - @test hasmethod(Semigroups.to, Tuple{Type{ToddCoxeter},congruence_kind,KnuthBendix}) - end - - @testset "from FroidurePin" begin - # Convert a completed Froidure-Pin graph. - fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - tc = Semigroups.to(ToddCoxeter, twosided, fp, right_cayley_graph(fp)) - - @test tc isa ToddCoxeter - @test alphabet(presentation(tc)) == [1, 2] - end - - @testset "errors are translated" begin - fp = FroidurePin(Transf([2, 1, 3]), Transf([2, 3, 1])) - @test_throws LibsemigroupsError Semigroups.to( - ToddCoxeter, - twosided, - fp, - WordGraph(2, 1), - ) - end - - @testset "from KnuthBendix" begin - # Convert a Knuth-Bendix instance built from a presentation. - p = Presentation() - set_alphabet!(p, 1) - add_rule_no_checks!(p, [1, 1], [1]) - kb = KnuthBendix(twosided, p) - - tc = Semigroups.to(ToddCoxeter, twosided, kb) - - @test tc isa ToddCoxeter - @test alphabet(presentation(tc)) == [1] - @test number_of_classes(tc) == 1 - end -end From 6ececd695d2ad72fbc29e0c34e4d5b218811ad36 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Thu, 15 Oct 2026 22:55:49 +0100 Subject: [PATCH 22/28] Format --- src/presentation.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/presentation.jl b/src/presentation.jl index e25bab3..c841dbe 100644 --- a/src/presentation.jl +++ b/src/presentation.jl @@ -131,8 +131,8 @@ of rules (given as `(lhs, rhs)` tuples or `lhs => rhs` pairs), inferring the alp from the letters present in the rules. """ function Presentation( - rules::AbstractVector{<:Union{Tuple{W,W}, Pair{W,W}}}, -) where {W<:Union{Vector{Int}, String}} + rules::AbstractVector{<:Union{Tuple{W,W},Pair{W,W}}}, +) where {W<:Union{Vector{Int},String}} p = Presentation{W}() for (lhs, rhs) in rules add_rule_no_checks!(p, lhs, rhs) @@ -147,7 +147,7 @@ end Construct an empty [`Presentation{W}`](@ref Semigroups.Presentation) with an initial alphabet defined by `alphabet`. """ -function Presentation(alphabet::Union{Vector{Int}, String, Vector{String}, Vector{Char}}) +function Presentation(alphabet::Union{Vector{Int},String,Vector{String},Vector{Char}}) if alphabet isa Vector{String} p = Presentation{String}() set_alphabet!(p, join(alphabet)) From 364fd1c306d3eae3981fc20615c6c53f03a8e433 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Fri, 16 Oct 2026 01:33:56 +0100 Subject: [PATCH 23/28] opefully added a test to get full coverage --- test/test_todd_coxeter.jl | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/test_todd_coxeter.jl b/test/test_todd_coxeter.jl index 77f7107..880d8d8 100644 --- a/test/test_todd_coxeter.jl +++ b/test/test_todd_coxeter.jl @@ -239,6 +239,35 @@ end @test def_policy(tc) == def_policy_purge_all end +@testset "TC init! overloads coverage" begin + p1 = Presentation() + set_alphabet!(p1, 2) + add_rule_no_checks!(p1, _tc_word(0, 0, 0), _tc_word(0)) + add_rule_no_checks!(p1, _tc_word(0), _tc_word(1, 1)) + + p2 = Presentation() + set_alphabet!(p2, 1) + add_rule_no_checks!(p2, _tc_word(0, 0), _tc_word(0)) + + tc = ToddCoxeter(twosided, p1) + run!(tc) + @test number_of_classes(tc) == 5 + + # Re-initialize with presentation + init!(tc, twosided, p2) + run!(tc) + @test number_of_classes(tc) == 1 + + # Re-initialize with another ToddCoxeter + tc_other = ToddCoxeter(twosided, p1) + init!(tc, onesided, tc_other) + @test tc isa ToddCoxeter + + # Re-initialize with WordGraph + wg = WordGraph(1, 2) + init!(tc, twosided, wg) + @test tc isa ToddCoxeter +end @testset "TC - current_word_graph after run!" begin # After run!, current_word_graph may include inactive allocation slots, # so it has at least number_of_classes(tc) + 1 nodes (the +1 accounts for From a6d3451d4ceb39ca66ae62b91ed36cc636153ae6 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Fri, 16 Oct 2026 01:43:48 +0100 Subject: [PATCH 24/28] removed mistake --- src/presentation.jl | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/presentation.jl b/src/presentation.jl index c841dbe..7be469f 100644 --- a/src/presentation.jl +++ b/src/presentation.jl @@ -69,6 +69,7 @@ _presentation_word_to_cpp(::Type{Vector{Int}}, w::AbstractVector{<:Integer}) = _word_to_cpp(w) _presentation_word_to_cpp(::Type{String}, w::AbstractString) = String(w) _presentation_word_to_cpp(::Type{String}, w::AbstractVector{<:AbstractString}) = join(w) +_presentation_word_to_cpp(::Type{String}, w::AbstractVector{Char}) = String(w) _presentation_word_from_cpp(::Type{Vector{Int}}, w) = _word_from_cpp(w) _presentation_word_from_cpp(::Type{String}, w) = String(w) @@ -101,7 +102,7 @@ function _cxx_word(p::Presentation{W}) where {W} W === Vector{Int} || throw( ArgumentError( "this algorithm is only available for Presentation{Vector{Int}}; " * - "got Presentation{$W}. Use `to_presentation_word(p)` first.", + "got Presentation{$W}.", ), ) return p.cxx_obj @@ -159,37 +160,6 @@ function Presentation(alphabet::Union{Vector{Int},String,Vector{String},Vector{C return p end -# ============================================================================ -# Conversions -# ============================================================================ - -""" - to_presentation_string(p::Presentation{Vector{Int}}) -> Presentation{String} - -Convert a word-backed presentation into a string-backed presentation. -""" -function to_presentation_string(p::Presentation{Vector{Int}}) - return _wrap_presentation( - @wrap_libsemigroups_call LibSemigroups.to_presentation_string(p.cxx_obj) - ) -end - -""" - to_presentation_word(p::Presentation{String}) -> Presentation{Vector{Int}} - -Convert a string-backed presentation into a word-backed presentation. -""" -function to_presentation_word(p::Presentation{String}) - return _wrap_presentation( - @wrap_libsemigroups_call LibSemigroups.to_presentation_word(p.cxx_obj) - ) -end - -Base.convert(::Type{Presentation{String}}, p::Presentation{Vector{Int}}) = - to_presentation_string(p) -Base.convert(::Type{Presentation{Vector{Int}}}, p::Presentation{String}) = - to_presentation_word(p) - # ============================================================================ # Core presentation methods # ============================================================================ From 574c2af7e2da0db216ef9c4f2b94d5585fa0c172 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Fri, 16 Oct 2026 02:01:55 +0100 Subject: [PATCH 25/28] Attmeoting fix --- src/presentation.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/presentation.jl b/src/presentation.jl index 7be469f..79a020e 100644 --- a/src/presentation.jl +++ b/src/presentation.jl @@ -89,10 +89,8 @@ const _CxxRefs{T} = # Always copy via the C++ copy constructor: presentation(kb) and friends # return a reference into kb, which must not be aliased by a Julia object. -_wrap_presentation(x::_CxxRefs{PresentationWord}) = - Presentation{Vector{Int}}(PresentationWord(x)) -_wrap_presentation(x::_CxxRefs{PresentationString}) = - Presentation{String}(PresentationString(x)) +_wrap_presentation(x::PresentationWord) = Presentation{Vector{Int}}(x) +_wrap_presentation(x::PresentationString) = Presentation{String}(x) _wrap_presentation(x::Presentation) = x _cxx(p::Presentation) = p.cxx_obj From 059fb65fd41702793bd0ffdbd07e9dafb87f694c Mon Sep 17 00:00:00 2001 From: Joseph Macgowan Date: Fri, 16 Oct 2026 02:31:48 +0100 Subject: [PATCH 26/28] Aiming for 100% test coverage & praying --- deps/src/presentation.cpp | 6 +++--- src/presentation.jl | 4 ++-- test/test_presentation.jl | 45 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/deps/src/presentation.cpp b/deps/src/presentation.cpp index da18607..fe01c35 100644 --- a/deps/src/presentation.cpp +++ b/deps/src/presentation.cpp @@ -103,7 +103,7 @@ namespace libsemigroups_julia { type.method("init!", [](P& self) { self.init(); }); type.method("alphabet", - [](P const& self) -> Word const& { return self.alphabet(); }); + [](P const& self) -> Word { return self.alphabet(); }); type.method("set_alphabet_size!", [](P& self, size_t n) { self.alphabet(n); }); type.method("alphabet_from_rules!", @@ -133,10 +133,10 @@ namespace libsemigroups_julia { type.method("number_of_rules", [](P const& self) -> size_t { return self.rules.size() / 2; }); - type.method("rule_lhs", [](P const& self, size_t i) -> Word const& { + type.method("rule_lhs", [](P const& self, size_t i) -> Word { return self.rules.at(2 * i); }); - type.method("rule_rhs", [](P const& self, size_t i) -> Word const& { + type.method("rule_rhs", [](P const& self, size_t i) -> Word { return self.rules.at(2 * i + 1); }); type.method("clear_rules!", [](P& self) { self.rules.clear(); }); diff --git a/src/presentation.jl b/src/presentation.jl index 79a020e..fbb87ea 100644 --- a/src/presentation.jl +++ b/src/presentation.jl @@ -75,8 +75,8 @@ _presentation_word_from_cpp(::Type{Vector{Int}}, w) = _word_from_cpp(w) _presentation_word_from_cpp(::Type{String}, w) = String(w) _presentation_letter_to_cpp(::Type{Vector{Int}}, x::Integer) = _letter_to_cpp(x) -_presentation_letter_to_cpp(::Type{String}, x::Char) = x - +# In src/presentation.jl: +_presentation_letter_to_cpp(::Type{String}, x::Char) = UInt(x) _presentation_letter_from_cpp(::Type{Vector{Int}}, x) = _letter_from_cpp(x) _presentation_letter_from_cpp(::Type{String}, x) = Char(x) diff --git a/test/test_presentation.jl b/test/test_presentation.jl index 6e4431a..02deb59 100644 --- a/test/test_presentation.jl +++ b/test/test_presentation.jl @@ -40,6 +40,51 @@ using Semigroups @test r !== p # distinct wrapper end +@testset "Alphabet Invariants & Custom Alphabets" begin + p_char = Presentation(['a', 'b', 'c']) + @test p_char isa Presentation{String} + @test alphabet(p_char) == "abc" + @test letter(p_char, 1) == 'a' + @test index_of(p_char, 'b') == 2 + @test in_alphabet(p_char, 'c') + @test !in_alphabet(p_char, 'z') + + p_dup = Presentation() + @test_throws LibsemigroupsError set_alphabet!(p_dup, [1, 1, 2]) +end + @testset "Rule Searching and Index Invariants" begin + p = Presentation() + set_alphabet!(p, [1, 2]) + add_rule!(p, [1, 2], [2, 1]) + + # Verified present rule + @test is_rule(p, [1, 2], [2, 1]) + @test index_rule(p, [1, 2], [2, 1]) == 1 + + # Verified absent rule returns UNDEFINED sentinel + @test !is_rule(p, [1], [2]) + @test index_rule(p, [1], [2]) == Semigroups.UNDEFINED + @test !contains_rule(p, [1], [2]) + end + +@testset "Rule Validation Guards" begin + p = Presentation() + set_alphabet!(p, [1, 2]) + + @test_throws LibsemigroupsError throw_if_letter_not_in_alphabet(p, 3) + + add_rule_no_checks!(p, [1, 3], [2]) + @test_throws LibsemigroupsError throw_if_bad_rules(p) + @test_throws LibsemigroupsError throw_if_bad_alphabet_or_rules(p) +end + +@testset "Algorithm Type Guard Constraints" begin + p_str = Presentation{String}() + set_alphabet!(p_str, "ab") + + # Assert type guard raises ArgumentError explaining type incompatibility + @test_throws ArgumentError Semigroups._cxx_word(p_str) +end @testset "alphabet setters" begin p = Presentation() set_alphabet!(p, 3) From 9bc208f018c0cdf2054be769f065b6eb8edee13a Mon Sep 17 00:00:00 2001 From: Joseph Macgowan <117601359+jmacgowan@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:45:47 +0100 Subject: [PATCH 27/28] remove changes file --- OR | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 OR diff --git a/OR b/OR deleted file mode 100644 index e7853ce..0000000 --- a/OR +++ /dev/null @@ -1,36 +0,0 @@ -### Summary - -**Add parametric `Presentation{W}` support for `Vector{Int}` and `String` representations.** - -* **Parametric Wrapper**: Updated `Presentation` to a parametric `mutable struct Presentation{W}` to support both integer (`Vector{Int}`) and character (`String`) representations. -* **Unified Constructors**: Simplified outer constructors to two main methods that pick `W` automatically based on your rules or alphabet inputs. -* **API Forwarding**: Added a short loop to forward `InversePresentation` methods through a `Presentation{Vector{Int}}` view, avoiding duplicated code. -* **Examples Refactor**: Updated all built-in example functions in `presentation-examples.jl` to explicitly return `Presentation{Vector{Int}}` so they match the new parametric type structure. -* **Legacy Safety**: Added `_cxx_word` to stop `Presentation{String}` from entering single-type C++ routines, giving a clear error instead. -* **C++ Conversion & Memory Helpers**: - * `_cxx_word`: Safely extracts the inner C++ object, blocking string presentations from reaching unsupported algorithms. - * `_wrap_presentation`: Wraps C++ outputs back into Julia `Presentation{W}` structs while making a safe memory copy. - ---- - -### Key Changes - -1. **`src/presentation.jl`**: - * Changed `Presentation` from a fixed CxxWrap alias to `mutable struct Presentation{W}`. - * Created outer constructors that infer `W` from inputs like `[("a", "b")]` or `["a", "b"]`. - * Replaced manual `InversePresentation` methods with an `@eval` delegation loop. - -2. **`src/presentation-examples.jl`**: - * Updated example functions (like `symmetric_group` and `full_transformation_monoid`) to construct `Presentation{Vector{Int}}(p)`. - -3. **`test/test_presentation.jl`**: - * Added tests covering default construction, rule type inference (`Vector{Int}` vs `String`), alphabet initialization, and the `_cxx_word` safety check. - ---- - -### Testing - -Ran the full test suite via `Pkg.test()`: -* Verified default `Presentation()` creates `Presentation{Vector{Int}}`. -* Verified string presentations create `Presentation{String}`. -* Verified `_cxx_word` throws an `ArgumentError` when given a string presentation. From b0f4bd4d0ce3fc43400ff26a81d84db2a4081557 Mon Sep 17 00:00:00 2001 From: Joseph Macgowan <117601359+jmacgowan@users.noreply.github.com> Date: Fri, 16 Oct 2026 22:47:22 +0100 Subject: [PATCH 28/28] Delete .vscode/settings.json --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 2b031df..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "cmake.sourceDirectory": "/home/joem/VIP/Semigroups.jl/deps/src" -}