-
Notifications
You must be signed in to change notification settings - Fork 1
/
utilities.jl
147 lines (114 loc) · 3.07 KB
/
utilities.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#####
##### Utilities
#####
export wrapping, picking
####
#### Container element type management
####
"""
$(SIGNATURES)
Test if a collection of element type `T` can contain a new element `elt` without *any* loss
of precision.
"""
@inline cancontain(T, elt::S) where {S} = S <: T || T ≡ promote_type(S, T)
@inline cancontain(::Type{Union{}}, _) = false
@inline cancontain(::Type{Union{}}, ::Integer) = false
@inline cancontain(T::Type{<:Integer}, elt::Integer) where {S <: Integer} =
typemin(T) ≤ elt ≤ typemax(T)
@inline cancontain(T::Type{<:AbstractFloat}, elt::Integer) =
(m = Integer(maxintfloat(T)); -m ≤ elt ≤ m)
"""
$(SIGNATURES)
Convert the argument to a narrower type if possible without losing precision.
!!! NOTE
This function is not type stable, use only when new container types are determined.
"""
@inline narrow(x) = x
@inline narrow(x::Bool) = x
@inline function narrow(x::Integer)
intype(T) = typemin(T) ≤ x ≤ typemax(T)
if intype(Int8)
Int8(x)
elseif intype(Int16)
Int16(x)
elseif intype(Int32)
Int32(x)
elseif intype(Int64)
Int64(x)
else
x
end
end
"""
$(SIGNATURES)
Append `elt` to `v`, allocating a new vector and copying the contents.
Type of new collection is calculated using `promote_type`.
"""
function append1(v::Vector{T}, elt::S) where {T,S}
U = promote_type(T, S)
w = Vector{U}(undef, length(v) + 1)
copyto!(w, v)
w[end] = elt
w
end
####
#### Miscellaneous
####
"""
$(SIGNATURES)
Splits a named tuple in two, based on the names in `splitter`.
Returns two `NamedTuple`s; the first one is ordered as `splitter`, the second one with the
remaining values as in the original argument.
```jldoctest
julia> split_namedtuple(NamedTuple{(:a, :c)}, (c = 1, b = 2, a = 3, d = 4))
((a = 3, c = 1), (b = 2, d = 4))
```
"""
@inline function split_namedtuple(::Type{<:NamedTuple{N}}, nt::NamedTuple) where N
S = NamedTuple{N}
S(nt), Base.structdiff(nt, S)
end
"""
$(SIGNATURES)
Whenever `defaults` has a given key, use the corresponding type in `rowtype`, otherwise
`Union{}`.
"""
Base.@pure function merge_default_types(rowtype::Type{<: NamedTuple{A}},
defaults::Type{<: NamedTuple{B}}) where {A, B}
M = Any[]
for a in A
push!(M, key_in(a, B) ? fieldtype(defaults, a) : Union{})
end
NamedTuple{A, Tuple{M...}}
end
"""
$(SIGNATURES)
Test if `b` starts with `a`.
"""
is_prefix(a, b) = length(a) ≤ length(b) && all(a == b for (a,b) in zip(a, b))
####
#### wrapping and picking
####
struct Wrapping{K}
function Wrapping{K}() where K
@argcheck K isa Symbol
new{K}()
end
end
(::Wrapping{K})(x) where K = NamedTuple{(K, )}((x, ))
"""
$(SIGNATURES)
Return a callable that wraps its argument in a `NamedTuple` with a given `key`.
"""
wrapping(key::Symbol) = Wrapping{key}()
struct Picking{K}
function Picking{K}() where K
@argcheck K isa Symbol
new{K}()
end
end
(::Picking{K})(x) where K = getproperty(x, K)
"""
$(SIGNATURES)
"""
picking(key::Symbol) = Picking{key}()