diff --git a/docs/src/Groups/group_characters.md b/docs/src/Groups/group_characters.md index 5e4727974637..b9f59f84be44 100644 --- a/docs/src/Groups/group_characters.md +++ b/docs/src/Groups/group_characters.md @@ -101,6 +101,7 @@ characteristic(tbl::GAPGroupCharacterTable) Base.mod(tbl::GAPGroupCharacterTable, p::Int) quo(tbl::GAPGroupCharacterTable, nclasses::Vector{Int}) all_character_table_names +is_character_table_name ``` ## Attributes of group characters diff --git a/docs/src/Groups/tom.md b/docs/src/Groups/tom.md index e1647f48bffb..fb04e7274c40 100644 --- a/docs/src/Groups/tom.md +++ b/docs/src/Groups/tom.md @@ -30,6 +30,11 @@ These tables of marks can be fetched via the names of these groups, which coincide with the names of the character tables of these groups in the Character Table Library, see [`table_of_marks(id::String)`](@ref). +Like the library of character tables, the library of tables of marks +can be used similar to group libraries (see [Group libraries](@ref)) +in the sense that [`all_table_of_marks_names`](@ref) returns descriptions +of all those available tables of marks that have certain properties. + ## Construct tables of marks ```@docs @@ -37,6 +42,8 @@ GAPGroupTableOfMarks table_of_marks(G::Union{GAPGroup, FinGenAbGroup}) table_of_marks(id::String) Base.show(io::IO, ::MIME"text/plain", tom::GAPGroupTableOfMarks) +all_table_of_marks_names +is_table_of_marks_name ``` ## Attributes and operations for tables of marks diff --git a/src/GAP/wrappers.jl b/src/GAP/wrappers.jl index 6d6a2eb31e27..3591823434c2 100644 --- a/src/GAP/wrappers.jl +++ b/src/GAP/wrappers.jl @@ -120,6 +120,7 @@ GAP.@wrap HasMaxes(x::GapObj)::Bool GAP.@wrap HasMaximalAbelianQuotient(x::Any)::Bool GAP.@wrap HasSize(x::Any)::Bool GAP.@wrap Hasfhmethsel(x::GapObj)::Bool +GAP.@wrap Identifier(x::GapObj)::GapObj GAP.@wrap Identity(x::GapObj)::GapObj GAP.@wrap Image(x::Any)::GapObj GAP.@wrap Image(x::Any, y::Any)::GapObj @@ -247,6 +248,7 @@ GAP.@wrap IsSyllableAssocWordRep(x::Any)::Bool GAP.@wrap IsSyllableWordsFamily(x::Any)::Bool GAP.@wrap IsSymmetricForm(x::Any)::Bool GAP.@wrap IsSymmetricGroup(x::Any)::Bool +GAP.@wrap IsTableOfMarks(x::Any)::Bool GAP.@wrap IsTransitive(x::Any)::Bool GAP.@wrap IsTransitive(x::Any, y::Any)::Bool GAP.@wrap IsTrivial(x::Any)::Bool diff --git a/src/Groups/group_characters.jl b/src/Groups/group_characters.jl index 7667ba4af4ea..91b5fa7eba0b 100644 --- a/src/Groups/group_characters.jl +++ b/src/Groups/group_characters.jl @@ -340,6 +340,17 @@ julia> println(character_table("A5", 2)) julia> println(character_table("J5")) nothing ``` + +Several names can be admissible for the same character table from the library. +For example, the alternating group on five points is isomorphic to the +projective special linear groups in dimension 2 over the fields with +four or five elements, and each of the strings `"A5"`, `"L2(4)"`, `"L2(5)"` +is an admissible name for its library character table. +The names are not case sensitive, thus also `"a5"` is admissible. + +Use [`all_character_table_names`](@ref) for creating a vector that contains +one admissible name for each available character table, +perhaps filtered by some conditions. """ function character_table(id::String, p::Int = 0) if p != 0 @@ -461,8 +472,9 @@ true """ all_character_table_names(L...; ordered_by = nothing) -Return an vector of strings that contains all those names of character tables -in the character table library that satisfy the conditions in the vector `L`. +Return a vector of strings that contains an admissible name of each +character table in the character table library that satisfies the conditions +in the vector `L`. # Examples ``` @@ -495,6 +507,24 @@ function all_character_table_names(L...; ordered_by = nothing) end +""" + is_character_table_name(name::String) + +Return `true` if `character_table(name)` returns a character table, +and `false` otherwise + +# Examples +```jldoctest +julia> is_character_table_name("J1") +true + +julia> is_character_table_name("J5") +false +``` +""" +is_character_table_name(name::String) = GAPWrap.LibInfoCharacterTable(GapObj(name)) !== GAP.Globals.fail + + ############################################################################## # # `print` and `show` character tables diff --git a/src/Groups/tom.jl b/src/Groups/tom.jl index 6e257aa2b40d..18f94b11ba54 100644 --- a/src/Groups/tom.jl +++ b/src/Groups/tom.jl @@ -23,15 +23,46 @@ Objects of type `GAPGroupTableOfMarks` support [`get_attribute`](@ref). isomorphism::Map # isomorphism from `group` to a group in GAP function GAPGroupTableOfMarks(G::Union{GAPGroup, FinGenAbGroup}, tom::GapObj, iso::Map) + @req GAPWrap.IsTableOfMarks(tom) "tom must be a GAP table of marks" return new(tom, G, iso) end function GAPGroupTableOfMarks(tom::GapObj) # group and isomorphism are left undefined + @req GAPWrap.IsTableOfMarks(tom) "tom must be a GAP table of marks" return new(tom) end end + +# If `tblid` is an admissible name for a library character table +# then translate it to the GAP name of the corresponding +# library table of marks if there is one. +function _table_of_marks_name_in_GAP(tblid::String) + info = GAPWrap.LibInfoCharacterTable(GapObj(tblid)) + info === GAP.Globals.fail && return false, "" + + tblid = string(info.firstName::GapObj) + info = GAP.Globals.LIBLIST.TOM_TBL_INFO::GapObj + pos = findfirst(isequal(GapObj(lowercase(tblid))), Vector{GapObj}(info[2])) + pos === nothing && return false, "" + + return true, string(info[1][pos]::GapObj) +end + +# If `tomid` is the GAP name for a library table of marks +# then translate it to the GAP name of the corresponding +# library character table. +# (There is always such a character table.) +function _character_table_name_in_GAP(tomid::String) + info = GAP.Globals.LIBLIST.TOM_TBL_INFO + pos = findfirst(isequal(GapObj(lowercase(tomid))), Vector{GapObj}(info[1])) + pos === nothing && return false, "" + info = GAPWrap.LibInfoCharacterTable(info[2][pos]) + return true, string(info.firstName) +end + + # access to field values via functions GapObj(tom::GAPGroupTableOfMarks) = tom.GAPTable @@ -69,12 +100,10 @@ Table of marks of Sym(3) 4: 1 1 1 1 ``` """ -function table_of_marks(G::Union{GAPGroup, FinGenAbGroup}) - return get_attribute!(G, :table_of_marks) do - iso = isomorphism_to_GAP_group(G) - gaptom = GAPWrap.TableOfMarks(codomain(iso)) - return GAPGroupTableOfMarks(G, gaptom, iso) - end +@attr GAPGroupTableOfMarks function table_of_marks(G::Union{GAPGroup, FinGenAbGroup}) + iso = isomorphism_to_GAP_group(G) + gaptom = GAPWrap.TableOfMarks(codomain(iso)) + return GAPGroupTableOfMarks(G, gaptom, iso) end # A table of marks constructed from a group is stored in this group. @@ -101,14 +130,11 @@ nothing ``` """ function table_of_marks(id::String) - # normalize `id` - info = GAPWrap.LibInfoCharacterTable(GapObj(id)) - if info !== GAP.Globals.fail - id = string(info.firstName) - end - return get!(tables_of_marks_by_id, id) do - tom = GAPWrap.TableOfMarks(GapObj(id)) - tom === GAP.Globals.fail && return nothing + flag, tomid =_table_of_marks_name_in_GAP(id) + flag || return nothing + return get!(tables_of_marks_by_id, tomid) do + tom = GAPWrap.TableOfMarks(GapObj(tomid)) + @assert tom !== GAP.Globals.fail "no table of marks with name $tomid?" if GAP.Globals.HasUnderlyingGroup(tom) G = _oscar_group(GAPWrap.UnderlyingGroup(tom)) iso = isomorphism_to_GAP_group(G) @@ -116,9 +142,65 @@ function table_of_marks(id::String) else return GAPGroupTableOfMarks(tom) end - end + end::GAPGroupTableOfMarks end +""" + all_table_of_marks_names(L...; ordered_by = nothing) + +Return a vector of strings that contains an admissible name of each +table of marks in the library of tables of marks that satisfies the +conditions in the vector `L`. + +The supported conditions in `L` are the same as for +[`all_character_table_names`](@ref), +and the returned vector contains the subset of those names returned by +`all_character_table_names` with the same input for which a table of marks +is available in the library. + +# Examples +``` +julia> spor_names = all_table_of_marks_names(is_sporadic_simple => true); + +julia> println(spor_names[1:5]) +["Co3", "HS", "He", "J1", "J2"] + +julia> spor_names = all_table_of_marks_names(is_sporadic_simple; + ordered_by = order); + +julia> println(spor_names[1:5]) +["M11", "M12", "J1", "M22", "J2"] + +julia> length(all_table_of_marks_names(number_of_conjugacy_classes => 5)) +4 +``` +""" +function all_table_of_marks_names(L...; ordered_by = nothing) + names = all_character_table_names(L...; ordered_by = ordered_by) + return filter(x -> _table_of_marks_name_in_GAP(x)[1], names) +end +#TODO: As soon as the character table library provides an *a priori* +# filtering of names for which a table of marks is available, +# use this instead of filtering afterwards. + + +""" + is_table_of_marks_name(name::String) + +Return `true` if `table_of_marks(name)` returns a table of marks, +and `false` otherwise + +# Examples +```jldoctest +julia> is_table_of_marks_name("J1") +true + +julia> is_table_of_marks_name("J4") +false +``` +""" +is_table_of_marks_name(name::String) = _table_of_marks_name_in_GAP(name)[1] + # For tables of marks with stored group, we take the hash value of the group. # For tables of marks without stored group, we take the table identifier. @@ -305,11 +387,20 @@ julia> identifier(table_of_marks("A5")) julia> identifier(table_of_marks(symmetric_group(3))) "" + +!!! warning "Note:" + The `identifier` of a table of marks from the library is equal to + the `identifier` of the corresponding library character table. + In a few cases, this value differs from the `GAP.Globals.Identifier` + value of the underlying `GapObj` of the table of marks. ``` """ -function identifier(tom::GAPGroupTableOfMarks) - GAP.Globals.HasIdentifier(GapObj(tom)) || return "" - return string(GAP.Globals.Identifier(GapObj(tom))) +@attr String function identifier(tom::GAPGroupTableOfMarks) + gaptom = GapObj(tom) + GAPWrap.IsLibTomRep(gaptom) || return "" + flag, id = _character_table_name_in_GAP(string(GAPWrap.Identifier(gaptom))) + @assert flag "no character table for GAP table of marks?" + return id end @@ -535,18 +626,10 @@ julia> character_table(tom) == character_table(g) true ``` """ -function character_table(tom::GAPGroupTableOfMarks) - tbl = get_attribute!(tom, :character_table) do - if GAPWrap.IsLibTomRep(GapObj(tom)) - info = GAP.Globals.LIBLIST.TOM_TBL_INFO - pos = findfirst(isequal(GapObj(lowercase(identifier(tom)))), Vector{GapObj}(info[1])) - pos !== GAP.Globals.fail && return character_table(string(info[2][pos])) - end - return character_table(group(tom)) - end - +@attr GAPGroupCharacterTable function character_table(tom::GAPGroupTableOfMarks) + GAPWrap.IsLibTomRep(GapObj(tom)) && return character_table(identifier(tom)) + tbl = character_table(group(tom)) set_attribute!(tbl, :table_of_marks, tom) - return tbl end @@ -566,14 +649,11 @@ julia> table_of_marks(tbl) == table_of_marks(g) true ``` """ -function table_of_marks(tbl::GAPGroupCharacterTable) - tom = get_attribute!(tbl, :table_of_marks) do - isdefined(tbl, :group) && return table_of_marks(group(tbl)) - return table_of_marks(identifier(tbl)) - end - +@attr Union{Nothing, GAPGroupTableOfMarks} function table_of_marks(tbl::GAPGroupCharacterTable) + isdefined(tbl, :group) && return table_of_marks(group(tbl)) + tom = table_of_marks(identifier(tbl)) + tom === nothing && return nothing set_attribute!(tom, :character_table, tbl) - return tom end diff --git a/src/exports.jl b/src/exports.jl index a59a34e15263..01c52834bc68 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -230,6 +230,7 @@ export all_perfect_groups export all_primitive_groups export all_small_groups export all_subsets_matroid +export all_table_of_marks_names export all_transitive_groups export all_triangulations export allow_unicode @@ -745,6 +746,7 @@ export is_canonically_isomorphic export is_canonically_isomorphic_with_map export is_cartier export is_cellular +export is_character_table_name export is_characteristic_subgroup export is_closed_embedding export is_clutter @@ -873,6 +875,7 @@ export is_subscheme export is_subset export is_supersolvable, has_is_supersolvable, set_is_supersolvable export is_surjective +export is_table_of_marks_name export is_ternary export is_total export is_transitive diff --git a/test/Groups/tom.jl b/test/Groups/tom.jl index 567607304e4a..de14b99ff6fa 100644 --- a/test/Groups/tom.jl +++ b/test/Groups/tom.jl @@ -243,7 +243,7 @@ Test the case where a group has a custom name where the first character should not be turned into lowercase ```jldoctest tables_of_marks.test julia> table_of_marks(SL(2,2)) -SL(2,2) +Table of marks of SL(2,2) 1: 6 2: 3 1