From 15dc3d829b6b0cc5afb6adb253dc4cb81632df5f Mon Sep 17 00:00:00 2001 From: Tianyi Pu <912396513@qq.com> Date: Tue, 27 Feb 2024 22:39:39 +0000 Subject: [PATCH] first commit --- Project.toml | 4 +- docs/.gitignore | 2 + docs/Project.toml | 2 + docs/make.jl | 15 +++++ docs/src/docstring.md | 4 ++ docs/src/index.md | 30 +++++++++ src/EltypeExtensions.jl | 131 +++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 5 ++ 8 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/Project.toml create mode 100644 docs/make.jl create mode 100644 docs/src/docstring.md create mode 100644 docs/src/index.md diff --git a/Project.toml b/Project.toml index 20aaf13..6139bd1 100644 --- a/Project.toml +++ b/Project.toml @@ -4,10 +4,12 @@ authors = ["Tianyi Pu <912396513@qq.com> 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"] diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..a303fff --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +build/ +site/ diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 0000000..dfa65cd --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,2 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" diff --git a/docs/make.jl b/docs/make.jl new file mode 100644 index 0000000..904d188 --- /dev/null +++ b/docs/make.jl @@ -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 = "" +)=# diff --git a/docs/src/docstring.md b/docs/src/docstring.md new file mode 100644 index 0000000..2e27675 --- /dev/null +++ b/docs/src/docstring.md @@ -0,0 +1,4 @@ +# Docstrings +```@autodocs +Modules = [EltypeExtensions] +``` \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 0000000..fb8491e --- /dev/null +++ b/docs/src/index.md @@ -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`. \ No newline at end of file diff --git a/src/EltypeExtensions.jl b/src/EltypeExtensions.jl index 0892c8f..6347cfc 100644 --- a/src/EltypeExtensions.jl +++ b/src/EltypeExtensions.jl @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index 3032caf..97a2b96 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 \ No newline at end of file