From 06894fd6d171e197e679eb36e089fcecf13740de Mon Sep 17 00:00:00 2001 From: kalmar Date: Mon, 23 Jan 2017 16:53:33 +0100 Subject: [PATCH] create package from existing code --- src/Groups.jl | 177 ++++++++++++++++++++++++++++++++++++++++++++- src/free_groups.jl | 22 ++++++ test/runtests.jl | 81 ++++++++++++++++++++- 3 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 src/free_groups.jl diff --git a/src/Groups.jl b/src/Groups.jl index 19e0028..6ce0cf8 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -1,5 +1,178 @@ module Groups -# package code goes here +import Base: length, ==, hash, show +import Base: one, inv, reduce, *, ^ -end # module +export GSymbol, GWord + +abstract GSymbol + +function show(io::IO, s::GSymbol) + if s.pow == 0 + print(io, "(id)") + elseif s.pow == 1 + print(io, s.gen) + else + print(io, (s.gen)*"^$(s.pow)") + end +end + +length(s::GSymbol) = (s.pow == 0 ? 0 : 1) + +IdSymbol(T::Type{GSymbol}) = throw(ArgumentError("Define IdSymbol(::Type{$T}) which is the identity element for Your type!")) + +one{T<:GSymbol}(::Type{T}) = IdSymbol(T) +one(s::GSymbol) = one(typeof(s)) + +(*){T<:GSymbol}(s::T, t::T) = return GWord{T}([s])*t + +change_pow(s::GSymbol, n::Int) = throw(ArgumentError("Define change_pow function for $(typeof(s))!")) + + +abstract Word + +type GWord{T<:GSymbol} <: Word + symbols::Vector{T} + savedhash::UInt + modified::Bool + + function GWord(symbols::Vector{T}) + return new(symbols, hash(symbols), true) + end +end + +GWord{T<:GSymbol}(s::T) = GWord{T}([s]) + +IDWord{T<:GSymbol}(::Type{T}) = GWord(one(T)) +IDWord{T<:GSymbol}(W::GWord{T}) = IDWord(T) + +function length(W::GWord) + return sum([abs(s.pow) for s in W.symbols]) +end + +one{T}(::Type{GWord{T}}) = IDWord(T) +one{T}(w::GWord{T}) = one(GWord{T}) + +function inv{T}(W::GWord{T}) + if length(W) == 0 + return W + else + w = GWord{T}(reverse([inv(s) for s in W.symbols])) + w.modified = true + return w + end +end + +function join_free_symbols!(W::GWord) + reduced = true + for i in 1:length(W.symbols) - 1 + if W.symbols[i].gen == W.symbols[i+1].gen + reduced = false + p1 = W.symbols[i].pow + p2 = W.symbols[i+1].pow + W.symbols[i+1] = change_pow(W.symbols[i], p1 + p2) + W.symbols[i] = one(W.symbols[i]) + end + end + return reduced +end + +function freegroup_reduce!{T}(W::GWord{T}) + if length(W) < 2 + deleteat!(W.symbols, find(x -> x.pow == 0, W.symbols)) + else + reduced = false + while !reduced + reduced = join_free_symbols!(W) + deleteat!(W.symbols, find(x -> x.pow == 0, W.symbols)) + end + end + + W.modified = false + W.savedhash = hash(W.symbols,hash(typeof(W))) + return W +end + +freegroup_reduce(W::GWord) = freegroup_reduce!(deepcopy(W)) + +hash{T}(W::GWord{T}) = (W.modified && freegroup_reduce!(W); W.savedhash) + +function (==){T}(W::GWord{T}, Z::GWord{T}) + W.modified && freegroup_reduce!(W) # reduce clears the flag and recalculate the hash + Z.modified && freegroup_reduce!(Z) + return W.savedhash == Z.savedhash && W.symbols == Z.symbols +end + +(==){T}(W::GWord{T}, s::T) = W == GWord(s) +(==){T}(s::T, W::GWord{T}) = W == GWord(s) + +function show(io::IO, W::GWord) + if length(W) == 0 + print(io, "(id)") + else + join(io, [string(s) for s in W.symbols], "*") + end +end + +function r_multiply!(W::GWord, x; reduced::Bool=true) + if length(x) > 0 + push!(W.symbols, x...) + end + if reduced + freegroup_reduce!(W) + end + return W +end + +function l_multiply!(W::GWord, x; reduced::Bool=true) + if length(x) > 0 + unshift!(W.symbols, reverse(x)...) + end + if reduced + freegroup_reduce!(W) + end + return W +end + +r_multiply(W::GWord, x; reduced::Bool=true) = + r_multiply!(deepcopy(W),x, reduced=reduced) +l_multiply(W::GWord, x; reduced::Bool=true) = + l_multiply!(deepcopy(W),x, reduced=reduced) + +(*){T}(W::GWord{T}, Z::GWord{T}) = r_multiply(W, Z.symbols) +(*)(W::GWord, s::GSymbol) = W*GWord(s) +(*)(s::GSymbol, W::GWord) = GWord(s)*W + +function power_by_squaring{T}(x::GWord{T}, p::Integer) + if p < 0 + return power_by_squaring(inv(x), -p) + elseif p == 0 + return one(x) + elseif p == 1 + return deepcopy(x) + elseif p == 2 + return x*x + end + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) > 0 + x *= x + end + y = x + while p > 0 + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) >= 0 + x *= x + end + y *= x + end + return freegroup_reduce!(y) +end + +(^)(x::GWord, n::Integer) = power_by_squaring(x,n) +(^){T<:GSymbol}(x::T, n::Integer) = GWord(x)^n + +include("free_groups.jl") + +end # of module Groups diff --git a/src/free_groups.jl b/src/free_groups.jl new file mode 100644 index 0000000..8e34932 --- /dev/null +++ b/src/free_groups.jl @@ -0,0 +1,22 @@ +import Base:convert + +export FGSymbol, FGWord + +immutable FGSymbol <: GSymbol + gen::String + pow::Int +end + +(==)(s::FGSymbol, t::FGSymbol) = s.gen == t.gen && s.pow == t.pow +hash(s::FGSymbol, h::UInt) = hash(s.gen, hash(s.pow, hash(:FGSymbol, h))) + +IdSymbol(::Type{FGSymbol}) = FGSymbol("(id)", 0) +FGSymbol(x::String) = FGSymbol(x,1) + +inv(s::FGSymbol) = FGSymbol(s.gen, -s.pow) +convert(::Type{FGSymbol}, x::String) = FGSymbol(x) +change_pow(s::FGSymbol, n::Int) = (n==0 ? i=one(s) : FGSymbol(s.gen, n)) + +typealias FGWord GWord{FGSymbol} + +FGWord(s::FGSymbol) = FGWord([s]) diff --git a/test/runtests.jl b/test/runtests.jl index 3f1348d..a581bb4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,4 +2,83 @@ using Groups using Base.Test # write your own tests here -@test 1 == 2 +s = FGSymbol("s") +t = FGSymbol("t") + +@testset "FGSymbols" begin + @testset "defines" begin + @test isa(FGSymbol(string(Char(rand(50:2000)))), Groups.GSymbol) + @test FGSymbol("abc").pow == 1 + @test isa(s, FGSymbol) + @test isa(t, FGSymbol) + end + @testset "eltary functions" begin + @test length(s) == 1 + @test one(s) == s^0 + @test one(s) == one(FGSymbol) + @test Groups.change_pow(s,0) == one(s) + @test length(one(s)) == 0 + @test inv(s).pow == -1 + @test FGSymbol("s", 3) == Groups.change_pow(s,3) + @test s^2 ≠ t^2 + + end + @testset "powers" begin + s⁴ = Groups.change_pow(s,4) + @test s⁴.pow == 4 + @test (s^4).symbols[1] == Groups.change_pow(s,4) + @test s*s == s^2 + @test inv(s*s) == inv(s^2) + @test inv(s)^2 == inv(s^2) + @test inv(s)*inv(s) == inv(s^2) + @test inv(s*s) == inv(s)*inv(s) + end +end + +@testset "GWords" begin + @testset "defines" begin + @test isa(Groups.GWord(s), Groups.GWord) + @test isa(Groups.GWord(s), FGWord) + @test isa(FGWord(s), Groups.GWord) + @test isa(s*s, FGWord) + @test s*s == s^2 + @test t*s ≠ s*t + end + @testset "eltary functions" begin + @test length(FGWord(s)) == 1 + @test length(s*s) == 2 + @test length(s*s^-1) == 0 + @test length(s*t^-1) == 2 + @test isa(one(FGWord), FGWord) + @test one(FGWord).symbols == Vector{FGSymbol}([one(FGSymbol)]) + @test isa(one(Groups.GWord{FGSymbol}), Groups.GWord{FGSymbol}) + w = s*t*s^-1 + @test isa(one(w), FGWord) + @test inv(s*t) == t^-1*s^-1 + @test inv(w) == s*t^-1*s^-1 + end + + @testset "reductions" begin + @test one(FGWord) == one(s)*one(s) + w = GWord{FGSymbol}([s]) + push!(w.symbols, (s^-1).symbols[1]) + @test Groups.freegroup_reduce!(w) == one(FGWord) + o = (t*s)^3 + @test o == t*s*t*s*t*s + p = (t*s)^-3 + @test p == s^-1*t^-1*s^-1*t^-1*s^-1*t^-1 + @test o*p == one(FGWord) + w = FGWord([o.symbols..., p.symbols...]) + @test Groups.freegroup_reduce!(w).symbols ==Vector{FGSymbol}([]) + end + @testset "arithmetic" begin + @test Groups.r_multiply!(FGWord(t),[s,t]; reduced=true) == t*s*t + @test Groups.r_multiply!(FGWord(t),[s,t]; reduced=false) == t*s*t + + @test Groups.l_multiply!(FGWord(t),[s,t]; reduced=true) == t*s*t + @test Groups.l_multiply!(FGWord(t),[s,t]; reduced=false) == t*s*t + @test (t*s*t^-1)^10 == t*s^10*t^-1 + @test (t*s*t^-1)^-10 == t*s^-10*t^-1 + end + +end