Skip to content

Commit

Permalink
Update Combinatorics for Julia 1.0 (#69)
Browse files Browse the repository at this point in the history
* Update Combinatorics for 1.0

* Remove dependence on Compat
  • Loading branch information
HarrisonGrodin authored and ararslan committed Aug 10, 2018
1 parent 73198fc commit 0e0edc6
Show file tree
Hide file tree
Showing 16 changed files with 78 additions and 100 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ language: julia
os:
- linux
julia:
- 0.6
- 0.7
- 1.0
- nightly
sudo: false
notifications:
Expand Down
4 changes: 1 addition & 3 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
julia 0.6
Compat 0.59.0
julia 0.7
Polynomials
IterTools
2 changes: 0 additions & 2 deletions src/Combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ VERSION < v"0.7.0-beta2.199" && __precompile__()

module Combinatorics

using Compat
using Polynomials
using IterTools

include("numbers.jl")
include("factorials.jl")
Expand Down
34 changes: 17 additions & 17 deletions src/combinations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ struct Combinations{T}
t::Int
end

Base.start(c::Combinations) = collect(1:c.t)
function Base.next(c::Combinations, s)
function Base.iterate(c::Combinations, s = collect(1:c.t))
(!isempty(s) && s[1] > length(c.a) - c.t + 1) && return

comb = [c.a[si] for si in s]
if c.t == 0
# special case to generate 1 result for t==0
Expand All @@ -31,7 +32,6 @@ function Base.next(c::Combinations, s)
end
(comb, s)
end
Base.done(c::Combinations, s) = !isempty(s) && s[1] > length(c.a) - c.t + 1

Base.length(c::Combinations) = binomial(length(c.a), c.t)

Expand Down Expand Up @@ -59,7 +59,7 @@ end
Generate combinations of the elements of `a` of all orders. Chaining of order iterators
is eager, but the sequence at each order is lazy.
"""
combinations(a) = IterTools.chain([combinations(a, k) for k = 1:length(a)]...)
combinations(a) = Iterators.flatten([combinations(a, k) for k = 1:length(a)])



Expand Down Expand Up @@ -87,7 +87,7 @@ struct CoolLexIterState{T<:Integer}
R3::T
end

function Base.start(C::CoolLexCombinations)
function Base.iterate(C::CoolLexCombinations)
if C.n < 0
throw(DomainError(C.n))
end
Expand All @@ -102,10 +102,13 @@ function Base.start(C::CoolLexCombinations)
T = BigInt
end

CoolLexIterState{T}(0, 0, T(1) << C.n, (T(1) << C.t) - 1)
state = CoolLexIterState{T}(0, 0, T(1) << C.n, (T(1) << C.t) - 1)
iterate(C, state)
end

function Base.next(C::CoolLexCombinations, S::CoolLexIterState)
function Base.iterate(C::CoolLexCombinations, S::CoolLexIterState)
(S.R3 & S.R2 != 0) && return

R0 = S.R0
R1 = S.R1
R2 = S.R2
Expand Down Expand Up @@ -134,8 +137,6 @@ function _cool_lex_visit(X::Integer)
subset
end

Base.done(C::CoolLexCombinations, S::CoolLexIterState) = (S.R3 & S.R2 != 0)

Base.length(C::CoolLexCombinations) = max(0, binomial(C.n, C.t))


Expand Down Expand Up @@ -189,8 +190,9 @@ function multiset_combinations(a, t::Integer)
multiset_combinations(m, f, t)
end

Base.start(c::MultiSetCombinations) = c.ref
function Base.next(c::MultiSetCombinations, s)
function Base.iterate(c::MultiSetCombinations, s = c.ref)
((!isempty(s) && max(s[1], c.t) > length(c.ref)) || (isempty(s) && c.t > 0)) && return

ref = c.ref
n = length(ref)
t = c.t
Expand Down Expand Up @@ -218,8 +220,6 @@ function Base.next(c::MultiSetCombinations, s)
end
(comb, s)
end
Base.done(c::MultiSetCombinations, s) =
(!isempty(s) && max(s[1], c.t) > length(c.ref)) || (isempty(s) && c.t > 0)

struct WithReplacementCombinations{T}
a::T
Expand All @@ -237,8 +237,9 @@ Generate all combinations with replacement of size `t` from an array `a`.
"""
with_replacement_combinations(a, t::Integer) = WithReplacementCombinations(a, t)

Base.start(c::WithReplacementCombinations) = [1 for i in 1:c.t]
function Base.next(c::WithReplacementCombinations, s)
function Base.iterate(c::WithReplacementCombinations, s = [1 for i in 1:c.t])
(!isempty(s) && s[1] > length(c.a) || c.t < 0) && return

n = length(c.a)
t = c.t
comb = [c.a[si] for si in s]
Expand All @@ -261,7 +262,6 @@ function Base.next(c::WithReplacementCombinations, s)
end
(comb, s)
end
Base.done(c::WithReplacementCombinations, s) = !isempty(s) && s[1] > length(c.a) || c.t < 0

## Power set

Expand All @@ -276,5 +276,5 @@ subsets.
function powerset(a, min::Integer=0, max::Integer=length(a))
itrs = [combinations(a, k) for k = min:max]
min < 1 && append!(itrs, eltype(a)[])
IterTools.chain(itrs...)
Iterators.flatten(itrs)
end
10 changes: 4 additions & 6 deletions src/multinomials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ struct MultiExponents{T}
nterms::Int
end

Base.start(m::MultiExponents) = start(m.c)

# Standard stars and bars:
# https://en.wikipedia.org/wiki/Stars_and_bars_(combinatorics)
function Base.next(m::MultiExponents, s)
stars, ss = next(m.c, s)
function Base.iterate(m::MultiExponents, s = nothing)
next = s === nothing ? iterate(m.c) : iterate(m.c, s)
next === nothing && return
stars, ss = next

# stars minus their consecutive
# position becomes their index
Expand All @@ -25,8 +25,6 @@ function Base.next(m::MultiExponents, s)
result, ss
end

Base.done(m::MultiExponents, s) = done(m.c, s)

Base.length(m::MultiExponents) = length(m.c)

"""
Expand Down
56 changes: 34 additions & 22 deletions src/partitions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ end

Base.length(p::IntegerPartitions) = npartitions(p.n)

Base.start(p::IntegerPartitions) = Int[]
Base.done(p::IntegerPartitions, xs) = length(xs) == p.n
Base.next(p::IntegerPartitions, xs) = (xs = nextpartition(p.n,xs); (xs,xs))
function Base.iterate(p::IntegerPartitions, xs = Int[])
length(xs) == p.n && return
xs = nextpartition(p.n,xs)
(xs, xs)
end

"""
partitions(n)
Expand Down Expand Up @@ -105,13 +107,15 @@ partitions(n::Integer, m::Integer) =
FixedPartitions(n, m) :
throw(DomainError((n, m), "n and m must be positive"))

Base.start(f::FixedPartitions) = Int[]
function Base.done(f::FixedPartitions, s::Vector{Int})
f.m <= f.n || return true
isempty(s) && return false
return f.m == 1 || s[1]-1 <= s[end]
function Base.iterate(f::FixedPartitions, s::Vector{Int} = Int[])
f.m <= f.n || return
if !isempty(s)
(f.m == 1 || s[1]-1 <= s[end]) && return
end

xs = nextfixedpartition(f.n,f.m,s)
(xs, xs)
end
Base.next(f::FixedPartitions, s::Vector{Int}) = (xs = nextfixedpartition(f.n,f.m,s); (xs,xs))

function nextfixedpartition(n, m, bs)
as = copy(bs)
Expand Down Expand Up @@ -177,9 +181,14 @@ number of partitions to generate can be efficiently computed using
"""
partitions(s::AbstractVector) = SetPartitions(s)

Base.start(p::SetPartitions) = (n = length(p.s); (zeros(Int32, n), ones(Int32, n-1), n, 1))
Base.done(p::SetPartitions, s) = s[1][1] > 0
Base.next(p::SetPartitions, s) = nextsetpartition(p.s, s...)
function Base.iterate(p::SetPartitions)
n = length(p.s)
iterate(p, (zeros(Int32, n), ones(Int32, n-1), n, 1))
end
function Base.iterate(p::SetPartitions, s)
s[1][1] > 0 && return
nextsetpartition(p.s, s...)
end

function nextsetpartition(s::AbstractVector, a, b, n, m)
function makeparts(s, a, m)
Expand Down Expand Up @@ -255,18 +264,22 @@ partitions(s::AbstractVector, m::Int) =
FixedSetPartitions(s, m) :
throw(DomainError((length(s), m), "length(s) and m must be positive"))

function Base.start(p::FixedSetPartitions)
function Base.iterate(p::FixedSetPartitions)
n = length(p.s)
m = p.m
m <= n ? (vcat(ones(Int, n-m),1:m), vcat(1,n-m+2:n), n) : (Int[], Int[], n)
state = m <= n ? (vcat(ones(Int, n-m),1:m), vcat(1,n-m+2:n), n) : (Int[], Int[], n)
# state consists of:
# vector a of length n describing to which partition every element of s belongs
# vector b of length n describing the first index b[i] that belongs to partition i
# integer n

iterate(p, state)
end
# state consists of:
# vector a of length n describing to which partition every element of s belongs
# vector b of length n describing the first index b[i] that belongs to partition i
# integer n

Base.done(p::FixedSetPartitions, s) = isempty(s[1]) || s[1][1] > 1
Base.next(p::FixedSetPartitions, s) = nextfixedsetpartition(p.s,p.m, s...)
function Base.iterate(p::FixedSetPartitions, s)
(isempty(s[1]) || s[1][1] > 1) && return
nextfixedsetpartition(p.s,p.m, s...)
end

function nextfixedsetpartition(s::AbstractVector, m, a, b, n)
function makeparts(s, a)
Expand Down Expand Up @@ -310,7 +323,7 @@ function nextfixedsetpartition(s::AbstractVector, m, a, b, n)
end
b[k] -= 1
b[k+1:m] = n-m+k+1:n
a[1:n] = 1
a[1:n] .= 1
a[b] = 1:m
end
end
Expand Down Expand Up @@ -496,4 +509,3 @@ function _ncpart!(a::Int, b::Int, nn::Int, x::Vector, partitions::Vector)
end
end
end

23 changes: 11 additions & 12 deletions src/permutations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ function permutations(a, t::Integer)
Permutations(a, t)
end

Base.start(p::Permutations) = collect(1:length(p.a))
Base.next(p::Permutations, s) = nextpermutation(p.a, p.t ,s)
function Base.iterate(p::Permutations, s = collect(1:length(p.a)))
(!isempty(s) && max(s[1], p.t) > length(p.a) || (isempty(s) && p.t > 0)) && return
nextpermutation(p.a, p.t ,s)
end

function nextpermutation(m, t, state)
perm = [m[state[i]] for i in 1:t]
Expand Down Expand Up @@ -73,8 +75,6 @@ function nextpermutation(m, t, state)
return (perm, s)
end

Base.done(p::Permutations, s) = !isempty(s) && max(s[1], p.t) > length(p.a) || (isempty(s) && p.t > 0)

struct MultiSetPermutations{T}
m::T
f::Vector{Int}
Expand Down Expand Up @@ -134,11 +134,10 @@ function multiset_permutations(m, f::Vector{<:Integer}, t::Integer)
MultiSetPermutations(m, f, t, ref)
end

Base.start(p::MultiSetPermutations) = p.ref
Base.next(p::MultiSetPermutations, s) = nextpermutation(p.m, p.t, s)
Base.done(p::MultiSetPermutations, s) =
!isempty(s) && max(s[1], p.t) > length(p.ref) || (isempty(s) && p.t > 0)

function Base.iterate(p::MultiSetPermutations, s = p.ref)
(!isempty(s) && max(s[1], p.t) > length(p.ref) || (isempty(s) && p.t > 0)) && return
nextpermutation(p.m, p.t, s)
end


"""
Expand Down Expand Up @@ -195,9 +194,9 @@ end

# Parity of permutations

const levicivita_lut = cat(3, [0 0 0; 0 0 1; 0 -1 0],
[0 0 -1; 0 0 0; 1 0 0],
[0 1 0; -1 0 0; 0 0 0])
const levicivita_lut = cat([0 0 0; 0 0 1; 0 -1 0],
[0 0 -1; 0 0 0; 1 0 0],
[0 1 0; -1 0 0; 0 0 0]; dims=3)

"""
levicivita(p)
Expand Down
8 changes: 4 additions & 4 deletions src/youngdiagrams.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ function isrimhook(ξ::SkewDiagram)
l = maximum.x)
youngdiagram = zeros(Int, m, l)
for i = 1:n
youngdiagram[i, μ[i]+1:λ[i]] = 1
youngdiagram[i, μ[i]+1:λ[i]] .= 1
end
for i = n+1:m
youngdiagram[i, 1:λ[i]] = 1
youngdiagram[i, 1:λ[i]] .= 1
end
#Condition 1. Must be edgewise connected
youngdiagramList = Any[]
Expand Down Expand Up @@ -114,10 +114,10 @@ function leglength(ξ::SkewDiagram)
l = maximum.x)
youngdiagram = zeros(Int, m, l)
for i = 1:n
youngdiagram[i, μ[i]+1:λ[i]] = 1
youngdiagram[i, μ[i]+1:λ[i]] .= 1
end
for i = n+1:m
youngdiagram[i, 1:λ[i]] = 1
youngdiagram[i, 1:λ[i]] .= 1
end
for i = m:-1:1
any(==(1), youngdiagram[i,:]) && return i-1
Expand Down
6 changes: 1 addition & 5 deletions test/combinations.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using Combinatorics
using Compat
using Compat.Test

@test [combinations([])...] == []
@test [combinations(['a', 'b', 'c'])...] == Any[['a'],['b'],['c'],['a','b'],['a','c'],['b','c'],['a','b','c']]

Expand Down Expand Up @@ -38,7 +34,7 @@ using Compat.Test
@test_throws DomainError [CoolLexCombinations(-1, 1)...]
@test_throws DomainError [CoolLexCombinations(5, 0)...]
@test [CoolLexCombinations(4,2)...] == Vector[[1,2], [2,3], [1,3], [2,4], [3,4], [1,4]]
@test isa(start(CoolLexCombinations(1000, 20)), Combinatorics.CoolLexIterState{BigInt})
@test isa(iterate(CoolLexCombinations(1000, 20))[2], Combinatorics.CoolLexIterState{BigInt})

# Power set
@test collect(powerset([])) == Any[[]]
Expand Down
4 changes: 0 additions & 4 deletions test/factorials.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using Combinatorics
using Compat
using Compat.Test

@test factorial(7,3) == 7*6*5*4
@test_throws DomainError factorial(3,7)
@test_throws DomainError factorial(-3,-7)
Expand Down
5 changes: 1 addition & 4 deletions test/multinomials.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using Combinatorics
using Compat
using Compat.Test
using Compat.LinearAlgebra
using LinearAlgebra

# degenerate cases (x₁ + x₂ + ⋯ + xₘ)¹
@test [multiexponents(1,1)...] == [[1]]
Expand Down
4 changes: 0 additions & 4 deletions test/numbers.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using Combinatorics
using Compat
using Compat.Test

# catalan
@test catalannum(5) == 42
@test catalannum(30) == parse(BigInt,"3814986502092304")
Expand Down
4 changes: 0 additions & 4 deletions test/partitions.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using Combinatorics
using Compat
using Compat.Test

@test collect(partitions(4)) == Any[[4], [3,1], [2,2], [2,1,1], [1,1,1,1]]
@test collect(partitions(8,3)) == Any[[6,1,1], [5,2,1], [4,3,1], [4,2,2], [3,3,2]]
@test collect(partitions(8, 1)) == Any[[8]]
Expand Down
Loading

0 comments on commit 0e0edc6

Please sign in to comment.