From 414333c009d00b2eb3fca17cbc06a5552657e23f Mon Sep 17 00:00:00 2001 From: Johannes Schmitt Date: Wed, 14 Feb 2024 14:21:38 +0100 Subject: [PATCH] Make `partitions` and `(semi)standard_tableaux` return iterators ... the quick and dirty way to have a stable API --- .../EnumerativeCombinatorics/partitions.md | 2 +- experimental/LieAlgebras/src/Combinatorics.jl | 2 +- .../EnumerativeCombinatorics/partitions.jl | 84 +++++++++++-------- .../EnumerativeCombinatorics/tableaux.jl | 37 ++++---- .../EnumerativeCombinatorics/partitions.jl | 48 +++++------ .../EnumerativeCombinatorics/tableaux.jl | 16 ++-- 6 files changed, 99 insertions(+), 90 deletions(-) diff --git a/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md b/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md index 29be7f4e7e23..70a8b9d8404d 100644 --- a/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md +++ b/docs/src/Combinatorics/EnumerativeCombinatorics/partitions.md @@ -75,7 +75,7 @@ one can also handle more general types of restrictions. For example, there are precisely six ways for the second question in the exercise quoted above: ```jldoctest -julia> partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]) +julia> collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) 6-element Vector{Partition{Int64}}: [50, 50] [50, 20, 20, 10] diff --git a/experimental/LieAlgebras/src/Combinatorics.jl b/experimental/LieAlgebras/src/Combinatorics.jl index 6706f32adb54..ff40eba35bae 100644 --- a/experimental/LieAlgebras/src/Combinatorics.jl +++ b/experimental/LieAlgebras/src/Combinatorics.jl @@ -33,7 +33,7 @@ function multicombinations(n::Integer, k::Integer) return sort( map( reverse ∘ Vector, - reduce(vcat, partitions(i, k, 1, n) for i in 0:(k * n); init=Vector{Int}[]), + reduce(vcat, collect(partitions(i, k, 1, n)) for i in 0:(k * n); init=Vector{Int}[]), ), ) end diff --git a/src/Combinatorics/EnumerativeCombinatorics/partitions.jl b/src/Combinatorics/EnumerativeCombinatorics/partitions.jl index efe54ad568c3..7c0acfd5c238 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/partitions.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/partitions.jl @@ -151,8 +151,8 @@ end @doc raw""" partitions(n::IntegerUnion) -Return a list of all partitions of a non-negative integer `n`, produced in -lexicographically *descending* order. +Return an iterator over all partitions of a non-negative integer `n`, produced +in lexicographically *descending* order. Using a smaller integer type for `n` (e.g. `Int8`) may increase performance. The algorithm used is "Algorithm ZS1" by [ZS98](@cite). This algorithm is also @@ -160,7 +160,12 @@ discussed in [Knu11](@cite), Algorithm P (page 392). # Examples ```jldoctest -julia> partitions(4) +julia> p = partitions(4); + +julia> first(p) +[4] + +julia> collect(p) 5-element Vector{Partition{Int64}}: [4] [3, 1] @@ -168,7 +173,7 @@ julia> partitions(4) [2, 1, 1] [1, 1, 1, 1] -julia> partitions(Int8(4)) # using less memory +julia> collect(partitions(Int8(4))) # using less memory 5-element Vector{Partition{Int8}}: Int8[4] Int8[3, 1] @@ -184,9 +189,9 @@ function partitions(n::T) where T <: IntegerUnion # Some trivial cases if n == 0 - return Partition{T}[ partition(T[], check = false) ] + return (p for p in Partition{T}[ partition(T[], check = false) ]) elseif n == 1 - return Partition{T}[ partition(T[1], check = false) ] + return (p for p in Partition{T}[ partition(T[1], check = false) ]) end # Now, the algorithm starts @@ -222,7 +227,7 @@ function partitions(n::T) where T <: IntegerUnion end push!(P, partition(d[1:k], check = false)) end - return P + return (p for p in P) end ################################################################################ @@ -287,9 +292,10 @@ end partitions(m::IntegerUnion, n::IntegerUnion; only_distinct_parts::Bool = false) partitions(m::IntegerUnion, n::IntegerUnion, l1::IntegerUnion, l2::IntegerUnion; only_distinct_parts::Bool = false) -Return all partitions of a non-negative integer `m` into `n >= 0` parts. -Optionally, a lower bound `l1 >= 0` and an upper bound `l2` for the parts can be -supplied. In this case, the partitions are produced in *decreasing* order. +Return an iterator over all partitions of a non-negative integer `m` into +`n >= 0` parts. Optionally, a lower bound `l1 >= 0` and an upper bound `l2` for +the parts can be supplied. In this case, the partitions are produced in +*decreasing* order. There are two choices for the parameter `only_distinct_parts`: * `false`: no further restriction (*default*); @@ -300,7 +306,7 @@ The implemented algorithm is "parta" in [RJ76](@cite). # Examples All partitions of 7 into 3 parts: ```jldoctest -julia> partitions(7, 3) +julia> collect(partitions(7, 3)) 4-element Vector{Partition{Int64}}: [5, 1, 1] [4, 2, 1] @@ -309,7 +315,7 @@ julia> partitions(7, 3) ``` All partitions of 7 into 3 parts where all parts are between 1 and 4: ```jldoctest -julia> partitions(7, 3, 1, 4) +julia> collect(partitions(7, 3, 1, 4)) 3-element Vector{Partition{Int64}}: [4, 2, 1] [3, 3, 1] @@ -317,7 +323,7 @@ julia> partitions(7, 3, 1, 4) ``` Same as above but requiring all parts to be distinct: ```jldoctest -julia> partitions(7, 3, 1, 4; only_distinct_parts = true) +julia> collect(partitions(7, 3, 1, 4; only_distinct_parts = true)) 1-element Vector{Partition{Int64}}: [4, 2, 1] ``` @@ -339,15 +345,15 @@ function partitions(m::T, n::IntegerUnion, l1::IntegerUnion, l2::IntegerUnion; o # Some trivial cases if m == 0 && n == 0 - return Partition{T}[ partition(T[], check = false) ] + return (p for p in Partition{T}[ partition(T[], check = false) ]) end if n == 0 || n > m - return Partition{T}[] + return (p for p in Partition{T}[]) end if l2 < l1 - return Partition{T}[] + return (p for p in Partition{T}[]) end # If l1 == 0 the algorithm parta will actually create lists containing the @@ -411,7 +417,7 @@ function partitions(m::T, n::IntegerUnion, l1::IntegerUnion, l2::IntegerUnion; o end end - return P + return (p for p in P) end function partitions(m::T, n::IntegerUnion; only_distinct_parts::Bool = false) where T <: IntegerUnion @@ -420,14 +426,14 @@ function partitions(m::T, n::IntegerUnion; only_distinct_parts::Bool = false) wh # Special cases if m == n - return [ partition(T[ 1 for i in 1:m], check = false) ] + return (p for p in [ partition(T[ 1 for i in 1:m], check = false) ]) elseif m < n || n == 0 - return Partition{T}[] + return (p for p in Partition{T}[]) elseif n == 1 - return [ partition(T[m], check = false) ] + return (p for p in [ partition(T[m], check = false) ]) end - return partitions(m, n, 1, m; only_distinct_parts = only_distinct_parts) + return (p for p in partitions(m, n, 1, m; only_distinct_parts = only_distinct_parts)) end function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, S <: IntegerUnion} @@ -451,15 +457,17 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T # Special cases if n == 0 + # TODO: I don't understand this distinction here + # (it also makes the function tabe instable) if m == 0 - return [ Partition{T}[] ] + return (p for p in [ Partition{T}[] ]) else - return Partition{T}[] + return (p for p in Partition{T}[]) end end if isempty(mu) - return Partition{T}[] + return (p for p in Partition{T}[]) end #This will be the list of all partitions found. @@ -523,7 +531,7 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T # This is a necessary condition for existence of a partition if m < 0 || m > n * (lr - ll) - return P #goto b3 + return (p for p in P) #goto b3 end # The following is a condition for when only a single partition @@ -534,7 +542,7 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T # Noticed on Mar 23, 2023. if m == 0 && x[1] != 0 push!(P, partition(copy(x), check = false)) - return P + return (p for p in P) end # Now, the actual algorithm starts @@ -625,7 +633,7 @@ function partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{S}) where {T end #if gotob2 end #while - return P + return (p for p in P) end function partitions(m::T, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, S <: IntegerUnion} @@ -645,11 +653,12 @@ function partitions(m::T, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, res = Partition{T}[] if isempty(v) - return res + return (p for p in res) end if m == 0 - return [ Partition{T}[] ] + # TODO: I don't understand this return (and it is type instable) + return (p for p in [ Partition{T}[] ]) end # We will loop over the number of parts. @@ -680,7 +689,7 @@ function partitions(m::T, v::Vector{T}, mu::Vector{S}) where {T <: IntegerUnion, append!(res, partitions(m, n, v, mu)) end - return res + return (p for p in res) end @doc raw""" @@ -688,8 +697,8 @@ end partitions(m::T, v::Vector{T}, mu::Vector{<:IntegerUnion}) where T <: IntegerUnion partitions(m::T, n::IntegerUnion, v::Vector{T}, mu::Vector{<:IntegerUnion}) where T <: IntegerUnion -Return all partitions of a non-negative integer `m` where each part is an element -in the vector `v` of positive integers. +Return an iterator over all partitions of a non-negative integer `m` where each +part is an element in the vector `v` of positive integers. It is assumed that the entries in `v` are strictly increasing. If the optional vector `mu` is supplied, then each `v[i]` occurs a maximum of @@ -710,7 +719,7 @@ julia> length(partitions(100, [1, 2, 5, 10, 20, 50])) All partitions of 100 where the parts are from {1, 2, 5, 10, 20, 50} and each part is allowed to occur at most twice: ```jldoctest -julia> partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]) +julia> collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) 6-element Vector{Partition{Int64}}: [50, 50] [50, 20, 20, 10] @@ -722,7 +731,7 @@ julia> partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]) The partitions of 100 into seven parts, where the parts are required to be elements from {1, 2, 5, 10, 20, 50} and each part is allowed to occur at most twice. ```jldoctest -julia> partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]) +julia> collect(partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2])) 1-element Vector{Partition{Int64}}: [50, 20, 20, 5, 2, 2, 1] ``` @@ -735,11 +744,12 @@ function partitions(m::T, v::Vector{T}) where T <: IntegerUnion res = Partition{T}[] if isempty(v) - return res + return (p for p in res) end if m == 0 - return [ Partition{T}[] ] + # TODO: I don't understand this return (and it is type instable) + return (p for p in [ Partition{T}[] ]) end # We will loop over the number of parts. @@ -755,7 +765,7 @@ function partitions(m::T, v::Vector{T}) where T <: IntegerUnion append!(res, partitions(m, n, v, mu)) end - return res + return (p for p in res) end ################################################################################ diff --git a/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl b/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl index 74502cc37881..65af5b0e59e1 100644 --- a/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl +++ b/src/Combinatorics/EnumerativeCombinatorics/tableaux.jl @@ -342,8 +342,8 @@ end semistandard_tableaux(shape::Partition{T}, max_val::T = sum(shape)) where T <: Integer semistandard_tableaux(shape::Vector{T}, max_val::T = sum(shape)) where T <: Integer -Return all semistandard Young tableaux of given shape `shape` and filling elements -bounded by `max_val`. +Return an iterator over all semistandard Young tableaux of given shape `shape` +and filling elements bounded by `max_val`. By default, `max_val` is equal to the sum of the shape partition (the number of boxes in the Young diagram). @@ -355,10 +355,10 @@ function semistandard_tableaux(shape::Partition{T}, max_val::T = sum(shape)) whe SST = Vector{YoungTableau{T}}() len = length(shape) if max_val < len - return SST + return (t for t in SST) elseif len == 0 push!(SST, young_tableau(Vector{T}[], check = false)) - return SST + return (t for t in SST) end tab = [Array{T}(fill(i, shape[i])) for i = 1:len] m = len @@ -377,7 +377,7 @@ function semistandard_tableaux(shape::Partition{T}, max_val::T = sum(shape)) whe m -= 1 n = shape[m] else - return SST + return (t for t in SST) end end @@ -418,14 +418,14 @@ end @doc raw""" semistandard_tableaux(box_num::T, max_val::T = box_num) where T <: Integer -Return all semistandard Young tableaux consisting of `box_num` boxes and -filling elements bounded by `max_val`. +Return an iterator over all semistandard Young tableaux consisting of `box_num` +boxes and filling elements bounded by `max_val`. """ function semistandard_tableaux(box_num::T, max_val::T = box_num) where T <: Integer @req box_num >= 0 "box_num >= 0 required" SST = Vector{YoungTableau{T}}() if max_val <= 0 - return SST + return (t for t in SST) end shapes = partitions(box_num) @@ -435,16 +435,15 @@ function semistandard_tableaux(box_num::T, max_val::T = box_num) where T <: Inte end end - return SST + return (t for t in SST) end - @doc raw""" semistandard_tableaux(s::Partition{T}, weight::Vector{T}) where T <: Integer semistandard_tableaux(s::Vector{T}, weight::Vector{T}) where T <: Integer -Return all semistandard Young tableaux with shape `s` and given weight. This -requires that `sum(s) = sum(weight)`. +Return an iterator over all semistandard Young tableaux with shape `s` and given +weight. This requires that `sum(s) = sum(weight)`. """ function semistandard_tableaux(s::Vector{T}, weight::Vector{T}) where T <: Integer n_max = sum(s) @@ -453,7 +452,7 @@ function semistandard_tableaux(s::Vector{T}, weight::Vector{T}) where T <: Integ tabs = Vector{YoungTableau}() if isempty(s) push!(tabs, young_tableau(Vector{Int}[], check = false)) - return tabs + return (t for t in tabs) end ls = length(s) @@ -540,7 +539,7 @@ function semistandard_tableaux(s::Vector{T}, weight::Vector{T}) where T <: Integ end #rec_sst!() rec_sst!(1) - return tabs + return (t for t in tabs) end function semistandard_tableaux(s::Partition{T}, weight::Partition{T}) where T <: Integer @@ -620,13 +619,13 @@ end standard_tableaux(s::Partition) standard_tableaux(s::Vector{Integer}) -Return all standard Young tableaux of a given shape `s`. +Return an iterator over all standard Young tableaux of a given shape `s`. """ function standard_tableaux(s::Partition) tabs = Vector{YoungTableau}() if isempty(s) push!(tabs, young_tableau(Vector{Int}[], check = false)) - return tabs + return (t for t in tabs) end n_max = sum(s) ls = length(s) @@ -669,7 +668,7 @@ function standard_tableaux(s::Partition) end end - return tabs + return (t for t in tabs) end function standard_tableaux(s::Vector{T}) where T <: Integer @@ -679,7 +678,7 @@ end @doc raw""" standard_tableaux(n::Integer) -Return all standard Young tableaux with `n` boxes. +Return an iterator over all standard Young tableaux with `n` boxes. """ function standard_tableaux(n::Integer) @req n >= 0 "n >= 0 required" @@ -687,7 +686,7 @@ function standard_tableaux(n::Integer) for s in partitions(n) append!(ST, standard_tableaux(s)) end - return ST + return (t for t in ST) end ################################################################################ diff --git a/test/Combinatorics/EnumerativeCombinatorics/partitions.jl b/test/Combinatorics/EnumerativeCombinatorics/partitions.jl index 8ab5a370d086..a0424d398558 100644 --- a/test/Combinatorics/EnumerativeCombinatorics/partitions.jl +++ b/test/Combinatorics/EnumerativeCombinatorics/partitions.jl @@ -32,7 +32,7 @@ # partitions(n) ############################################################################ for n = 0:20 - P = partitions(n) + P = collect(partitions(n)) # Check that the number of partitions is correct # Note that number_of_partitions(n) is computed independently of partitions(n) @@ -76,10 +76,10 @@ ############################################################################ for n in 0:20 for k = 0:n+1 - P = partitions(n,k) + P = collect(partitions(n,k)) # Create the same by filtering all partitions - Q = partitions(n) + Q = collect(partitions(n)) filter!( Q->length(Q) == k, Q) # Check that P and Q coincide (up to reordering) @@ -98,10 +98,10 @@ for k = 0:n+1 for l1 = 0:n for l2 = l1:n - P = partitions(n,k,l1,l2) + P = collect(partitions(n,k,l1,l2)) # Create the same by filtering all partitions - Q = partitions(n,k) + Q = collect(partitions(n,k)) filter!( Q->all(>=(l1),Q), Q) filter!( Q->all(<=(l2),Q), Q) @@ -120,10 +120,10 @@ for k = 0:n+1 for l1 = 0:n for l2 = l1:n - P = partitions(n,k,l1,l2; only_distinct_parts=true) + P = collect(partitions(n, k, l1, l2; only_distinct_parts=true)) # Create the same by filtering all partitions - Q = partitions(n,k, l1, l2) + Q = collect(partitions(n, k, l1, l2)) filter!( Q->Q==unique(Q), Q ) # Check that P and Q coincide (up to reordering) @@ -147,40 +147,40 @@ @test_throws ArgumentError partitions(6,3,[0,2,1],[1,2,3]) #v > 0 # Issues from https://github.com/oscar-system/Oscar.jl/issues/2043 - @test length(partitions(17, 3, [1,4], [1,4])) == 0 - @test partitions(17, 5, [1,4], [1,4]) == [ partition(4,4,4,4,1) ] - @test length(partitions(17,6,[1,2], [1,7])) == 0 - @test length(partitions(20,5,[1,2,3],[1,3,6])) == 0 + @test length(collect(partitions(17, 3, [1, 4], [1,4]))) == 0 + @test collect(partitions(17, 5, [1, 4], [1, 4])) == [ partition(4, 4, 4, 4, 1) ] + @test length(collect(partitions(17, 6, [1, 2], [1, 7]))) == 0 + @test length(collect(partitions(20, 5, [1, 2, 3], [1, 3, 6]))) == 0 # Issues UT found - @test length(partitions(1,1,[1],[1])) == 1 - @test length(partitions(100, 7, [1,2,5,10,20,50], [2,2,2,2,2,2])) == 1 + @test length(collect(partitions(1, 1, [1], [1]))) == 1 + @test length(collect(partitions(100, 7, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]))) == 1 # Special cases - for n=0:20 - for k=0:n+1 - P = partitions(n,k, [i for i in 1:n], [n for i in 1:n]) - Q = partitions(n,k) + for n in 0:20 + for k in 0:n + 1 + P = collect(partitions(n, k, [i for i in 1:n], [n for i in 1:n])) + Q = collect(partitions(n, k)) @test length(P) == length(Q) @test Set(P) == Set(Q) - P = partitions(n,k, [i for i in 1:n], [1 for i in 1:n]) - Q = partitions(n,k,1,n;only_distinct_parts=true) + P = collect(partitions(n, k, [i for i in 1:n], [1 for i in 1:n])) + Q = collect(partitions(n, k, 1, n; only_distinct_parts=true)) @test length(P) == length(Q) @test Set(P) == Set(Q) end end # From https://www.maa.org/frank-morgans-math-chat-293-ways-to-make-change-for-a-dollar - @test length(partitions(100, [1,5,10,25,50])) == 292 - @test length(partitions(200, [1,5,10,25,50,100])) == 2728 + @test length(collect(partitions(100, [1, 5, 10, 25, 50]))) == 292 + @test length(collect(partitions(200, [1, 5, 10, 25, 50, 100]))) == 2728 # From Knu11, Exercise 11 on page 408 - @test length(partitions(100, [1,2,5,10,20,50], [2,2,2,2,2,2])) == 6 - @test length(partitions(100, [1,2,5,10,20,50])) == 4562 + @test length(collect(partitions(100, [1, 2, 5, 10, 20, 50], [2, 2, 2, 2, 2, 2]))) == 6 + @test length(collect(partitions(100, [1, 2, 5, 10, 20, 50]))) == 4562 # From https://oeis.org/A000008 - @test [ length(partitions(n, [1,2,5,10])) for n in 0:60 ] == + @test [ length(collect(partitions(n, [1,2,5,10]))) for n in 0:60 ] == [ 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 11, 12, 15, 16, 19, 22, 25, 28, 31, 34, 40, 43, 49, 52, 58, 64, 70, 76, 82, 88, 98, 104, 114, 120, 130, 140, 150, 160, 170, 180, 195, 205, 220, 230, 245, 260, 275, 290, 305, 320, diff --git a/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl b/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl index 4e8cacce3fd5..aa87c5fc9ba3 100644 --- a/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl +++ b/test/Combinatorics/EnumerativeCombinatorics/tableaux.jl @@ -32,7 +32,7 @@ # semistandard_tableaux(shape::Array{T,1}, max_val=sum(shape)::Integer) shapes = [[3,2,1],[3,3,1],[2,2,2]] for s in shapes - SST = semistandard_tableaux(s) + SST = collect(semistandard_tableaux(s)) #check that all tableaux are distinct @test SST == unique(SST) @@ -48,7 +48,7 @@ weights = [[1,1,1,1,1,1,1,1,1,1],[3,0,2,0,0,5],[4,3,2,1]] for s in shapes for w in weights - SST = semistandard_tableaux(s,w) + SST = collect(semistandard_tableaux(s, w)) #check that all tableaux are distinct @test SST == unique(SST) #check that all tableaux are semistandard_tableaux @@ -65,14 +65,14 @@ end end end - @test semistandard_tableaux(Int[], Int[]) == [young_tableau(Array{Int,1}[])] + @test collect(semistandard_tableaux(Int[], Int[])) == [young_tableau(Array{Int,1}[])] #semistandard_tableaux(box_num, max_val) BoxNum = 0:5 MaxVal = 1:6 for box_num in BoxNum for max_val in MaxVal - SST = semistandard_tableaux(box_num, max_val) + SST = collect(semistandard_tableaux(box_num, max_val)) #check that all tableaux are distinct @test SST == unique(SST) #check that all tableaux are semistandard_tableaux @@ -96,7 +96,7 @@ # standard_tableaux(s::Partition) for i = 1:10 for s in partitions(i) - ST = standard_tableaux(s) + ST = collect(standard_tableaux(s)) #check that all tableaux are distinct @test ST == unique(ST) #check that all tableaux are standard_tableaux @@ -107,12 +107,12 @@ @test length(ST) == number_of_standard_tableaux(s) end end - @test standard_tableaux(partition(Int[])) == [young_tableau(Array{Int,1}[])] - @test standard_tableaux([3,2,1]) == standard_tableaux(partition([3,2,1])) + @test collect(standard_tableaux(partition(Int[]))) == [young_tableau(Array{Int,1}[])] + @test collect(standard_tableaux([3, 2, 1])) == collect(standard_tableaux(partition([3, 2, 1]))) # standard_tableaux(n::Integer) for n = 0:10 - ST = standard_tableaux(n) + ST = collect(standard_tableaux(n)) #check that all tableaux are distinct @test ST == unique(ST) #check that all tableaux are standard_tableaux