Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
putianyi889 committed Feb 27, 2024
1 parent 7013341 commit 15dc3d8
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 2 deletions.
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ authors = ["Tianyi Pu <[email protected]> and contributors"]
version = "1.0.0-DEV"

[compat]
Aqua = "0.8"
julia = "1"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
test = ["Aqua", "Test"]
2 changes: 2 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
site/
2 changes: 2 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
15 changes: 15 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Documenter
using EltypeExtensions

makedocs(
sitename = "EltypeExtensions",
format = Documenter.HTML(),
modules = [EltypeExtensions]
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
#=deploydocs(
repo = "<repository url>"
)=#
4 changes: 4 additions & 0 deletions docs/src/docstring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Docstrings
```@autodocs
Modules = [EltypeExtensions]
```
30 changes: 30 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# EltypeExtensions.jl

EltypeExtensions.jl is a mini toolbox for eltype-related conversions. The motivation of this package comes from manipulating (nested) arrays with different eltypes. However if you have any reasonable idea that works on other collections, feel free to write an issue/pull request.

We note that this package has some overlap with TypeUtils.jl and Unitless.jl.

## Guides

### `basetype` and `precisiontype`
The `basetype` is used for nested collections, where `eltype` is repeatedly applied until the bottom. `precisiontype` has a similar idea, but goes deeper when possible. `precisiontype` is used to manipulate the accuracy of (nested) collections.
```@setup 1
using EltypeExtensions
```
```@example 1
basetype(Set{Matrix{Vector{Matrix{Complex{Rational{Int}}}}}})
precisiontype(Set{Matrix{Vector{Matrix{Complex{Rational{Int}}}}}})
```

### Method naming convention
- `sometype(T)` gets the `sometype` of type `T`.
- `sometype(x) = sometype(typeof(x))` is also provided for convenience.
- `_to_sometype(T,S)` converts the type `S` to have the `sometype` of `T`.
- `someconvert(T,A)` converts `A` to have the `sometype` of `T`.

where `some` can be `el`, `base` and `precision`.

### On `precisionconvert`
Since some types (e.g. `BigFloat`) can have variable precision, `precisionconvert` accepts a third argument `prec` which specifies the precision. `prec` defaults to `precision(T)` and has no effect when `T` has a const precision.

When `T` is an integer, the conversion will dig into `Rational` as well. In contrast, since `Rational` as a whole is more "precise" than an integer, `precisiontype` doesn't unwrap `Rational`.
131 changes: 130 additions & 1 deletion src/EltypeExtensions.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,134 @@
module EltypeExtensions

# Write your package code here.
import Base: convert

export elconvert, basetype, baseconvert, precisiontype, precisionconvert

"""
elconvert(T, A)
Similar to `convert(T, A)`, but `T` refers to the eltype.
# Examples
```jldoctest; setup = :(using EltypeExtensions: elconvert)
julia> elconvert(Float64, 1:10)
1.0:1.0:10.0
julia> typeof(elconvert(Float64, rand(Int, 3, 3)))
$(repr("text/plain", Matrix{Float64}))
```
"""
elconvert(::Type{T}, A::AbstractArray) where T = AbstractArray{T}(A)
elconvert(::Type{T}, A::AbstractRange) where T = AbstractRange{T}(A)
elconvert(::Type{T}, A::AbstractSet) where T = AbstractSet{T}(A)

"""
_to_eltype(T, S)
Convert type `S` to have the `eltype` of `T`.
"""
_to_eltype(::Type{T}, ::Type{Array{S,N}}) where {T,S,N} = Array{T,N}
_to_eltype(::Type{T}, ::Type{Set}) where T = Set{T}
_to_eltype(::Type{T}, ::Type{S}) where {T,S} = Base.return_types(elconvert, (Type{T}, S))

nutype(x) = nutype(typeof(x))
nutype(T::Type) = throw(MethodError(nutype, T))

"""
basetype(T::Type)
Recursively apply `eltype` to `T` until convergence.
# Examples
```jldoctest; setup = :(using EltypeExtensions: basetype)
julia> basetype(Matrix{BitArray})
Bool
julia> basetype(Vector{Set{Complex{Float64}}})
$(repr("text/plain", Complex{Float64}))
julia> basetype([1:n for n in 1:10])
Int64
```
"""
basetype(x) = basetype(typeof(x))
basetype(::Type{T}) where T = eltype(T) == T ? T : basetype(eltype(T))

"""
_to_basetype(T::Type, S::Type)
Convert type `S` to have the [`basetype`](@ref) of `T`.
"""
_to_basetype(::Type{T}, ::Type{S}) where {T,S} = eltype(S) == S ? T : _to_eltype(_to_basetype(T, eltype(S)), S)

"""
baseconvert(T::Type, A)
Similar to `convert(T, A)`, but `T` refers to the [`basetype`](@ref).
"""
baseconvert(::Type{T}, A::S) where {T,S} = convert(_to_basetype(T,S), A)

"""
precisiontype(T::Type)
Returns the type that decides the precision of `T`. The difference from [`basetype`](@ref) is that `precisiontype` unwraps composite basetypes such as `Complex` and that `precisiontype` is not generalised.
# Examples
```jldoctest; setup = :(using EltypeExtensions: precisiontype)
julia> precisiontype(Complex{Float32})
Float32
julia> precisiontype(Matrix{ComplexF64})
Float64
```
"""
precisiontype(x) = precisiontype(typeof(x))
precisiontype(::Type{T}) where T<:Real = T
precisiontype(::Type{Complex{T}}) where T = T
precisiontype(::Type{T}) where T = eltype(T) == T ? throw(MethodError(precisiontype, T)) : precisiontype(basetype(T))

"""
_to_precisiontype(T::Type, S::Type)
Convert type `S` to have the [`precisiontype`](@ref) of `T`. An exception is that if `T<:Integer`, then `Rational` will also be unwrapped.
# Examples
```jldoctest; setup = :(using EltypeExtensions: _to_precisiontype)
julia> _to_precisiontype(Float64, Complex{Rational{Int}})
$(repr("text/plain", Complex{Float64}))
julia> _to_precisiontype(BigFloat, Matrix{Complex{Bool}})
$(repr("text/plain", Matrix{Complex{BigFloat}}))
julia> _to_precisiontype(Int, Complex{Rational{BigInt}})
Complex{Rational{Int64}}
```
"""
_to_precisiontype(::Type{T}, ::Type{Complex{S}}) where {T,S} = Complex{_to_precisiontype(T,S)}
_to_precisiontype(::Type{T}, ::Type{<:Rational}) where T<:Integer = Rational{T}
_to_precisiontype(::Type{T}, ::Type{S}) where {T,S} = eltype(S) == S ? T : _to_eltype(_to_precisiontype(T, eltype(S)), S)

"""
precisionconvert(T::Type, A, prec=precision(T))
Convert `A` to have the [`precisiontype`](@ref) of `T`. If `T` has adjustable precision such as `BigFloat`, the precision can be specified by `prec`, otherwise `prec` takes no effect.
# Examples
```jldoctest; setup = :(using EltypeExtensions: precisionconvert)
julia> precisionconvert(BigFloat, 1//3+im, 128)
0.3333333333333333333333333333333333333338 + 1.0im
julia> precisionconvert(Float16, [[m/n for n in 1:3] for m in 1:3])
3-element Vector{Vector{Float16}}:
[1.0, 0.5, 0.3333]
[2.0, 1.0, 0.6665]
[3.0, 1.5, 1.0]
```
"""
precisionconvert(T,A) = precisionconvert(T,A,precision(T))
precisionconvert(::Type{T}, A::S, prec) where {T,S} = convert(_to_precisiontype(T,S), A)
precisionconvert(::Type{BigFloat}, x::Real, prec) = BigFloat(x, prec)
precisionconvert(::Type{BigFloat}, x::Complex, prec) = Complex(BigFloat(real(x), prec), BigFloat(imag(x), prec))
precisionconvert(::Type{BigFloat}, A, prec) = precisionconvert.(BigFloat, A, prec)

end
5 changes: 5 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
using EltypeExtensions
using Test
using Aqua

@testset "EltypeExtensions.jl" begin
# Write your tests here.
end

@testset "Aqua" begin
Aqua.test_all(EltypeExtensions)
end

0 comments on commit 15dc3d8

Please sign in to comment.