diff --git a/src/AutGroup.jl b/src/AutGroup.jl index e5d6540..10b3bc5 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -13,7 +13,7 @@ end typealias AutGroupElem GWord{AutSymbol} -type AutGroup <: FPGroup +type AutGroup <: AbstractFPGroup objectGroup::Group gens::Vector{AutSymbol} end diff --git a/src/FPGroups.jl b/src/FPGroups.jl new file mode 100644 index 0000000..4ab27c8 --- /dev/null +++ b/src/FPGroups.jl @@ -0,0 +1,188 @@ +############################################################################### +# +# FPSymbol/FPGroupElem/FPGroup definition +# +############################################################################### + +immutable FPSymbol <: GSymbol + str::String + pow::Int +end + +typealias FPGroupElem = GWord{FPSymbol} + +type FPGroup <: AbstractFPGroup + gens::Vector{FPSymbol} + rels::Dict{FPGroupElem, FPGroupElem} + + function FPGroup{T<:GSymbol}(gens::Vector{T}, rels::Dict{FPGroupElem, FPGroupElem}) + G = new(gens) + G.rels = Dict(G(k) => G(v) for (k,v) in rels) + return G + end +end + +export FPGroupElem, FPGroup + +############################################################################### +# +# Type and parent object methods +# +############################################################################### + +parent_type(::Type{FPGroupElem}) = FPGroup + +elem_type(::FPGroup) = FPGroupElem + +############################################################################### +# +# FPSymbol constructors +# +############################################################################### + +FPSymbol(s::String) = FPSymbol(s,1) + +convert(::Type{FPSymbol}, s::FreeSymbol) = FPSymbol(s.str, s.pow) + +FPGroup(gens::Vector{FPSymbol}) = FPGroup(gens, Dict{FPGroupElem, FPGroupElem}()) + +FPGroup(a::Vector{String}) = FPGroup([FPSymbol(i) for i in a]) + +FPGroup(n::Int, symbol::String="f") = FPGroup(["$symbol$i" for i in 1:n]) +FPGroup(H::FreeGroup) = FPGroup([FPSymbol(s) for s in H.gens]) + +############################################################################### +# +# Parent object call overloads +# +############################################################################### + +function (G::FPGroup)() + id = FPGroupElem(FPSymbol("", 0)) + id.parent = G + return id +end + +function (G::FPGroup)(w::GWord) + if length(w) == 0 + return G() + end + + if eltype(w.symbols) == FreeSymbol + w = FPGroupElem(w.symbols) + end + + if eltype(w.symbols) == FPSymbol + for s in w.symbols + i = findfirst(g -> g.str == s.str, G.gens) + i == 0 && throw("Symbol $s does not belong to $G.") + s.pow % G.gens[i].pow == 0 || throw("Symbol $s doesn't belong to $G.") + end + end + w.parent = G + return reduce!(w) +end + +(G::FPGroup)(s::FPSymbol) = G(FPGroupElem(s)) + +############################################################################### +# +# Basic manipulation +# +############################################################################### + +hash(s::FPSymbol, h::UInt) = hash(s.str, hash(s.pow, hash(FPSymbol, h))) + +change_pow(s::FPSymbol, n::Int) = FPSymbol(s.str, n) + +length(s::FPSymbol) = abs(s.pow) + +############################################################################### +# +# String I/O +# +############################################################################### + +function show(io::IO, G::FPGroup) + print(io, "FPgroup on $(length(G.gens)) generators: ") + print(io, "⟨ ", join(G.gens, ", "), " | ", join(G.rels, ", "), " ⟩.") +end + +############################################################################### +# +# Comparison +# +############################################################################### + +function (==)(s::FPSymbol, t::FPSymbol) + isone(s) && isone(t) && return true + s.str == t.str || return false + s.pow == t.pow || return false + return true +end + +############################################################################### +# +# Inversion +# +############################################################################### + +inv(s::FPSymbol) = change_pow(s, -s.pow) + +############################################################################### +# +# Binary operations +# +############################################################################### + +(*)(W::FPGroupElem, Z::FPGroupElem) = r_multiply(W, Z.symbols) +(*)(W::FPGroupElem, s::FPSymbol) = r_multiply(W, [s]) +(*)(s::FPSymbol, W::FPGroupElem) = l_multiply(W, [s]) + +function reduce!(W::FPGroupElem) + if length(W) < 2 + deleteat!(W.symbols, find(x -> x.pow == 0, W.symbols)) + else + reduced = false + while !reduced + reduced = free_reduce!(W) || replace_all!(W, parent(W).rels) + end + end + + W.savedhash = hash(W.symbols, hash(typeof(W))) + W.modified = false + return W +end + +############################################################################### +# +# Misc +# +############################################################################### + +function add_rels!(G::FPGroup, newrels::Dict{FPGroupElem,FPGroupElem}) + for w in keys(newrels) + if !(w in keys(G.rels)) + G.rels[w] = G(newrels[w]) + end + end +end + +function /(G::FPGroup, newrels::Vector{FPGroupElem}) + for r in rels + parent(r) == G || throw("Can not form quotient group: $r is not an element of $G") + end + H = deepcopy(G) + newrels = Dict(H(r) => H() for r in newrels) + add_rels!(H, newrels) + return H +end + +function /(G::FreeGroup, rels::Vector{FreeGroupElem}) + for r in rels + parent(r) == G || throw("Can not form quotient group: $r is not an element of $G") + end + H = FPGroup(G) + H.rels = Dict(H(rel) => H() for rel in unique(rels)) + return H +end diff --git a/src/FreeGroup.jl b/src/FreeGroup.jl index 56f9e83..a023395 100644 --- a/src/FreeGroup.jl +++ b/src/FreeGroup.jl @@ -11,7 +11,7 @@ end typealias FreeGroupElem GWord{FreeSymbol} -type FreeGroup <: FPGroup +type FreeGroup <: AbstractFPGroup gens::Vector{FreeSymbol} # order::Vector{T} # fastmult_table::Array{Int,2} @@ -22,7 +22,7 @@ type FreeGroup <: FPGroup end end -export FreeGroupElem, FreeGroup, generators +export FreeGroupElem, FreeGroup ############################################################################### # @@ -116,28 +116,3 @@ end ############################################################################### inv(s::FreeSymbol) = change_pow(s, -s.pow) - -############################################################################### -# -# Misc -# -############################################################################### - -# function add_rel!{T<:FreeSymbol}(G::FreeGroup, w::GWord{T}) -# if !(w in G.rels) -# w = G(w) -# push!(G.rels, w) -# end -# return G -# end -# -# function quotientgroup(G::FreeGroup, rels::Vector{FreeGroupElem}) -# for r in rels -# parent(r) == G || throw("Can not form quotient group: $r is not an element of $G") -# end -# H = deepcopy(G) -# for rel in rels -# add_rel!(H, rel) -# end -# return H -# end diff --git a/src/Groups.jl b/src/Groups.jl index ef1156f..af2e929 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -18,7 +18,7 @@ import Base: deepcopy_internal doc""" ::GSymbol -> Abstract type which all group symbols of FPGroups should subtype. Each +> Abstract type which all group symbols of AbstractFPGroups should subtype. Each > concrete subtype should implement fields: > * `str` which is the string representation/identification of a symbol > * `pow` which is the (multiplicative) exponent of a symbol. @@ -53,7 +53,7 @@ type GWord{T<:GSymbol} <: GroupElem end end -abstract FPGroup <: Group +abstract AbstractFPGroup <: Group ############################################################################### # @@ -140,7 +140,7 @@ doc""" > returns vector of generators of `G`, as its elements. """ -gens(G::FPGroup) = [G(g) for g in G.gens] +gens(G::AbstractFPGroup) = [G(g) for g in G.gens] ############################################################################### # @@ -352,18 +352,25 @@ function replace(W::GWord, index, toreplace::GWord, replacement::GWord) end function replace_all!{T}(W::GWord{T}, subst_dict::Dict{GWord{T}, GWord{T}}) + modified = false for toreplace in reverse!(sort!(collect(keys(subst_dict)), by=length)) replacement = subst_dict[toreplace] i = findfirst(W, toreplace) while i ≠ 0 + modified = true replace!(W,i,toreplace, replacement) i = findnext(W, toreplace, i) end end - return W + return modified end -replace_all{T<:GSymbol}(W::GWord{T}, subst_dict::Dict{GWord{T}, GWord{T}}) = replace_all!(deepcopy(W), subst_dict) +function replace_all{T<:GSymbol}(W::GWord{T}, + subst_dict::Dict{GWord{T}, GWord{T}}) + W = deepcopy(W) + replace_all!(W, subst_dict) + return W +end ############################################################################### # @@ -404,6 +411,7 @@ end ############################################################################### include("FreeGroup.jl") +include("FPGroups.jl") include("AutGroup.jl") include("DirectProducts.jl") diff --git a/test/AutGroup-tests.jl b/test/AutGroup-tests.jl index c7984d4..98aaae9 100644 --- a/test/AutGroup-tests.jl +++ b/test/AutGroup-tests.jl @@ -87,7 +87,7 @@ @test isa(AutGroupElem(f), Groups.GWord) @test isa(AutGroupElem(f), AutGroupElem) @test isa(AutGroup(FreeGroup(3)), Nemo.Group) - @test isa(AutGroup(FreeGroup(1)), Groups.FPGroup) + @test isa(AutGroup(FreeGroup(1)), Groups.AbstractFPGroup) A = AutGroup(FreeGroup(1)) @test isa(Nemo.gens(A), Vector{AutGroupElem}) @test length(Nemo.gens(A)) == 1 diff --git a/test/FreeGroup-tests.jl b/test/FreeGroup-tests.jl index a193471..698dc38 100644 --- a/test/FreeGroup-tests.jl +++ b/test/FreeGroup-tests.jl @@ -131,7 +131,7 @@ end @test Groups.replace(c, 1, w, subst[w]) == s*t^-1 @test Groups.replace(s*c*t^-1, 1, w, subst[w]) == s^2*t^-2 @test Groups.replace(t*c*t, 2, w, subst[w]) == t*s - @test Groups.replace_all!(s*c*s*c*s, subst) == s*t^4*s*t^4*s + @test Groups.replace_all(s*c*s*c*s, subst) == s*t^4*s*t^4*s G = FreeGroup(["x", "y"]) x,y = Nemo.gens(G)