|
1 | 1 | module TupleTools
|
2 | 2 |
|
3 |
| -# package code goes here |
| 3 | +using Base: tuple_type_head, tuple_type_tail, tuple_type_cons, tail, front, setindex |
| 4 | + |
| 5 | +# don't export anything, import whatever you need |
| 6 | + |
| 7 | +struct StaticLength{N} |
| 8 | +end |
| 9 | +Base.@pure StaticLength(N::Int) = StaticLength{N}() |
| 10 | +Base.@pure Base.:+(::StaticLength{N₁}, ::StaticLength{N₂}) where {N₁,N₂} = StaticLength(N₁+N₂) |
| 11 | +Base.@pure Base.:-(::StaticLength{N₁}, ::StaticLength{N₂}) where {N₁,N₂} = StaticLength(max(0,N₁-N₂)) |
| 12 | + |
| 13 | +if VERSION < v"0.7.0-DEV.843" |
| 14 | + @inline Base.ntuple(f, ::StaticLength{N}) where {N} = ntuple(f, Val{N}) |
| 15 | +else |
| 16 | + @inline Base.ntuple(f, ::StaticLength{N}) where {N} = ntuple(f, Val{N}()) |
| 17 | +end |
| 18 | + |
| 19 | +if VERSION >= v"0.7-" # to fix type instability in (f)indmin / (f)indmax |
| 20 | + Base.Pair(p::Tuple{Any,Any}) = p[1] => p[2] |
| 21 | + Base.pairs(collection) = Base.Generator(=>, zip(keys(collection), values(collection))) |
| 22 | +end |
| 23 | + |
| 24 | +@inline argtail2(a, b, c...) = c |
| 25 | +""" |
| 26 | + tail2(t::Tuple) -> ::Tuple |
| 27 | +
|
| 28 | +Returns a tuple with the first two elements stripped, equivalent to `tail(tail(t))` |
| 29 | +""" |
| 30 | +@inline tail2(t::Tuple{Any,Any,Vararg{Any}}) = argtail2(t...) |
| 31 | + |
| 32 | +""" |
| 33 | + unsafe_tail(t::Tuple) -> ::Tuple |
| 34 | +
|
| 35 | +Returns a tuple with the first element stripped, similar to `tail(t)`, but does |
| 36 | +not error on an empty tuple (instead returning an empty tuple again). An empty tuple |
| 37 | +is thus the fixed point of this function. |
| 38 | +""" |
| 39 | +@inline unsafe_tail(t::Tuple{}) = t |
| 40 | +@inline unsafe_tail(t::Tuple) = tail(t) |
| 41 | + |
| 42 | +""" |
| 43 | + unsafe_front(t::Tuple) -> ::Tuple |
| 44 | +
|
| 45 | +Returns a tuple with the last element stripped, similar to `front(t)`, but does |
| 46 | +not error on an empty tuple (instead returning an empty tuple again). An empty tuple |
| 47 | +is thus the fixed point of this function. |
| 48 | +""" |
| 49 | +@inline unsafe_front(t::Tuple{}) = t |
| 50 | +@inline unsafe_front(t::Tuple) = front(t) |
| 51 | + |
| 52 | +""" |
| 53 | + vcat(args...) -> ::Tuple |
| 54 | +
|
| 55 | +Like `vcat` for tuples, concatenates a combination of tuple arguments and non-tuple |
| 56 | +arguments into a single tuple. Only works one level deep, i.e. tuples in tuples are |
| 57 | +not expanded. |
| 58 | +""" |
| 59 | +@inline vcat(t::Tuple) = t |
| 60 | +@inline vcat() = () |
| 61 | +@inline vcat(t) = (t,) |
| 62 | +@inline vcat(a, args...) = (vcat(a)..., vcat(args...)...) |
| 63 | + |
| 64 | +""" |
| 65 | + deleteat(t::Tuple, i::Int) -> ::Tuple |
| 66 | + deleteat(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple |
| 67 | +
|
| 68 | +Delete the element at location `i` in `t`; if a list `I` of indices is specified |
| 69 | +(again as a tuple), the elements of these different positions are deleted. |
| 70 | +""" |
| 71 | +@inline deleteat(t::Tuple, I::Tuple{Int}) = deleteat(t, I[1]) |
| 72 | +@inline deleteat(t::Tuple, I::Tuple{Int, Int, Vararg{Int}}) = deleteat(deleteat(t, I[1]), map(i->(i<I[1] ? i : i-1), tail(I))) |
| 73 | +@inline deleteat(t::Tuple, i::Int) = 1 <= i <= length(t) ? _deleteat(t, i) : throw(BoundsError(t, i)) |
| 74 | +@inline _deleteat(t::Tuple, i::Int) = i == 1 ? tail(t) : (t[1], _deleteat(tail(t), i-1)...) |
| 75 | +@inline _deleteat(t::Tuple{}, i::Int) = throw(BoundsError(t, i)) |
| 76 | + |
| 77 | +""" |
| 78 | + insertat(t::Tuple, i::Int, t2::Tuple) -> ::Tuple |
| 79 | +
|
| 80 | +
|
| 81 | +Insert the elements of tuple t2 at location `i` in `t`, i.e. the output tuple will |
| 82 | +look as (t[1:i-1]..., t2..., t[i+1:end]). Note that element `t[i]` is deleted. See |
| 83 | +`splice` if you would also like to return `t[i]` |
| 84 | +""" |
| 85 | +@inline insertat(t::Tuple, i::Int, t2::Tuple) = 1 <= i <= length(t) ? _insertat(t, i, t2) : throw(BoundsError(t, i)) |
| 86 | +@inline _insertat(t::Tuple, i::Int, t2::Tuple) = i == 1 ? (t2..., tail(t)...) : (t[1], _insertat(tail(t), i-1, t2)...) |
| 87 | +@inline _insertat(t::Tuple{}, i::Int, t2::Tuple) = throw(BoundsError(t, i)) |
| 88 | + |
| 89 | +@inline function sortperm(t::Tuple) |
| 90 | + i = indmin(t) |
| 91 | + r = map(n->(n < i ? n : n+1), sortperm(deleteat(t, i))) |
| 92 | + return (i, r...) |
| 93 | +end |
| 94 | +@inline sortperm(t::Tuple{Any}) = (1,) |
| 95 | + |
| 96 | +""" |
| 97 | + getindices(t::Tuple, I::Tuple{Vararg{Int}}) -> ::Tuple |
| 98 | +
|
| 99 | +Get the indices `t[i] for i in I`, again as tuple. |
| 100 | +""" |
| 101 | +@inline getindices(t::Tuple, ind::Tuple{Vararg{Int}}) = (t[ind[1]], getindices(t, tail(ind))...) |
| 102 | +@inline getindices(t::Tuple, ind::Tuple{}) = () |
| 103 | + |
| 104 | +""" |
| 105 | + permute(t::Tuple, p) -> ::Tuple |
| 106 | +
|
| 107 | +Permute the elements of tuple `t` according to the permutation in `p`. |
| 108 | +""" |
| 109 | +permute(t::NTuple{N}, p::NTuple{N,Int}) where {N} = isperm(p) ? getindices(t, p) : throw(ArgumentError("not a valid permutation: $p")) |
| 110 | +permute(t::NTuple{N}, p) where {N} = isperm(p) && length(p) == N ? ntuple(n->t[p[n]], StaticLength(N)) : throw(ArgumentError("not a valid permutation: $p")) |
| 111 | + |
| 112 | +invperm(p::Tuple{Vararg{Int}}) = sortperm(p) |
4 | 113 |
|
5 | 114 | end # module
|
0 commit comments