|
| 1 | +""" |
| 2 | +The `extrap_prep` function is used by `getindex_impl` to generate the body of |
| 3 | +the `getindex` function for extrapolation objects. |
| 4 | +
|
| 5 | +The methods of `extrap_prep` work in "layers", iteratively working out the exact |
| 6 | +expression needed. |
| 7 | +
|
| 8 | +The first layer takes a specification of the extrapolation scheme(s) to be used |
| 9 | +and a `Val` object that specifies the dimensionality of the extrapolation object: |
| 10 | +`extrap_prep{T,N}(::Type{T}, Val{N})`. These methods only dispatch to the second |
| 11 | +layer, and need not be extended for new schemes. |
| 12 | +
|
| 13 | +The second layer also takes a `Val` object that specifies a single dimension on |
| 14 | +which to work: `extrap_prep{T,N,d}(::Type{T}, ::Val{N}, ::Val{d}). The methods |
| 15 | +with this signature in src/extrapolation/extrap_prep.jl simply expand into a |
| 16 | +block with sub-expressions for handling too-low and too-high values separately |
| 17 | +(the third layer), but specific interpolation schemes can provide more specific |
| 18 | +methods for this layer that handle both ends simultaneously. For example, the |
| 19 | +`Flat` scheme has a layer-2 method that uses `clamp` to restrict the coordinate |
| 20 | +when used in both directions, but uses `min` and `max` when handling each end |
| 21 | +separately. |
| 22 | +
|
| 23 | +The third layer, to which the second dispatches if no scheme-specific method is |
| 24 | +found, adds a final `Val` object with a symbol `:lo` or `:hi`: |
| 25 | +`extrap_prep{T,N,d,l}(::Type{T}, ::Val{N}, ::Val{d}, ::Val{l})`. These methods |
| 26 | +must be specified for each extrapolation scheme. However, the general framework |
| 27 | +takes care of expanding all possible tuple combinations, so individual schemes |
| 28 | +need only care about e.g. `T==Flat`. |
| 29 | +
|
| 30 | +In addition to these methods, there is a similar three-layer method hierarchy |
| 31 | +for gradient evaluation, in which a `Val{:gradient}` is prepended to the other |
| 32 | +arguments: |
| 33 | +`extrap_prep{T,N,d,l}(::Val{:gradient}`, ::Type{T}, ::Val{N}, ::Val{d}, ::Val{l})` |
| 34 | +If nothing else is specified for the individual schemes, these methods forward |
| 35 | +to the same methods without the `:gradient` argument, i.e. the same behavior as |
| 36 | +for value extrapolation. This works well with all schemes that are simple |
| 37 | +coordinate transformations, but for anything else methods for the low- and high- |
| 38 | +value cases need to be implemented for each scheme. |
| 39 | +""" extrap_prep |
| 40 | + |
| 41 | +extrap_prep{T}(::Type{T}, n::Val{1}) = extrap_prep(T, n, Val{1}()) |
| 42 | +extrap_prep{T}(::Type{Tuple{T}}, n::Val{1}) = extrap_prep(T, n) |
| 43 | +extrap_prep{T}(::Type{Tuple{T,T}}, n::Val{1}) = extrap_prep(T, n) |
| 44 | +extrap_prep{T}(::Type{Tuple{Tuple{T,T}}}, n::Val{1}) = extrap_prep(T, n) |
| 45 | +function extrap_prep{S,T}(::Type{Tuple{S,T}}, n::Val{1}) |
| 46 | + quote |
| 47 | + $(extrap_prep(S, n, Val{1}(), Val{:lo}())) |
| 48 | + $(extrap_prep(T, n, Val{1}(), Val{:hi}())) |
| 49 | + end |
| 50 | +end |
| 51 | +extrap_prep{S,T}(::Type{Tuple{Tuple{S,T}}}, n::Val{1}) = extrap_prep(Tuple{S,T}, n) |
| 52 | + |
| 53 | +# needed for ambiguity resolution |
| 54 | +extrap_prep{T<:Tuple}(::Type{T}, ::Val{1}) = :(throw(ArgumentError("The 1-dimensional extrap configuration $T is not supported"))) |
| 55 | + |
| 56 | +function extrap_prep{T,N}(::Type{T}, n::Val{N}) |
| 57 | + exprs = Expr[] |
| 58 | + for d in 1:N |
| 59 | + push!(exprs, extrap_prep(T, n, Val{d}())) |
| 60 | + end |
| 61 | + return Expr(:block, exprs...) |
| 62 | +end |
| 63 | +function extrap_prep{N,T<:Tuple}(::Type{T}, n::Val{N}) |
| 64 | + length(T.parameters) == N || return :(throw(ArgumentError("The $N-dimensional extrap configuration $T is not supported - must be a tuple of length $N (was length $(lenght(T.parameters)))"))) |
| 65 | + exprs = Expr[] |
| 66 | + for d in 1:N |
| 67 | + Tdim = T.parameters[d] |
| 68 | + if Tdim <: Tuple |
| 69 | + length(Tdim.parameters) == 2 || return :(throw(ArgumentError("The extrap configuration $Tdim for dimension $d is not supported - must be a tuple of length 2 or a simple configuration type"))) |
| 70 | + if Tdim.parameters[1] != Tdim.parameters[2] |
| 71 | + push!(exprs, extrap_prep(Tdim, n, Val{d}())) |
| 72 | + else |
| 73 | + push!(exprs, extrap_prep(Tdim.parameters[1], n, Val{d}())) |
| 74 | + end |
| 75 | + else |
| 76 | + push!(exprs, extrap_prep(Tdim, n, Val{d}())) |
| 77 | + end |
| 78 | + end |
| 79 | + return Expr(:block, exprs...) |
| 80 | +end |
| 81 | +extrap_prep{T,N,d}(::Type{T}, n::Val{N}, dim::Val{d}) = extrap_prep(Tuple{T,T}, n, dim) |
| 82 | +function extrap_prep{S,T,N,d}(::Type{Tuple{S,T}}, n::Val{N}, dim::Val{d}) |
| 83 | + quote |
| 84 | + $(extrap_prep(S, n, dim, Val{:lo}())) |
| 85 | + $(extrap_prep(T, n, dim, Val{:hi}())) |
| 86 | + end |
| 87 | +end |
0 commit comments