From 459c358fdfb158789a2ef86b185652c4a364e211 Mon Sep 17 00:00:00 2001 From: Lionel Zoubritzky Date: Thu, 30 Jun 2022 14:55:12 +0200 Subject: [PATCH] Add union_cycles --- Project.toml | 2 +- docs/src/rings.md | 2 + src/algorithms/rings.jl | 88 ++++++++++++++++++++++++++++++++++++++++- src/precompile.jl | 1 + test/runtests.jl | 3 ++ 5 files changed, 94 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index b2e7c29..904f987 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PeriodicGraphs" uuid = "18c5b727-b240-4874-878a-f2e242435bab" authors = ["Lionel Zoubritzky lionel.zoubritzky@gmail.com"] -version = "0.9.2" +version = "0.9.3" [deps] Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" diff --git a/docs/src/rings.md b/docs/src/rings.md index 7cb71e6..71de275 100644 --- a/docs/src/rings.md +++ b/docs/src/rings.md @@ -172,6 +172,8 @@ PeriodicGraphs.IterativeGaussianElimination PeriodicGraphs.gaussian_elimination! PeriodicGraphs.intersect_cycles! PeriodicGraphs.intersect_cycles +PeriodicGraphs.union_cycles! +PeriodicGraphs.union_cycles PeriodicGraphs.retrieve_track! PeriodicGraphs.rings_around PeriodicGraphs.EdgeDict diff --git a/src/algorithms/rings.jl b/src/algorithms/rings.jl index 030a4d1..e829492 100644 --- a/src/algorithms/rings.jl +++ b/src/algorithms/rings.jl @@ -1119,6 +1119,14 @@ Symmetric difference between two sorted lists `a` and `b`. Return the sorted list of elements belonging to `a` or to `b` but not to both. Use [`PeriodicGraphs.symdiff_cycles!`](@ref) to provide a pre-allocated destination. + +## Example +```jldoctest +julia> PeriodicGraphs.symdiff_cycles([3,4,5], [4,5,6]) +2-element Vector{Int64}: + 3 + 6 +``` """ symdiff_cycles(a, b) = symdiff_cycles!(Vector{Int}(undef, length(b) + length(a) - 1), a, b) @@ -1282,7 +1290,8 @@ retrieve_track!(gauss::IterativeGaussianEliminationDecomposition) = retrieve_tra Like [`PeriodicGraphs.intersect_cycles`](@ref) but stores the result in `c`. -`c` will be resized accordingly so its initial length does not matter. +`c` will be resized accordingly so its initial length does not matter as long as it is +at least as large as the resulting list. """ function intersect_cycles!(c::Vector{T}, a::Vector{T}, b::Vector{T}) where T isempty(b) && (empty!(c); return c) @@ -1301,6 +1310,7 @@ function intersect_cycles!(c::Vector{T}, a::Vector{T}, b::Vector{T}) where T c[j] = xa counter_b += 1 counter_b > lenb && @goto ret + xb = b[counter_b] end end @label ret @@ -1315,10 +1325,86 @@ Intersection between two sorted lists `a` and `b`. Return the sorted list of elements belonging to both `a` and `b`. Use [`PeriodicGraphs.intersect_cycles!`](@ref) to provide a pre-allocated destination. + +## Example +```jldoctest +julia> PeriodicGraphs.intersect_cycles([3,4,5], [4,5,6]) +2-element Vector{Int64}: + 4 + 5 +``` """ intersect_cycles(a, b) = intersect_cycles!(Vector{Int}(undef, min(length(a), length(b))), a, b) +""" + union_cycles!(c::Vector{T}, a::Vector{T}, b::Vector{T}) where T + +Like [`PeriodicGraphs.union_cycles`](@ref) but stores the result in `c`. + +`c` will be resized accordingly so its initial length does not matter as long as it is +at least as large as the resulting list. +""" +function union_cycles!(c::Vector{T}, a::Vector{T}, b::Vector{T}) where T + lenb = length(b) + lena = length(a) + counter_a = 0 + counter_b = 1 + y = lenb == 0 ? typemax(Int) : (@inbounds b[1]) + j = 1 + @inbounds while counter_a < lena + counter_a += 1 + x = a[counter_a] + while y < x + c[j] = y + j += 1 + counter_b += 1 + counter_b > lenb && @goto fillenda + y = b[counter_b] + end + c[j] = x + j += 1 + if y == x + counter_b += 1 + if counter_b > lenb + counter_a += 1 + @goto fillenda + end + y = b[counter_b] + end + end + remaining_towriteb = lenb - counter_b + 1 + remaining_towriteb > 0 && unsafe_copyto!(c, j, b, counter_b, remaining_towriteb) + @inbounds resize!(c, (j + remaining_towriteb - 1) % UInt) + return c + + @label fillenda + remaining_towritea = lena - counter_a + 1 + remaining_towritea > 0 && unsafe_copyto!(c, j, a, counter_a, remaining_towritea) + @inbounds resize!(c, (j + remaining_towritea - 1) % UInt) + return c +end + +""" + union_cycles(a, b) + +Union between two sorted lists `a` and `b`. +Return the sorted list of elements belonging to `a` or `b` or both. + +Use [`PeriodicGraphs.union_cycles!`](@ref) to provide a pre-allocated destination. + +## Example +```jldoctest +julia> PeriodicGraphs.union_cycles([3,4,5], [4,5,6]) +4-element Vector{Int64}: + 3 + 4 + 5 + 6 +``` +""" +union_cycles(a, b) = union_cycles!(Vector{Int}(undef, length(a) + length(b)), a, b) + # function retrieve_vcycle(ecycle, known_pairs) # fst_pair = known_pairs[ecycle[1]] # last_pair = known_pairs[ecycle[end]] diff --git a/src/precompile.jl b/src/precompile.jl index fee9b59..4fa94ab 100644 --- a/src/precompile.jl +++ b/src/precompile.jl @@ -52,6 +52,7 @@ function _precompile_() @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.unsafe_incr!),Ptr{SmallIntT}}) @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.symdiff_cycles), Vector{Int}, Vector{Int}}) @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.intersect_cycles), Vector{Int}, Vector{Int}}) + @enforce Base.precompile(Tuple{typeof(PeriodicGraphs.union_cycles), Vector{Int}, Vector{Int}}) @enforce Base.precompile(Tuple{Type{PeriodicGraphs.IterativeGaussianEliminationLength}, Vector{Int}}) @enforce Base.precompile(Tuple{Type{PeriodicGraphs.IterativeGaussianElimination{Vector{Int32}}}, Vector{Int}}) @enforce Base.precompile(Tuple{Type{PeriodicGraphs.IterativeGaussianElimination{Vector{Vector{Int32}}}}, Vector{Int}}) diff --git a/test/runtests.jl b/test/runtests.jl index 20dae26..e3f714d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -871,6 +871,9 @@ end @test PeriodicGraphs.symdiff_cycles([3,4], [3,5,6]) == PeriodicGraphs.symdiff_cycles([4,5,6,7,8], [7,8]) == [4,5,6] @test PeriodicGraphs.intersect_cycles([3,4], [3,5,6]) == PeriodicGraphs.intersect_cycles([2,3,5], [1,3]) == [3] + @test PeriodicGraphs.intersect_cycles([3,4,5], [4,5,6]) == PeriodicGraphs.intersect_cycles([4,5,7], [2,4,5,6]) == [4,5] + @test PeriodicGraphs.union_cycles([3,4], [4,5,6]) == PeriodicGraphs.union_cycles([4,5,6], [3,4]) == [3,4,5,6] + @test PeriodicGraphs.union_cycles([4,6], [3,5,6]) == PeriodicGraphs.union_cycles([3,5,6], [3,4,6]) == [3,4,5,6] gausslengths = PeriodicGraphs.IterativeGaussianEliminationLength([3, 4, 7]) @test !PeriodicGraphs.gaussian_elimination!(gausslengths, [3, 6])