├── REQUIRE ├── test ├── REQUIRE ├── speed_bench.jl ├── speed_bench_precompile.jl └── runtests.jl ├── .gitignore ├── .travis.yml ├── src ├── array_of_fixedsize.jl ├── conversion.jl ├── functors.jl ├── FixedSizeArrays.jl ├── destructure.jl ├── mapreduce.jl ├── core.jl ├── indexing.jl ├── constructors.jl ├── expm.jl └── ops.jl ├── appveyor.yml ├── LICENSE.md ├── bench ├── matrix.jl └── construction.jl └── README.md /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.4 2 | -------------------------------------------------------------------------------- /test/REQUIRE: -------------------------------------------------------------------------------- 1 | FactCheck -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.jl.cov 2 | *.jl.*.cov 3 | *.jl.mem 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: julia 2 | os: 3 | - linux 4 | - osx 5 | julia: 6 | - 0.4 7 | notifications: 8 | email: false 9 | script: 10 | - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi 11 | - julia --inline=no -e 'Pkg.clone(pwd()); Pkg.test("FixedSizeArrays"; coverage=true)' 12 | after_success: 13 | - julia -e 'cd(Pkg.dir("FixedSizeArrays")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())' 14 | -------------------------------------------------------------------------------- /src/array_of_fixedsize.jl: -------------------------------------------------------------------------------- 1 | function show{FSA <: FixedVector}(io::IO, a::Vector{FSA}) 2 | println(io, length(a), "-element", typeof(a), ":") 3 | for elem in a 4 | print(io, " ") 5 | showcompact(io, elem) 6 | println(io) 7 | end 8 | end 9 | immutable MaxFun <: Func{2} end 10 | immutable MinFun <: Func{2} end 11 | call(::MaxFun, a, b) = max(a, b) 12 | call(::MinFun, a, b) = min(a, b) 13 | minimum{T <: FixedArray}(a::Vector{T}) = reduce(MinFun(), a) 14 | maximum{T <: FixedArray}(a::Vector{T}) = reduce(MaxFun(), a) 15 | function isapprox{FSA <: FixedArray, A <: Union{Array, FixedArray}}(a::FSA, b::A) 16 | for i=1:length(a) 17 | !isapprox(a[i], b[i]) && return false 18 | end 19 | true 20 | end 21 | 22 | (.+){T<:FixedArray, ND}(a::Array{T, ND}, x::T) = T[elem .+ x for elem in a] 23 | (+){T<:FixedArray, ND}(a::Array{T, ND}, x::T) = a .+ x 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe" 4 | - JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe" 5 | 6 | branches: 7 | only: 8 | - master 9 | 10 | install: 11 | # Download most recent Julia Windows binary 12 | - ps: (new-object net.webclient).DownloadFile( 13 | $("http://s3.amazonaws.com/"+$env:JULIAVERSION), 14 | "C:\projects\julia-binary.exe") 15 | # Run installer silently, output to C:\projects\julia 16 | - C:\projects\julia-binary.exe /S /D=C:\projects\julia 17 | 18 | build_script: 19 | # Need to convert from shallow to complete for Pkg.clone to work 20 | # - git fetch --unshallow 21 | - C:\projects\julia\bin\julia-debug -e "versioninfo(); Pkg.init(); Pkg.clone(pwd(), \"FixedSizeArrays\")" 22 | 23 | test_script: 24 | - C:\projects\julia\bin\julia-debug -e "Pkg.test(\"FixedSizeArrays\")" -------------------------------------------------------------------------------- /src/conversion.jl: -------------------------------------------------------------------------------- 1 | function convert{DA <: Array, FSA <: FixedArray}(::Type{DA}, b::FSA) 2 | result = Array(eltype(b), size(b)...) 3 | @inbounds for i=1:length(b) 4 | result[i] = b[i] 5 | end 6 | result 7 | end 8 | 9 | 10 | convert{FA<:FixedArray}(a::Type{FA}, b::FA) = b 11 | convert{FA<:Mat}(a::Type{FA}, b::FA) = b 12 | 13 | function convert{R, C, R2, C2, T}(MT::Type{Mat{R, C, T}}, b::Mat{R2, C2, T}) 14 | MT(ntuple(c -> ntuple(r -> b[r,c], Val{R}), Val{C})) 15 | end 16 | 17 | #conversion 18 | convert{T <: Tuple}(::Type{T}, x::Real) = ntuple(ConstFunctor(eltype(T)(x)), Val{length(T.parameters),}) 19 | convert{T <: Tuple, FSA <: FixedArray}(::Type{T}, x::FSA) = convert(T, get_tuple(x)) 20 | 21 | function convert{FSA1 <: FixedArray}(t::Type{FSA1}, f::FixedArray) 22 | map(ConversionIndexFunctor(f, eltype_or(FSA1, eltype(typeof(f)))), FSA1) 23 | end 24 | 25 | convert{T <: FixedArray}(::Type{T}, x) = T(x) 26 | convert{T <: FixedArray}(::Type{T}, x...) = T(x) 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The FixedSizeArrays.jl package is licensed under the MIT "Expat" License: 2 | 3 | > Copyright (c) 2014: Simon Danisch. 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /bench/matrix.jl: -------------------------------------------------------------------------------- 1 | function parallelmap{S,T}(f, A::AbstractArray{S}, B::AbstractArray{T}) 2 | F = similar(A, promote_type(S,T), promote_shape(size(A),size(B))) 3 | @parallel for i in eachindex(A,B) 4 | @inbounds F[i] = f(A[i], B[i]) 5 | end 6 | return F 7 | end 8 | 9 | function simplemap{S,T}(f, A::AbstractArray{S}, B::AbstractArray{T}) 10 | F = similar(A, promote_type(S,T), promote_shape(size(A),size(B))) 11 | for i in eachindex(A,B) 12 | @inbounds F[i] = f(A[i], B[i]) 13 | end 14 | return F 15 | end 16 | function test() 17 | const a = rand(10^7) 18 | const b = rand(10^7) 19 | 20 | 21 | c = parallelmap(+, a, b) 22 | println("parallel:") 23 | @time parallelmap(+, a, b) 24 | d = parallelmap(Base.AddFun(), a, b) 25 | println("functor:") 26 | @time parallelmap(Base.AddFun(), a, b) 27 | 28 | 29 | e = simplemap(+, a, b) 30 | 31 | println("simplemap:") 32 | @time simplemap(+, a, b) 33 | f = simplemap(Base.AddFun(), a, b) 34 | println("functor:") 35 | @time simplemap(Base.AddFun(), a, b) 36 | 37 | g = map(+, a, b) 38 | println("map:") 39 | @time map(+, a, b) 40 | h = map(Base.AddFun(), a, b) 41 | println("functor:") 42 | @time map(Base.AddFun(), a, b) 43 | 44 | println("plus") 45 | i = a+b 46 | @time a+b 47 | 48 | @assert c==d && c==e && c==f && c==g && c==h && c==i 49 | end 50 | test() 51 | 52 | -------------------------------------------------------------------------------- /test/speed_bench.jl: -------------------------------------------------------------------------------- 1 | using GeometryTypes 2 | import ImmutableArrays 3 | using Benchmarks 4 | 5 | macro mybench(expr) 6 | quote 7 | val = $(esc(expr)) 8 | stats = Statistics(@benchmark for i=1:10 9 | val = $(esc(expr)) 10 | end) 11 | println(stats.average_time, ": ", stats.interval[1], " - ", stats.interval[2]) 12 | val 13 | end 14 | end 15 | function test() 16 | const a = Float64[1 1 1 1; 2 2 2 2; 3 3 3 3; 4 4 4 4] 17 | const b = Float64[1,2,3,4] 18 | const a2 = Mat{4,4,Float64}(( 19 | (1,2,3,4), 20 | (1,2,3,4), 21 | (1,2,3,4), 22 | (1,2,3,4) 23 | )) 24 | const b2 = Vec{4, Float64}(1,2,3,4) 25 | const a3 = ImmutableArrays.Matrix4x4{Float64}( 26 | ImmutableArrays.Vector4{Float64}(1,2,3,4), 27 | ImmutableArrays.Vector4{Float64}(1,2,3,4), 28 | ImmutableArrays.Vector4{Float64}(1,2,3,4), 29 | ImmutableArrays.Vector4{Float64}(1,2,3,4) 30 | ) 31 | const b3 = ImmutableArrays.Vector4{Float64}(1,2,3,4) 32 | 33 | 34 | println("matmul 4x4 * 4: ") 35 | @mybench a*b 36 | @mybench a2*b2 37 | @mybench a3*b3 38 | 39 | println("matmul 4x4 * 4x4: ") 40 | @mybench a*a 41 | @mybench a2*a2 42 | @mybench a3*a3 43 | 44 | println("sum: ") 45 | @mybench sum(a) 46 | @mybench sum(a2) 47 | @mybench sum(a3) 48 | 49 | println("dot:") 50 | @mybench for i=1:100 dot(b,b) end 51 | @mybench for i=1:100 dot(b2,b2) end 52 | @mybench for i=1:100 dot(b3,b3) end 53 | 54 | println("column:") 55 | @mybench for i=1:1000 column(a2,1) end 56 | @mybench for i=1:1000 ImmutableArrays.column(a3,1) end 57 | 58 | println("row:") 59 | @mybench for i=1:1000 row(a2,1) end 60 | @mybench for i=1:1000 ImmutableArrays.row(a3,1) end 61 | end 62 | test() -------------------------------------------------------------------------------- /src/functors.jl: -------------------------------------------------------------------------------- 1 | immutable MersenneFunctor{T} <: Func{1} 2 | mt::MersenneTwister 3 | end 4 | @inline call{T}(rf::MersenneFunctor{T}, i...) = rand(rf.mt, T) 5 | 6 | immutable RandFunctor{T} <: Func{1} 7 | valuerange::T 8 | end 9 | @inline call(rf::RandFunctor, i...) = rand(rf.valuerange) 10 | immutable RandnFunctor{T} <: Func{1} 11 | mt::MersenneTwister 12 | end 13 | @inline call(rf::RandnFunctor{Float64}, i...) = randn(rf.mt) 14 | @inline call(rf::RandnFunctor{Complex{Float64}}, i...) = randn(rf.mt) + im*randn(rf.mt) 15 | 16 | immutable ConstFunctor{T} <: Base.Func{1} 17 | args::T 18 | end 19 | call(f::ConstFunctor, i...) = f.args 20 | 21 | immutable EyeFunc{T} <: Func{1} end 22 | @inline call{T}(ef::Type{EyeFunc{T}}, i::Int, j::Int) = (i==j ? one(T) : zero(T)) 23 | 24 | immutable UnitFunctor <: Func{1} 25 | i::Int 26 | eltype::DataType 27 | end 28 | call(ef::UnitFunctor, i) = ef.i==i ? one(ef.eltype) : zero(ef.eltype) 29 | 30 | immutable ConversionIndexFunctor{T, T2} <: Func{1} 31 | args1::T 32 | target::Type{T2} 33 | end 34 | call(f::ConversionIndexFunctor, i...) = f.target(f.args1[i...]) 35 | 36 | immutable IndexFunctor{T} <: Func{1} 37 | args1::T 38 | end 39 | call(f::IndexFunctor, i) = f.args1[i] 40 | 41 | immutable IndexFunc{T} <: Base.Func{1} 42 | arg::T 43 | i::Int 44 | end 45 | Base.call{T}(a::IndexFunc{T}, j) = a.arg[a.i,j] 46 | 47 | 48 | immutable CRowFunctor{M} 49 | mat::M 50 | end 51 | call(r::CRowFunctor, i::Int) = crow(r.mat, i) 52 | 53 | immutable SetindexFunctor{T <: FixedArray, V, N} <: Func{1} 54 | target::T 55 | value::V 56 | index::NTuple{N, Int} 57 | end 58 | 59 | function call(sf::SetindexFunctor, i::Int...) 60 | sf.index == i && return eltype(sf.target)(sf.value) 61 | sf.target[i...] 62 | end 63 | 64 | immutable RowFunctor{M} 65 | mat::M 66 | end 67 | call(r::RowFunctor, i::Int) = row(r.mat, i) 68 | -------------------------------------------------------------------------------- /src/FixedSizeArrays.jl: -------------------------------------------------------------------------------- 1 | VERSION >= v"0.4.0-dev+6521" && __precompile__(true) 2 | module FixedSizeArrays 3 | 4 | importall Base 5 | import Base.Func, Base.LinAlg.chol! 6 | 7 | include("core.jl") 8 | include("functors.jl") 9 | include("constructors.jl") 10 | 11 | if VERSION <= v"0.5.0" 12 | supertype(x) = super(x) 13 | end 14 | 15 | # put them here due to #JuliaLang/julia#12814 16 | # needs to be befor indexing and ops, but after constructors 17 | immutable Mat{Row, Column, T} <: FixedMatrix{Row, Column, T} 18 | _::NTuple{Column, NTuple{Row, T}} 19 | end 20 | function similar{FSA <: Mat, T}(::Type{FSA}, ::Type{T}, SZ::NTuple{2, Int}) 21 | Mat{SZ[1], SZ[2], T} 22 | end 23 | 24 | # most common FSA types 25 | immutable Vec{N, T} <: FixedVector{N, T} 26 | _::NTuple{N, T} 27 | end 28 | immutable Point{N, T} <: FixedVector{N, T} 29 | _::NTuple{N, T} 30 | end 31 | 32 | include("mapreduce.jl") 33 | include("destructure.jl") 34 | include("indexing.jl") 35 | include("ops.jl") 36 | include("expm.jl") 37 | include("array_of_fixedsize.jl") 38 | include("conversion.jl") 39 | 40 | 41 | function show{R,C,T}(io::IO, m::Mat{R,C,T}) 42 | println(io, typeof(m), "(") 43 | for i=1:R 44 | println(io, " ", join(row(m, i), " ")) 45 | end 46 | println(io, ")") 47 | end 48 | 49 | showcompact(io::IO, v::FixedVector{0}) = print(io, typeof(v).name.name, "()") 50 | function showcompact{N}(io::IO, v::FixedVector{N}) 51 | print(io, typeof(v).name.name, "(", v[1]) 52 | for i = 2:N 53 | print(io, ",", v[i]) 54 | end 55 | print(io, ")") 56 | end 57 | 58 | export FixedArray 59 | export FixedVector 60 | export FixedMatrix 61 | export MutableFixedArray 62 | export MutableFixedVector 63 | export MutableFixedMatrix 64 | export Mat, Vec, Point 65 | export @fsa 66 | 67 | export unit 68 | export normalize 69 | export row 70 | export column 71 | export MatMulFunctor 72 | export setindex 73 | export eltype_or, size_or, ndims_or 74 | export @fslice 75 | export destructure 76 | 77 | end 78 | -------------------------------------------------------------------------------- /src/destructure.jl: -------------------------------------------------------------------------------- 1 | import Base.linearindexing 2 | import Base.similar 3 | 4 | "Destructuring view around an arbitrary abstract array. See destructure()" 5 | immutable DestructuredArray{T,N,ArrayT} <: AbstractArray{T,N} 6 | base::ArrayT 7 | end 8 | 9 | function DestructuredArray{ArrayT<:AbstractArray}(A::ArrayT) 10 | DestructuredArray{eltype(eltype(A)), ndims(A)+ndims(eltype(A)), ArrayT}(A) 11 | end 12 | 13 | linearindexing(::Type{DestructuredArray}) = Base.LinearSlow() 14 | size{T,N,ArrayT}(A::DestructuredArray{T,N,ArrayT}) = (size(eltype(ArrayT))..., size(A.base)...) 15 | 16 | # TODO: Should define similar() but it doesn't work for sparse matrices in 0.4 17 | # since they don't support more than two dimensions. 18 | # similar{S,D}(A::DestructuredArray, ::Type{S}, dims::NTuple{D,Int}) = similar(A.base, S, dims...) 19 | 20 | @generated function getindex{T,N,ArrayT}(A::DestructuredArray{T,N,ArrayT}, inds::Int...) 21 | eldims = ndims(eltype(ArrayT)) 22 | # We'd like to just do 23 | # A.base[inds[eldims+1:end]...][inds[1:eldims]...] 24 | # but splatting is currently (2015-12) a performance disaster 25 | fixinds = [:(inds[$i]) for i in 1:eldims] 26 | baseinds = [:(inds[$i]) for i in eldims+1:length(inds)] 27 | quote 28 | A.base[$(baseinds...)][$(fixinds...)] 29 | end 30 | end 31 | 32 | @generated function setindex!{T,N,ArrayT}(A::DestructuredArray{T,N,ArrayT}, value, inds::Int...) 33 | eldims = ndims(eltype(ArrayT)) 34 | fixinds = [:(inds[$i]) for i in 1:eldims] 35 | baseinds = [:(inds[$i]) for i in eldims+1:length(inds)] 36 | quote 37 | # TODO: FixedSizeArrays.setindex gives a rather severe performance 38 | # penalty here. 39 | A.base[$(baseinds...)] = setindex(A.base[$(baseinds...)], value, $(fixinds...)) 40 | end 41 | end 42 | 43 | 44 | """ 45 | Destructure the elements of an array `A` to create `M=ndims(eltype(A))` 46 | additional dimensions prepended to the dimensions of `A`. The returned array 47 | is a view onto the original elements; additional dimensions occur first 48 | for consistency with the natural memory ordering. 49 | 50 | For example, `AbstractArray{F<:FixedArray{T,M,SIZE},N}` appears as an 51 | `AbstractArray{T,M+N}` after destructuring. 52 | """ 53 | destructure(A::AbstractArray) = DestructuredArray(A) 54 | 55 | # Faster reinterpret-based version (as of 2015-12) for plain Array 56 | destructure{T,N}(A::Array{T,N}) = reinterpret(eltype(T), A, (size(T)..., size(A)...)) 57 | 58 | -------------------------------------------------------------------------------- /src/mapreduce.jl: -------------------------------------------------------------------------------- 1 | @inline function reduce{FSA <: Union{FixedArray, Tuple}}(f, a::FSA) 2 | length(a) == 1 && return a[1] 3 | @inbounds begin 4 | red = f(a[1], a[2]) 5 | for i=3:length(a) 6 | red = f(red, a[i]) 7 | end 8 | end 9 | red 10 | end 11 | 12 | @inline function reduce(f::Base.Func{2}, a::Mat) 13 | length(a) == 1 && return a[1,1] 14 | @inbounds begin 15 | red = reduce(f, a.(1)[1]) 16 | for i=2:size(a, 2) 17 | red = f(red, reduce(f, a.(1)[i])) 18 | end 19 | end 20 | red 21 | end 22 | 23 | index_expr{T <: Number}(::Type{T}, i::Int, inds::Int...) = :($(symbol("arg$i"))) 24 | index_expr{T <: FixedArray}(::Type{T}, i::Int, inds::Int...) = :($(symbol("arg$i"))[$(inds...)]) 25 | inner_expr{N}(args::NTuple{N, DataType}, inds::Int...) = :( F($(ntuple(i -> index_expr(args[i], i, inds...), N)...)) ) 26 | 27 | 28 | # This solves the combinational explosion from FixedVectorNoTuple while staying fast. 29 | function constructor_expr{T <: FixedVector}(::Type{T}, tuple_expr::Expr) 30 | quote 31 | $(Expr(:boundscheck, false)) 32 | $(Expr(:meta, :inline)) 33 | rvalue = FSA($(tuple_expr)) 34 | $(Expr(:boundscheck,:pop)) 35 | rvalue 36 | end 37 | end 38 | constructor_expr{T <: Mat}(::Type{T}, tuple_expr::Expr) = quote 39 | $(Expr(:boundscheck, false)) 40 | $(Expr(:meta, :inline)) 41 | rvalue = Mat($(tuple_expr)) 42 | $(Expr(:boundscheck,:pop)) 43 | rvalue 44 | end 45 | constructor_expr{T <: FixedVectorNoTuple}(::Type{T}, tuple_expr::Expr) = quote 46 | $(Expr(:boundscheck, false)) 47 | $(Expr(:meta, :inline)) 48 | rvalue = FSA($(tuple_expr)...) 49 | $(Expr(:boundscheck,:pop)) 50 | rvalue 51 | end 52 | @generated function map{FSA <: FixedArray}(F, arg1::FSA, arg2::FSA) 53 | inner = fill_tuples_expr((inds...) -> inner_expr((arg1, arg2), inds...), size(FSA)) 54 | constructor_expr(FSA, inner) 55 | end 56 | @generated function map{FSA <: FixedArray}(F, arg1::FSA, arg2::Number) 57 | inner = fill_tuples_expr((inds...) -> inner_expr((arg1, arg2), inds...), size(FSA)) 58 | constructor_expr(FSA, inner) 59 | end 60 | @generated function map{FSA <: FixedArray}(F, arg1::Number, arg2::FSA) 61 | inner = fill_tuples_expr((inds...) -> inner_expr((arg1, arg2), inds...), size(FSA)) 62 | constructor_expr(FSA, inner) 63 | end 64 | @generated function map{FSA <: FixedArray}(F, ::Type{FSA}) 65 | inner = fill_tuples_expr((inds...) -> :(F($(inds...))), size(FSA)) 66 | :( FSA($inner) ) 67 | end 68 | 69 | @generated function map{FSA <: FixedArray}(F, arg1::FSA) 70 | inner = fill_tuples_expr((inds...) -> :( F(arg1[$(inds...)]) ), size(FSA)) 71 | constructor_expr(FSA, inner) 72 | end 73 | 74 | 75 | @generated function map{T}(::Type{T}, arg1::FixedArray) 76 | eltype(arg1) == T && return :(arg1) 77 | FSA = similar(arg1, T) 78 | inner = fill_tuples_expr((inds...) -> :( T(arg1[$(inds...)]) ), size(FSA)) 79 | :( $FSA($(inner)) ) 80 | end 81 | 82 | @inline map{R,C,T}(F::Type{T}, arg1::Mat{R,C,T}) = arg1 83 | @generated function map{R,C,T}(F::DataType, arg1::Mat{R,C,T}) 84 | inner = fill_tuples_expr((inds...) -> :( F(arg1[$(inds...)]) ), (R, C)) 85 | :( Mat{R, C, F}($(inner)) ) 86 | end 87 | 88 | @generated function map{FSA <: FixedVectorNoTuple}(F::DataType, arg1::FSA) 89 | eltype(FSA) == F && return :(arg1) 90 | inner = ntuple(i-> :(F(arg1[$i])), length(FSA)) 91 | :( similar(FSA, F)($(inner...)) ) 92 | end 93 | -------------------------------------------------------------------------------- /bench/construction.jl: -------------------------------------------------------------------------------- 1 | using FixedSizeArrays, ImmutableArrays 2 | 3 | immutable BenchData <: FixedVector{Float64, 5} 4 | gc_bytes::Float64 5 | time_ns::Float64 6 | gc_time_ns::Float64 7 | gc_num_pause::Float64 8 | gc_num_full_sweep::Float64 9 | end 10 | 11 | function Base.show(io::IO, b::BenchData) 12 | print(io, """ 13 | bytes allocated : $(b.gc_bytes) 14 | time_ns : $(b.time_ns) 15 | gc_time_ns : $(b.gc_time_ns) 16 | gc_num_pause : $(b.gc_num_pause) 17 | gc_num_full_sweep : $(b.gc_num_full_sweep) 18 | """) 19 | end 20 | 21 | macro timing(expr) 22 | tms = gensym() 23 | bts = gensym() 24 | gctms = gensym() 25 | gcps = gensym() 26 | gcswps = gensym() 27 | 28 | tms1 = gensym() 29 | gcswps1 = gensym() 30 | gcps1 = gensym() 31 | gctms1 = gensym() 32 | bts1 = gensym() 33 | esc(quote 34 | $bts = Base.gc_bytes() 35 | $gctms = Base.gc_time_ns() 36 | $gcps = Base.gc_num_pause() 37 | $gcswps = Base.gc_num_full_sweep() 38 | $tms = Base.time_ns() 39 | 40 | $expr 41 | 42 | $tms1 = Base.time_ns() 43 | $gcswps1 = Base.gc_num_full_sweep() 44 | $gcps1 = Base.gc_num_pause() 45 | $gctms1 = Base.gc_time_ns() 46 | $bts1 = Base.gc_bytes() 47 | 48 | BenchData($bts1-$bts, $tms1-$tms, $gctms1-$gctms, $gcps1-$gcps, $gcswps1-$gcswps) 49 | end) 50 | end 51 | type Vec4{T} <: FixedVector{T, 4} 52 | x::T 53 | y::T 54 | z::T 55 | w::T 56 | end 57 | 58 | 59 | function test(N) 60 | bench = Dict( 61 | (:julia, :vector_creation) => BenchData[], 62 | (:julia, :matrix_creation) => BenchData[], 63 | (:julia, :vector_creation_rand) => BenchData[], 64 | (:julia, :matrix_creation_rand) => BenchData[], 65 | (:fsa, :vector_creation) => BenchData[], 66 | (:fsa, :matrix_creation) => BenchData[], 67 | 68 | (:fsa, :vector_creation_fromj) => BenchData[], 69 | (:fsa, :matrix_creation_fromj) => BenchData[], 70 | (:fsawrapper, :vector_creation) => BenchData[], 71 | (:immutablearrays, :vector_creation) => BenchData[], 72 | ) 73 | for i=1:N 74 | push!(bench[(:julia, :vector_creation)], (@timing vecdata = [1,2,3,4])) 75 | push!(bench[(:julia, :matrix_creation)], (@timing matdata = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4])) 76 | 77 | push!(bench[(:julia, :vector_creation_rand)], (@timing vecrand = rand(4))) 78 | push!(bench[(:julia, :matrix_creation_rand)], (@timing matrand = rand(4,4))) 79 | nvec(1,2,3,4) 80 | nvec(1,2,3,4) 81 | 82 | push!(bench[(:fsa, :vector_creation)], (@timing vecrand = nvec(1,2,3,4))) 83 | push!(bench[(:fsa, :matrix_creation)], (@timing matrand = nvec((4,4), 1,2,3,4, 1,2,3,4, 1,2,3,4, 1,2,3,4))) 84 | 85 | push!(bench[(:fsa, :vector_creation_fromj)], (@timing vecrand = nvec(vecrand))) 86 | push!(bench[(:fsa, :matrix_creation_fromj)], (@timing matrand = nvec(matrand))) 87 | 88 | push!(bench[(:fsawrapper, :vector_creation)], (@timing vecrand = Point(1,2,3,4))) 89 | 90 | push!(bench[(:immutablearrays, :vector_creation)], (@timing vecrand = Vector4(1,2,3,4))) 91 | end 92 | bench 93 | end 94 | result = test(1000) 95 | 96 | println(mean(result[(:fsa, :vector_creation)][100:end])) 97 | println(mean(result[(:fsawrapper, :vector_creation)][100:end])) 98 | println(mean(result[(:immutablearrays, :vector_creation)][100:end])) 99 | 100 | #= 101 | function test2(N) 102 | a,b,c,d = rand(Float64, 4) 103 | result = LolVec(a,b,c,d) 104 | bench = 0.0 105 | for i=1:N 106 | a,b,c,d = rand(Float64, 4) 107 | tic() 108 | result = LolVec(a,b,c,d) 109 | b = toq() 110 | bench += b 111 | end 112 | 113 | result,bench 114 | end 115 | 116 | @time t= test2(10) 117 | @time t,b= test2(10^4) 118 | println(b) 119 | #0.00622337 seconds (1 MB allocated) 120 | #0.001912617 seconds (470 kB allocated) 121 | 122 | #0.0036441109999999725 2mb 123 | #0.0010228019999999729 1mb 124 | #0.0012369930000000022 1mb 125 | =# -------------------------------------------------------------------------------- /src/core.jl: -------------------------------------------------------------------------------- 1 | abstract FixedArray{T, NDim, SIZE} 2 | abstract MutableFixedArray{T, NDim, SIZE} <: FixedArray{T, NDim, SIZE} 3 | 4 | typealias MutableFixedVector{T, CARDINALITY} MutableFixedArray{T, 1, Tuple{CARDINALITY}} 5 | typealias MutableFixedMatrix{T, M, N} MutableFixedArray{T, 2, Tuple{M,N}} 6 | 7 | typealias FixedVector{CARDINALITY, T} FixedArray{T, 1, Tuple{CARDINALITY,}} 8 | typealias FixedMatrix{Row, Column, T} FixedArray{T, 2, Tuple{Row, Column}} 9 | 10 | abstract FixedVectorNoTuple{CARDINALITY, T} <: FixedVector{CARDINALITY, T} 11 | export FixedVectorNoTuple 12 | 13 | 14 | _size{T <: Tuple}(::Type{T}) = (T.parameters...) 15 | _size{N, N2}(::Type{Tuple{N, N2}}) = (N,N2) 16 | _size{N}(::Type{Tuple{N}}) = (N,) 17 | 18 | eltype{T <: FixedArray}(A::Type{T}) = eltype_or(T, Any) 19 | eltype{T <: FixedArray,N,SZ}(A::FixedArray{T,N,SZ}) = T 20 | 21 | 22 | function length{T <: FixedArray}(A::Type{T}) 23 | prod(size(T)) 24 | end 25 | length{T <: FixedArray}(A::T) = length(T) 26 | 27 | endof{T <: FixedArray}(A::Type{T}) = length(T) 28 | endof{T <: FixedArray}(A::T) = endof(T) 29 | 30 | 31 | @generated function ndims{T <: FixedArray}(A::Type{T}) 32 | :($(fsa_abstract(T).parameters[2])) 33 | end 34 | ndims{T <: FixedArray}(A::T) = ndims(T) 35 | 36 | 37 | size{T,N,SZ}(A::Type{FixedArray{T,N,SZ}}) = _size(SZ) 38 | size{T <: FixedArray}(A::Type{T}) = size_or(T, ()) 39 | size{T <: FixedArray}(A::T) = size(T) 40 | 41 | size{T <: FixedArray}(A::Type{T}, d::Integer) = size(T)[d] 42 | size{T <: FixedArray}(A::T, d::Integer) = size(T, d) 43 | 44 | @generated function fsa_abstract{FSA <: FixedArray}(::Type{FSA}) 45 | ff = FSA 46 | while ff.name.name != :FixedArray 47 | ff = supertype(ff) 48 | end 49 | :($ff) 50 | end 51 | @generated function size_or{FSA <: FixedArray}(::Type{FSA}, OR) 52 | fsatype = fsa_abstract(FSA) 53 | sz = fsatype.parameters[3] 54 | isa(sz, TypeVar) && return :(OR) 55 | any(x->isa(x, TypeVar), sz.parameters) && return :(OR) 56 | :($(_size(sz))) 57 | end 58 | @generated function eltype_or{FSA <: FixedArray}(::Type{FSA}, OR) 59 | fsatype = fsa_abstract(FSA) 60 | T = fsatype.parameters[1] 61 | isa(T, TypeVar) && return :(OR) 62 | :($T) 63 | end 64 | @generated function ndims_or{FSA <: FixedArray}(::Type{FSA}, OR) 65 | fsatype = fsa_abstract(FSA) 66 | N = fsatype.parameters[2] 67 | isa(N, TypeVar) && return :(OR) 68 | :($N) 69 | end 70 | 71 | # Iterator 72 | start(A::FixedArray) = 1 73 | function next(A::FixedArray, state::Integer) 74 | @inbounds x = A[state] 75 | (x, state+1) 76 | end 77 | done(A::FixedArray, state::Integer) = length(A) < state 78 | 79 | 80 | similar{FSA <: FixedVector, T}(::Type{FSA}, ::Type{T}, n::Tuple) = similar(FSA, T, n...) 81 | @generated function similar{FSA <: FixedVector, T}(::Type{FSA}, ::Type{T}, n::Int) 82 | name = parse(string("Main.", FSA.name)) 83 | :($name{n, T, $(FSA.parameters[3:end]...)}) 84 | end 85 | @generated function similar{FSA <: FixedVector, T}(::Type{FSA}, ::Type{T}) 86 | name = parse(string("Main.", FSA.name)) 87 | :($name{$(FSA.parameters[1]), T, $(FSA.parameters[3:end]...)}) 88 | end 89 | @generated function similar{FSA <: FixedVectorNoTuple, T}(::Type{FSA}, ::Type{T}) 90 | name = parse(string("Main.", FSA.name)) 91 | :($name{T, $(FSA.parameters[3:end]...)}) 92 | end 93 | @generated function similar{FSA <: FixedVectorNoTuple, T}(::Type{FSA}, ::Type{T}, n::Int) 94 | name = parse(string("Main.", FSA.name)) 95 | :($name{T, $(FSA.parameters[3:end]...)}) 96 | end 97 | 98 | @generated function get_tuple{N, T}(f::FixedVectorNoTuple{N, T}) 99 | :(tuple($(ntuple(i->:(f[$i]), N)...))) 100 | end 101 | function get_tuple(f::FixedArray) 102 | f.(1) # a little wonky, but there really no much sense if isn't the only field 103 | end 104 | -------------------------------------------------------------------------------- /src/indexing.jl: -------------------------------------------------------------------------------- 1 | @inline getindex{T <: FixedVector}(x::T, i::Union{Range, Integer}) = x.(1)[i] 2 | @inline getindex{T <: FixedVectorNoTuple}(x::T, i::Integer) = x.(i) 3 | @inline getindex{N, M, T}(a::Mat{N, M, T}, i::Range, j::Int) = ntuple(IndexFunc(a, j), Val{length(i)})::NTuple{length(i), T} 4 | @inline getindex{N, M, T}(a::Mat{N, M, T}, i::Int, j::Union{Range, Int}) = a.(1)[j][i] 5 | @inline getindex{N, M, T}(a::Mat{N, M, T}, i::Int) = a[ind2sub((N,M), i)...] 6 | @inline getindex(A::FixedArray, I::Tuple) = map(IndexFunctor(A), I) 7 | 8 | @inline setindex(a::FixedArray, value, index::Int...) = map(SetindexFunctor(a, value, index), typeof(a)) 9 | 10 | @inline column{N, T}(v::FixedVector{N, T}) = v 11 | @inline column{R, C, T}(a::Mat{R, C, T}, i::Union{Range, Int}) = a.(1)[i] 12 | 13 | @inline row{N, T}(v::FixedVector{N, T}) = Mat{1, N, T}(v...) 14 | @inline row{N, T}(v::FixedVector{N, T}, i::Int) = (v[i],) 15 | @inline row{R, C, T}(a::Mat{R, C, T}, j::Int) = ntuple(IndexFunc(a, j), Val{C})::NTuple{C, T} 16 | @inline row{R, T}(a::Mat{R, 1, T}, j::Int) = (a.(1)[1][j],) 17 | @inline row{R, T}(a::Mat{R, 2, T}, j::Int) = (a.(1)[1][j], a.(1)[2][j]) 18 | @inline row{R, T}(a::Mat{R, 3, T}, j::Int) = (a.(1)[1][j], a.(1)[2][j], a.(1)[3][j]) 19 | @inline row{R, T}(a::Mat{R, 4, T}, j::Int) = (a.(1)[1][j], a.(1)[2][j], a.(1)[3][j], a.(1)[4][j],) 20 | 21 | 22 | # the columns of the ctranspose are the complex conjugate rows 23 | @inline crow{R, C, T}(a::Mat{R, C, T}, j::Int) = ntuple(IndexFunc(a, j)', Val{C})::NTuple{C, T} 24 | @inline crow{R, T}(a::Mat{R, 1, T}, j::Int) = (a.(1)[1][j]',) 25 | @inline crow{R, T}(a::Mat{R, 2, T}, j::Int) = (a.(1)[1][j]', a.(1)[2][j]') 26 | @inline crow{R, T}(a::Mat{R, 3, T}, j::Int) = (a.(1)[1][j]', a.(1)[2][j]', a.(1)[3][j]') 27 | @inline crow{R, T}(a::Mat{R, 4, T}, j::Int) = (a.(1)[1][j]', a.(1)[2][j]', a.(1)[3][j]', a.(1)[4][j]',) 28 | 29 | 30 | # Unified slicing along combinations of fixed and variable dimensions 31 | 32 | "Get index of a field with `name` in type `T`. Should this be in Base?" 33 | @generated function fieldindex{T}(::Type{T}, name::Symbol) 34 | # Expand all field names inline to allow this to become a constant 35 | # expression. Simple alternative is `findfirst(fieldnames(T), name)` 36 | exprs = [:($(Expr(:quote, n)) == name && return $i) for (i,n) in enumerate(fieldnames(T))] 37 | quote 38 | $(Expr(:meta, :inline)) 39 | $(exprs...) 40 | error("No field \"$name\" in type $T") 41 | end 42 | end 43 | 44 | # Turn `A[1,2,...]` into `destructure(A)[1,2,3,4]` 45 | # 46 | # Turn `A[:x,:y,1,2,...]` into 47 | # `destructure(A)[fieldindex(eltype(A),:x), fieldindex(eltype(A),:y), 1,2,...]` 48 | # 49 | # Also works on the left side of an assignment 50 | function fixed_slice_expr(expr) 51 | assignrhs = nothing 52 | if expr.head == :(=) 53 | @assert length(expr.args) == 2 54 | assignrhs = expr.args[2] 55 | expr = expr.args[1] 56 | end 57 | expr.head == :ref || error("Array reference not found in expression $expr") 58 | inds = Any[] 59 | name = expr.args[1] 60 | for i = 2:length(expr.args) 61 | ind = expr.args[i] 62 | if isa(ind,Expr) && ind.head == :quote 63 | push!(inds, :(fieldindex(eltype(tmp), $(esc(ind))))) 64 | else 65 | push!(inds, esc(ind)) 66 | end 67 | end 68 | if assignrhs === nothing 69 | quote 70 | tmp = $(esc(name)) 71 | destructure(tmp)[$(inds...)] 72 | end 73 | else 74 | quote 75 | tmp = $(esc(name)) 76 | destructure(tmp)[$(inds...)] = $(esc(assignrhs)) 77 | end 78 | end 79 | end 80 | 81 | """ 82 | Slice across both fixed and variable size dimensions of 83 | `Array{F<:FixedArray,M}`. Before slicing, the array is reshaped to the natural 84 | memory ordering as in `destructure()`: the `N=ndims(F)` fixed dimensions come 85 | first, followed by the `M` dimensions of variable length. 86 | 87 | Examples: 88 | 89 | # Array of fixed size vectors 90 | a = [Vec(i,j) for i=1:5, j=1:5] 91 | @fslice a[2,:,:] = 10 92 | xcomps = @fslice a[1,:,:] 93 | 94 | # Vector of fixed size matrices 95 | m = [@fsa([i 0; 0 i^2]) for i=1:4] 96 | @fslice m[1,1,:] 97 | 98 | # Slice immutables by field name 99 | immutable MyVec <: FixedVectorNoTuple{2,Float64} 100 | x::Float64 101 | y::Float64 102 | end 103 | v = MyVec[MyVec(i,-i) for i=1:5] 104 | @fslice v[:x, :] 105 | """ 106 | macro fslice(expr) 107 | fixed_slice_expr(expr) 108 | end 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FixedSizeArrays 2 | 3 | [![Join the chat at https://gitter.im/SimonDanisch/FixedSizeArrays.jl](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SimonDanisch/FixedSizeArrays.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | [![Build Status](https://travis-ci.org/SimonDanisch/FixedSizeArrays.jl.svg?branch=master)](https://travis-ci.org/SimonDanisch/FixedSizeArrays.jl) 6 | [![Coverage Status](https://coveralls.io/repos/SimonDanisch/FixedSizeArrays.jl/badge.svg?branch=master)](https://coveralls.io/r/SimonDanisch/FixedSizeArrays.jl?branch=master) 7 | [![codecov.io](http://codecov.io/github/SimonDanisch/FixedSizeArrays.jl/coverage.svg?branch=master)](http://codecov.io/github/SimonDanisch/FixedSizeArrays.jl?branch=master) 8 | [![Build status](https://ci.appveyor.com/api/projects/status/k6bqy1h4jk322cg6/branch/master?svg=true)](https://ci.appveyor.com/project/SimonDanisch/fixedsizearrays-jl/branch/master) 9 | 10 | #### This package doesn't support 0.3 11 | 12 | #### Packages that use FixedSizeArrays: 13 | [GeometryTypes.jl](https://github.com/JuliaGeometry/GeometryTypes.jl) 14 | 15 | #### Usage and advantages: 16 | FixedSizeArrays is giving any composite type array like behavior by inheriting from FixedSizeArrays. 17 | So you can do something like this: 18 | ```Julia 19 | immutable RGB{T} <: FixedVectorNoTuple{3, T} 20 | r::T 21 | g::T 22 | b::T 23 | function RGB(a::NTuple{3, T}) #needs to be like this to keep constructor code sane 24 | new{T}(a[1], a[2], a[3]) 25 | end 26 | end 27 | immutable Vec{N, T} <: FixedVector{N, T} # defined in FixedSizeArrays already 28 | _::NTuple{N, T} 29 | end 30 | Vec{3, Float32}(77) # constructor with 1 argument already defined 31 | rand(Vec{3, Float64})+sin(Vec(0.,2.,2.)) # a lot of array functions are already defined 32 | #There is also a matrix type 33 | eye(Mat{3,3,Float32}) * rand(Vec{3, Float32}) # will also "just work" 34 | a = Vec(1,2,3)[1:2] # returns (1,2) 35 | ``` 36 | Note that all of the above types are stack allocated and the speed of operations should be fairly fast! 37 | If you find operations to be slow, please file a bug report! 38 | 39 | FixedSizeArrays can be used in a lot of different ways. 40 | You can define color types the same way, and arbitrary other point types like normals, vertices etc. 41 | As they all inherit from FixedSizeArray, it's very easy to handle them in the same way. 42 | 43 | For some more advantages, you can take a look at [MeshIO](https://github.com/JuliaIO/MeshIO.jl). 44 | 45 | Because it's so easy to define different types like Point3, RGB, HSV or Normal3, one can create customized code for these types via multiple dispatch. This is great for visualizing data, as you can offer default visualizations based on the type. 46 | Without FixedSizeArrays, this would end up in a lot of types which would all need to define the same functions over and over again. 47 | 48 | 49 | #### Roadmap 50 | * improve coverage 51 | * incorperate https://github.com/StephenVavasis/Modifyfield.jl 52 | * improve API and consistency 53 | 54 | #### TODO's 55 | 56 | - [ ] Core Array 57 | - [x] basic array interface 58 | - [ ] Inherit from DenseArray (a lot of warnings is caused by this) 59 | - [x] use tuples as a basis 60 | - [ ] Indexing: 61 | - [x] multidimensional access 62 | - [x] colon access for matrices 63 | - [x] multidimensional colon access 64 | - [ ] setindex! 65 | - [ ] setindex!/getindex for arrays of FSA (e.g. easy acces to single fields) 66 | - [x] access slices e.g. Matrix{RGBA} -> Matrix{Red} (sort of) 67 | - [ ] Constructor 68 | - [x] generic constructor for arbitrary Nvectors 69 | - [x] fast constructor for arbitrary types 70 | - [x] parsing constructor e.g Vec{3, Float32}(["23.", "23.", "0.23"]) 71 | - [x] different constructors for ease of use (zero, eye, from other FSAs, etc...) (could be more) 72 | - [ ] clean up constructor code (very messy since its hard to write constructors for abstract types) 73 | - [x] Functions 74 | - [x] all kinds of unary/binary operators 75 | - [x] matrix multiplication 76 | - [x] matrix functions (inv, transpose, etc...) (could be more) 77 | 78 | 79 | 80 | 81 | #### Acknowledgements 82 | [ImmutableArrays](https://github.com/twadleigh/ImmutableArrays.jl) by [twadleigh](https://github.com/twadleigh) was the package that got me going and gave the initial inspirations. 83 | There has been quite a few discussions on [JuliaLang/julia#7568](https://github.com/JuliaLang/julia/pull/7568) shaping the implementation. 84 | Also, [aaalexandrov](https://github.com/aaalexandrov) supplied some code and inspirations. 85 | Big thanks to all the other [contributors](https://github.com/SimonDanisch/FixedSizeArrays.jl/graphs/contributors) ! 86 | 87 | -------------------------------------------------------------------------------- /src/constructors.jl: -------------------------------------------------------------------------------- 1 | 2 | _fill_tuples_expr(inner::Function, SZ::Tuple{Int}, inds...) = 3 | :(tuple($(ntuple(i->inner(i, inds...), SZ[1])...))) 4 | _fill_tuples_expr{N}(inner::Function, SZ::NTuple{N, Int}, inds...) = 5 | :(tuple($(ntuple(i->_fill_tuples_expr(inner, SZ[1:end-1], i, inds...),SZ[end])...))) 6 | fill_tuples_expr(inner::Function, SZ::Tuple) = _fill_tuples_expr(inner, SZ) 7 | 8 | 9 | _fill_tuples(inner, originalSZ, SZ::Tuple{Int}, inds::Int...) = 10 | ntuple(i->inner(SZ, i, inds...), Val{SZ[1]}) 11 | _fill_tuples{N}(inner, originalSZ, SZ::NTuple{N, Int}, inds::Int...) = 12 | ntuple(i->_fill_tuples(inner, originalSZ, SZ[1:end-1], i, inds...), Val{SZ[end]}) 13 | fill_tuples{N}(inner, SZ::NTuple{N, Int}) = _fill_tuples(inner, SZ, SZ) 14 | 15 | 16 | #= 17 | Constructors for homogenous non tuple arguments 18 | Is unrolled for the first 4, since a::T... leads to slow performance 19 | =# 20 | #call{FSA <: FixedArray, T}(::Type{FSA}, a::T) = FSA(NTuple{1,T}((a,))) 21 | call{FSA <: FixedArray, T}(::Type{FSA}, a::T, b::T) = FSA(NTuple{2,T}((a,b))) 22 | call{FSA <: FixedArray, T}(::Type{FSA}, a::T, b::T, c::T) = FSA(NTuple{3,T}((a,b,c))) 23 | call{FSA <: FixedArray, T}(::Type{FSA}, a::T, b::T, c::T, d::T) = FSA(NTuple{4,T}((a,b,c,d))) 24 | call{FSA <: FixedArray, T}(::Type{FSA}, a::T...) = FSA(a) 25 | 26 | immutable ParseFunctor{T, S <: AbstractString} <: Func{1} 27 | t::Type{T} 28 | a::Vector{S} 29 | end 30 | call{T}(pf::ParseFunctor{T}, i::Int) = parse(T, pf.a[i]) 31 | call(pf::ParseFunctor{Void}, i::Int) = parse(pf.a[i]) 32 | 33 | 34 | """ 35 | Constructs a fixedsize array from a Base.Array 36 | """ 37 | @generated function call{FSA <: FixedArray, T <: Array}(::Type{FSA}, a::T) 38 | if eltype(a) <: AbstractString 39 | ElType = eltype_or(FSA, Void) 40 | # can't be defined in another method as it leads to lots of ambiguity with the default constructor 41 | return :(map(ParseFunctor($ElType, a), FSA)) 42 | end 43 | SZ = size_or(FSA, :(size(a))) 44 | ElType = eltype_or(FSA, eltype(a)) 45 | if isa(SZ, Expr) 46 | expr = :($FSA(fill_tuples((sz, i...)->$ElType(a[i...]), $SZ))) 47 | else 48 | tupexpr = fill_tuples_expr((i,inds...) -> :($ElType(a[$i, $(inds...)])), SZ) 49 | expr = :($FSA($tupexpr)) 50 | end 51 | quote 52 | $SZ != size(a) && throw(DimensionMismatch("size of $FSA is not fitting array $(typeof(a)), with size: $(size(a))")) 53 | $expr 54 | end 55 | end 56 | 57 | 58 | """ 59 | Constructor for singular arguments. 60 | Can be a tuple, is not declared as that, because it will generate ambiguities 61 | and overwrites the default constructor. 62 | """ 63 | @generated function call{FSA <: FixedArray, X}(::Type{FSA}, a::X) 64 | ND = ndims(FSA) 65 | if X <: Tuple 66 | types_svec = a.parameters 67 | if all(x-> x <: Tuple, types_svec) && ND == 1 68 | return :(throw( 69 | DimensionMismatch("tried to construct $FSA from $a. I can't allow that!") 70 | )) 71 | end 72 | orlen = length(types_svec) 73 | ortyp = promote_type(types_svec...) 74 | else 75 | orlen = 1 76 | ortyp = a 77 | end 78 | SZ = size_or(FSA, (orlen, ntuple(x->1, ND-1)...)) 79 | T = eltype_or(FSA, ortyp) 80 | FSAT = similar(FSA, T, SZ) 81 | if X <: Tuple 82 | expr = fill_tuples_expr((inds...)->:($T(a[$(inds[1])])), SZ) 83 | else 84 | expr = fill_tuples_expr((inds...)->:($T(a)), SZ) 85 | end 86 | return :($FSAT($expr)) 87 | end 88 | 89 | """ 90 | Constructors for heterogenous multiple arguments. 91 | E.g. 1, 2f0, 4.0 92 | """ 93 | @generated function call{FSA <: FixedArray}(::Type{FSA}, a...) 94 | SZ = size_or(FSA, (length(a),)) 95 | ElType = eltype_or(FSA, promote_type(a...)) 96 | all(x-> x <: Tuple, a) && return :( $FSA(a) ) # TODO be smarter about this 97 | any(x-> x != ElType, a) && return :($FSA(map($ElType, a))) 98 | return :($FSA(a)) 99 | end 100 | 101 | """ 102 | Construction from other FixedSizeArrays + X arguments 103 | E.g. Vec4f0(Vec3f0(1), 0) 104 | """ 105 | @generated function call{FSA <: FixedArray, T1 <: FixedArray}(::Type{FSA}, a::T1, b...) 106 | if isempty(b) # this is the conversion constructor for 2 FSA's 107 | #easiest way is to just construct from the tuple 108 | expr = :(FSA(get_tuple(a))) 109 | if size_or(FSA, nothing) == nothing # no concrete size 110 | return expr 111 | else #has a size 112 | len1 = size(FSA, 1) 113 | len2 = size(T1, 1) 114 | if len1 < len2 # we need to shrink 115 | return :(FSA(get_tuple(a)[1:$len1])) 116 | elseif len1==len2 117 | return expr 118 | else 119 | return :(throw(DimensionMismatch( 120 | "tried to create $FSA from $T1. The latter has too many elements" 121 | ))) 122 | end 123 | end 124 | end 125 | SZ = size_or(FSA, nothing) 126 | ElType = eltype_or(FSA, eltype(T1)) 127 | a_expr = :( a ) 128 | if SZ != nothing 129 | if (prod(SZ) > (length(T1) + length(b))) 130 | throw(DimensionMismatch("$FSA is too small, can not be constructed from array $a and $b arguments")) 131 | elseif prod(SZ) < length(T1) && isempty(b) #shrinking constructor, e.g. Vec3(Vec4(...)) 132 | a_expr = :( a[1:$(prod(SZ))] ) 133 | end 134 | end 135 | return :( $FSA(a..., b...) ) 136 | end 137 | 138 | @inline zero{FSA <: FixedArray}(::Type{FSA}) = map(ConstFunctor(zero(eltype(FSA))), FSA) 139 | zero(fsa::FixedArray) = zero(typeof(fsa)) 140 | @inline one{FSA <: FixedArray}(::Type{FSA}) = map(ConstFunctor(one(eltype(FSA))), FSA) 141 | @inline eye{FSA <: FixedArray}(::Type{FSA}) = map(EyeFunc{eltype(FSA)}, FSA) 142 | @inline unit{FSA <: FixedVector}(::Type{FSA}, i::Integer) = map(UnitFunctor(i, eltype(FSA)), FSA) 143 | 144 | @inline rand{FSA <: FixedArray}(m::MersenneTwister, x::Type{FSA}) = map(MersenneFunctor{eltype(FSA)}(m), FSA) 145 | @inline function rand{FSA <: FixedArray}(x::Type{FSA}, range::Range) 146 | T = eltype(FSA) 147 | map(RandFunctor(T(first(range)):T(step(range)):T(last(range))), FSA) # there's no easy way to convert eltypes of ranges (I think) 148 | end 149 | @inline randn{FSA <: FixedArray}(m::MersenneTwister, x::Type{FSA}) = map(RandnFunctor{eltype(FSA)}(m), FSA) 150 | @inline randn{FSA <: FixedArray}(x::Type{FSA}) = map(RandnFunctor{eltype(FSA)}(Base.Random.GLOBAL_RNG), FSA) 151 | 152 | 153 | """ 154 | Macro `fsa` helps to create fixed size arrays like Julia arrays. 155 | E.g. 156 | ``` 157 | @fsa([1 2 3;4 5 6]) 158 | @fsa([a,2,3]) # you can also use variables 159 | @fsa([a 2 3]) 160 | ``` 161 | """ 162 | macro fsa(expr) 163 | if expr.head == :vect 164 | result = Expr(:call, :Vec, expr.args...) 165 | elseif expr.head == :hcat 166 | result = Expr(:call, :Mat, [Expr(:tuple, a) for a in expr.args]...) 167 | elseif expr.head == :vcat 168 | if isa(expr.args[1], Expr) && expr.args[1].head == :row 169 | cols = [Any[] for _=1:length(expr.args[1].args)] 170 | for row in expr.args 171 | for (j, a) in enumerate(row.args) 172 | push!(cols[j], a) 173 | end 174 | end 175 | result = Expr(:call, :Mat, Expr(:tuple, [Expr(:tuple, col...) for col in cols]...)) 176 | else 177 | result = Expr(:call, :Vec, expr.args...) 178 | end 179 | end 180 | esc(result) 181 | end 182 | -------------------------------------------------------------------------------- /src/expm.jl: -------------------------------------------------------------------------------- 1 | ### expm 2 | 3 | function codeblock(stmts...) 4 | stmts_ = Any[] 5 | for s in stmts 6 | if !(s == nothing) 7 | push!(stmts_, s) 8 | end 9 | end 10 | isempty(stmts_) ? nothing : Expr(:block, stmts_...) 11 | end 12 | 13 | 14 | macro rotate3(A, c, s, i,j) 15 | l = setdiff(1:3, [i,j])[1] 16 | codeblock([ 17 | quote 18 | $(symbol("a",l,k)) = $(esc(A))[$l,$k] 19 | $(symbol("a",i,k)) = $(esc(A))[$i,$k] * $(esc(c)) + $(esc(A))[$j,$k] * $(esc(s)) 20 | $(symbol("a",j,k)) = $(esc(A))[$j,$k] * $(esc(c)) - $(esc(A))[$i,$k] * $(esc(s)) 21 | end 22 | for k in 1:3]..., quote Mat(((a11,a21,a31),(a12,a22,a32),(a13,a23,a33))) end 23 | ) 24 | end 25 | 26 | macro rotate3t(A, c, s, i,j) 27 | l = setdiff(1:3, [i,j])[1] 28 | codeblock([ 29 | quote 30 | $(symbol("a",k,l)) = $(esc(A))[$k,$l] 31 | $(symbol("a",k,i)) = $(esc(A))[$k,$i] * $(esc(c)) + $(esc(A))[$k,$j] * $(esc(s)) 32 | $(symbol("a",k,j)) = $(esc(A))[$k,$j] * $(esc(c)) - $(esc(A))[$k,$i] * $(esc(s)) 33 | end 34 | for k in 1:3]..., quote Mat(((a11,a21,a31),(a12,a22,a32),(a13,a23,a33))) end 35 | ) 36 | end 37 | 38 | macro dblrotate3(A, c, s, i,j) 39 | k = setdiff(1:3, [i,j])[1] 40 | quote 41 | $(symbol("a",i,k)) = $(esc(A))[$i,$k] * $(esc(c)) + $(esc(A))[$j,$k] * $(esc(s)) 42 | $(symbol("a",j,k)) = $(esc(A))[$j,$k] * $(esc(c)) - $(esc(A))[$i,$k] * $(esc(s)) 43 | $(symbol("a",k,i)) = $(esc(A))[$k,$i] * $(esc(c)) + $(esc(A))[$k,$j] * $(esc(s)) 44 | $(symbol("a",k,j)) = $(esc(A))[$k,$j] * $(esc(c)) - $(esc(A))[$k,$i] * $(esc(s)) 45 | 46 | $(symbol("a",j,j)) = $(esc(A))[$j,$j] * abs2($(esc(c))) + $(esc(A))[$i,$i] * abs2($(esc(s))) - ($(esc(A))[$i,$j] + $(esc(A))[$j,$i]) * $(esc(s)) * $(esc(c)) 47 | $(symbol("a",j,i)) = $(esc(A))[$j,$i] * abs2($(esc(c))) - $(esc(A))[$i,$j] * abs2($(esc(s))) + ($(esc(A))[$j,$j] - $(esc(A))[$i,$i]) * $(esc(c)) * $(esc(s)) 48 | $(symbol("a",i,j)) = $(esc(A))[$i,$j] * abs2($(esc(c))) - $(esc(A))[$j,$i] * abs2($(esc(s))) + ($(esc(A))[$j,$j] - $(esc(A))[$i,$i]) * $(esc(c)) * $(esc(s)) 49 | $(symbol("a",i,i)) = $(esc(A))[$i,$i] * abs2($(esc(c))) + $(esc(A))[$j,$j] * abs2($(esc(s))) + ($(esc(A))[$i,$j] + $(esc(A))[$j,$i]) * $(esc(s)) * $(esc(c)) 50 | 51 | $(symbol("a",k,k)) = $(esc(A))[$k,$k] 52 | 53 | Mat(((a11,a21,a31),(a12,a22,a32),(a13,a23,a33))) 54 | end 55 | end 56 | function hessenberg3(A) 57 | c, s, _ = LinAlg.givensAlgorithm(A[2,1],A[3,1]) 58 | @dblrotate3(A, c, s, 2, 3) 59 | end 60 | 61 | 62 | function reflect3{T}(x1,x2,x3, τ::Number, A::Mat{3, 3, T}) # apply reflector from left 63 | 64 | vA1 = τ'*(A[1, 1] + x2'*A[2, 1] + x3'*A[3, 1]) 65 | a11 = A[1,1] - vA1 66 | a21 = A[2,1] - x2*vA1 67 | a31 = A[3,1] - x3*vA1 68 | 69 | vA2 = τ'*(A[1, 2] + x2'*A[2, 2] + x3'*A[3, 2]) 70 | a12 = A[1,2] - vA2 71 | a22 = A[2,2] - x2*vA2 72 | a32 = A[3,2] - x3*vA2 73 | 74 | vA3 = τ'*(A[1, 3] + x2'*A[2, 3] + x3'*A[3, 3]) 75 | a13 = A[1,3] - vA3 76 | a23 = A[2,3] - x2*vA3 77 | a33 = A[3,3] - x3*vA3 78 | Mat(((a11,a21,a31),(a12,a22,a32),(a13,a23,a33)))::Mat{3, 3, T} 79 | 80 | end 81 | 82 | function reflect3t{T}(x1,x2,x3, τ::Number, A::Mat{3, 3, T}) # apply reflector from left 83 | 84 | vA1 = τ'*(A[1, 1] + x2'*A[1, 2] + x3'*A[1, 3]) 85 | a11 = A[1,1] - vA1 86 | a21 = A[1,2] - x2*vA1 87 | a31 = A[1,3] - x3*vA1 88 | 89 | vA2 = τ'*(A[2, 1] + x2'*A[2, 2] + x3'*A[2, 3]) 90 | a12 = A[2,1] - vA2 91 | a22 = A[2,2] - x2*vA2 92 | a32 = A[2,3] - x3*vA2 93 | 94 | vA3 = τ'*(A[3, 1] + x2'*A[3, 2] + x3'*A[3, 3]) 95 | a13 = A[3,1] - vA3 96 | a23 = A[3,2] - x2*vA3 97 | a33 = A[3,3] - x3*vA3 98 | Mat(((a11,a12,a13),(a21,a22,a23),(a31,a32,a33)))::Mat{3, 3, T} 99 | 100 | end 101 | 102 | 103 | #francis QR algorithm with single or double shift 104 | 105 | function francisdbl{T<:Real}(AA::Mat{3, 3, T}) 106 | A = hessenberg3(AA) 107 | EPS = eps(T) 108 | i = 0 109 | loc = 0 #location of zero 110 | la1::T = la2::T = la3::T = 0. 111 | for i in 1:200 112 | if abs(A[3,2]) <= EPS * (abs(A[2,2])+abs(A[3,3])) 113 | la1 = A[3,3] 114 | loc = 1 115 | break 116 | elseif abs(A[2,1]) <= EPS * (abs(A[1,1])+abs(A[2,2])) 117 | la1 = A[1,1] 118 | loc = 3 119 | break 120 | end 121 | l = 3 122 | # if real ev in sub 2x2 matrix do twice a raleigh shift 123 | if 0 < abs2(A[l,l] - A[2,2]) + 4*A[l,2]*A[2,l] 124 | mu = A[l,l] 125 | A = A - mu*(eye(typeof(A))::Mat{3, 3, T}) 126 | c, s, _ = LinAlg.givensAlgorithm(A[1,1],A[2,1])::Tuple{T,T,T} 127 | A2 = @rotate3(A, c, s, 1, 2) 128 | c2, s2, _ = LinAlg.givensAlgorithm(A2[2,2],A2[3,2])::Tuple{T,T,T} 129 | A = @rotate3(A2, c2, s2, 2, 3) 130 | 131 | A2 = @rotate3t(A, c, s, 1, 2) 132 | A = @rotate3t(A2, c2, s2, 2, 3) 133 | 134 | c, s, _ = LinAlg.givensAlgorithm(A[1,1],A[2,1])::Tuple{T,T,T} 135 | A2 = @rotate3(A, c, s, 1, 2) 136 | c2, s2, _ = LinAlg.givensAlgorithm(A2[2,2],A2[3,2])::Tuple{T,T,T} 137 | A = @rotate3(A2, c2, s2, 2, 3) 138 | 139 | A2 = @rotate3t(A, c, s, 1, 2) 140 | A = @rotate3t(A2, c2, s2, 2, 3) 141 | A = A + mu*(eye(typeof(A))::Mat{3, 3, T}) 142 | else #do an implicit francis double shift 143 | 144 | x = (A[1,1]*A[1,1] + A[1,2]*A[2,1] + A[3,1]*A[1,3]) - A[1,2]*A[2,1] - 145 | (A[3,3] + A[2,2])*A[1,1] + A[3,3] * A[2,2] - A[2,3] * A[3,2] 146 | y = A[2,1]*(A[1,1] - A[3,3]) 147 | z = A[2,1]*A[3,2] 148 | 149 | ξ1 = x 150 | normu = abs2(x) + abs2(y) + abs2(z) 151 | if normu == zero(normu) 152 | g = zero(ξ1/normu) 153 | else 154 | normu = sqrt(normu) 155 | ν = copysign(normu, real(ξ1)) 156 | ξ1 += ν 157 | x = -ν 158 | y /= ξ1 159 | z /= ξ1 160 | g = ξ1/ν 161 | end 162 | A = reflect3(x,y,z, g, A) 163 | A = reflect3t(x,y,z, g, A) 164 | A = hessenberg3(A) 165 | end 166 | end 167 | 168 | # check convergence 169 | if loc == 0 170 | converged = false 171 | loc = 1 172 | else 173 | converged = true 174 | end 175 | 176 | # compute eigenvalues of 2x2 submatrix 177 | spur = A[loc,loc] + A[2,2] 178 | det2 = A[loc,loc] * A[2,2] - A[2,loc] * A[loc,2] 179 | dis = abs2(A[loc,loc] - A[2,2]) + 4*A[loc,2]*A[2,loc] 180 | 181 | 182 | # return eigenvalue, discriminant of 2x2 matrix of end result and convergence status 183 | la1, 0.5*spur, dis/4, converged 184 | 185 | end 186 | 187 | 188 | # default version using conversion to and from Matrix type 189 | expm{N,T}(m::Mat{N,N,T}) = Mat{N,N,T}(expm(convert(Matrix{T}, m))) 190 | 191 | expm{T}(A::Mat{1, 1, T}) = Mat{1, 1, T}(((expm(A[1,1]),),)) 192 | function expm{T<:Complex}(A::Mat{2, 2, T}) 193 | a = A[1,1] 194 | b = A[1,2] 195 | c = A[2,1] 196 | d = A[2,2] 197 | 198 | z = sqrt((a-d)*(a-d) + 4.0*b*c ) 199 | e = exp(a/2.0 + d/2.0 - z/2.0) 200 | f = exp(a/2.0 + d/2.0 + z/2.0) 201 | 202 | Mat{2, 2, T}( 203 | ( -(e*(a - d - z))/(2.0* z) + (f*(a - d + z))/(2.0* z), -((e * c)/z) + (f * c)/z), 204 | ( -((e * b)/z) + (f * b)/z, -(e*(-a + d - z))/(2.0* z) + (f*(-a + d + z))/(2.0* z)) 205 | ) 206 | end 207 | 208 | # presumably better without resorting to complex numbers, but for now... 209 | function expm{T<:Real}(A::Mat{2, 2, T}) 210 | a = A[1,1] 211 | b = A[1,2] 212 | c = A[2,1] 213 | d = A[2,2] 214 | 215 | z = sqrt(Complex((a-d)*(a-d) + 4.0*b*c)) 216 | e = exp(a/2.0 + d/2.0 - z/2.0) 217 | f = exp(a/2.0 + d/2.0 + z/2.0) 218 | 219 | Mat{2, 2, T}( 220 | ( real(-(e*(a - d - z))/(2.0* z) + (f*(a - d + z))/(2.0* z)), real(-((e * c)/z) + (f * c)/z)), 221 | ( real(-((e * b)/z) + (f * b)/z), real(-(e*(-a + d - z))/(2.0* z) + (f*(-a + d + z))/(2.0* z))) 222 | ) 223 | end 224 | 225 | 226 | function expm{T<:Real}(AA::Mat{3, 3, T}) 227 | t = sqrt(sum(AA.^2)) 228 | 229 | A = AA/t #normalize 230 | 231 | la1, x, dis, conv = francisdbl(A) 232 | if !conv # if convergence takes to long, call lapack 233 | return Mat{3,3,T}(expm(convert(Matrix{T}, AA))) 234 | end 235 | putzer(t, A, la1, x, dis) 236 | end 237 | 238 | function dexp(t, x, y) 239 | if abs2(x-y) > eps() 240 | 2exp(t*(x+y)/2)*sinh(t*(x-y)/2)/((x-y)) 241 | else 242 | exp(t*(x+y)/2)*(t+(t^3*(x-y)^2)/24) 243 | end 244 | end 245 | 246 | function ddexp(t, x, y, z) 247 | d, i = findmax([abs2(y-x), abs2(z-y),abs2(z-x)]) 248 | if d < eps() 249 | exp(t*(x+y+z)/3)*t^2/2 250 | elseif i == 1 251 | (dexp(t, y, z) - dexp(t, x,z))/(y-x) 252 | elseif i==2 253 | (dexp(t, z, x) - dexp(t, y,x))/(z-y) 254 | elseif i==3 255 | (dexp(t, z, y) - dexp(t, x,y))/(z-x) 256 | end 257 | end 258 | 259 | function putzer{T}(t, A::Mat{3,3,T}, x, y, d) 260 | deltaabs = sqrt(abs(d)) 261 | delta = sqrt(complex(d)) 262 | r1 = real(exp(t * (y+delta))) 263 | la1 = (y+delta) 264 | la2 = (y-delta) 265 | la3 = x 266 | r2 = real(dexp(t, la1, la2)) 267 | r3 = real(ddexp(t, la1, la2, la3)) 268 | R = r2 - la2*r3 269 | r3 * A*A + real(R - la1*r3)*A + real(r1 - la1 * R)*eye(Mat{3,3,T}) 270 | end 271 | 272 | -------------------------------------------------------------------------------- /test/speed_bench_precompile.jl: -------------------------------------------------------------------------------- 1 | using FixedSizeArrays 2 | using FactCheck 3 | 4 | 5 | immutable Normal{N, T} <: FixedVector{N, T} 6 | _::NTuple{N, T} 7 | end 8 | immutable RGB{T} <: FixedVectorNoTuple{3, T} 9 | r::T 10 | g::T 11 | b::T 12 | end 13 | 14 | typealias Vec1d Vec{1, Float64} 15 | typealias Vec2d Vec{2, Float64} 16 | typealias Vec3d Vec{3, Float64} 17 | typealias Vec4d Vec{4, Float64} 18 | typealias Vec3f Vec{3, Float32} 19 | typealias Mat2d Mat{2,2, Float64} 20 | typealias Mat3d Mat{3,3, Float64} 21 | typealias Mat4d Mat{4,4, Float64} 22 | 23 | 24 | function test() 25 | a = 1 26 | a1 = @fsa([a,2,3]) 27 | a2 = @fsa([a 2 3]) 28 | a3 = @fsa([a;2;3]) 29 | a4 = @fsa([a 2;3 4]) 30 | a5 = @fsa([a 2 3;4 5 6]) 31 | a6 = @fsa([a 2;3 4;5 6]) 32 | 33 | a1 == Vec(a,2,3) 34 | a2 == Mat((a,),(2,),(3,)) 35 | a3 == Mat(((a,2,3),)) 36 | a4 == Mat(((a,3),(2,4))) 37 | a5 == Mat(((a,4),(2,5),(3,6))) 38 | a6 == Mat(((a,3,5),(2,4,6))) 39 | 40 | N = 100 41 | a = Point{3, Float32}[Point{3, Float32}(0.7132) for i=1:N] 42 | b = RGB{Float32}[RGB{Float32}(52.293) for i=1:N] 43 | 44 | c = Point{3, Float64}[Point{3, Float64}(typemin(Float64)), a..., Point{3, Float64}(typemax(Float64))] 45 | d = RGB{Float64}[RGB(typemin(Float64)), b..., RGB(typemax(Float64))] 46 | 47 | sa = sum(a) 48 | ma = mean(a) 49 | sb = sum(b) 50 | mb = mean(b) 51 | 52 | maximum(c) == Point{3, Float32}(typemax(Float64)) 53 | minimum(c) == Point{3, Float32}(typemin(Float64)) 54 | 55 | maximum(d) == RGB(typemax(Float64)) 56 | minimum(d) == RGB(typemin(Float64)) 57 | af = a + 1f0 58 | bf = b + 1f0 59 | for elem in af 60 | a[1] + 1f0 == elem 61 | end 62 | for elem in bf 63 | b[1] + 1f0 == elem 64 | end 65 | 66 | 67 | for T=[Float32, Float64, Int, UInt, UInt32, UInt8] 68 | r = rand(T) 69 | x = RGB{Int}[RGB(1) for i=1:10] 70 | RGB{Float32}(["0.222", "9.8822", "29.999"]) == RGB{Float32}(0.222, 9.8822, 29.999) 71 | typeof(map(RGB{Float32}, x)) == Vector{RGB{Float32}} 72 | RGB{T}(r) == RGB(r,r,r) 73 | RGB{T}([r,r,r]) == RGB(r,r,r) 74 | length(RGB{T}([r,r,r])) == 3 75 | length(RGB{T}) == 3 76 | eltype(RGB{T}([r,r,r])) == T 77 | eltype(RGB{T}) == T 78 | typeof(RGB(r,r,r)) == RGB{T} 79 | typeof(RGB{T}(1)) == RGB{T} 80 | typeof(RGB{T}(1,2,3)) == RGB{T} 81 | ndims(RGB{T}(1,2,3)) == 1 82 | 83 | typeof(RGB{T}(1f0)) == RGB{T} 84 | typeof(RGB{T}(1f0,2f0,3f0)) == RGB{T} 85 | typeof(RGB{T}(1f0, 2, 3.0)) == RGB{T} 86 | typeof(RGB(1f0, 2, 3.0)) == RGB{Float64} 87 | typeof(RGB{Int}(1f0, 2, 3.0)) == RGB{Int} 88 | end 89 | 90 | # A little brutal, but hey.... Better redudantant tests, than not enough tests 91 | for N=1:3:10 92 | for VT=[Point, Vec, Normal], VT2=[Normal, Vec, Point], ET=[Float32, Int, UInt64, Float64], ET2=[Float64, UInt64, Int, Float32] 93 | rand_range = ET(1):ET(10) 94 | rand_range2 = ET2(1):ET2(10) 95 | rn = rand(rand_range, N) 96 | v0 = VT(rn) 97 | # parse constructor: 98 | VT{N, ET}(map(string, rn)) == v0 99 | # multi constructor 100 | v1 = VT{N, ET}(rn...) 101 | v1 == v0 102 | typeof(v1) == VT{N, ET} 103 | length(v1) == N 104 | eltype(v1) == ET 105 | ndims(v1) == 1 106 | 107 | length(typeof(v1)) == N 108 | eltype(typeof(v1)) == ET 109 | 110 | for i=1:N 111 | v1[i] == rn[i] 112 | end 113 | # from other FSA without parameters 114 | v2 = VT2(v1) 115 | 116 | typeof(v2) == VT2{N, ET} 117 | length(v2) == N 118 | eltype(v2) == ET 119 | for i=1:N 120 | v2[i] == v1[i] 121 | end 122 | # from other FSA with parameters 123 | for i=1:N 124 | v3 = VT2{i, ET2}(v1) 125 | typeof(v3) == VT2{i, ET2} 126 | length(v3) == i 127 | eltype(v3) == ET2 128 | for i=1:i 129 | v3[i] == ET2(v2[i]) 130 | end 131 | end 132 | # from single 133 | r = rand(rand_range) 134 | r2 = rand(rand_range2) 135 | v1 = VT{N, ET}(r) 136 | v2 = VT{N, ET2}(r) 137 | v3 = VT{N, ET}(r2) 138 | v4 = VT{N, ET2}(r2) 139 | 140 | for i=1:N 141 | v1[i] == r 142 | v2[i] == ET2(r) 143 | v3[i] == r2 144 | v4[i] == ET2(r2) 145 | end 146 | x = VT{N, ET}[VT{N, ET}(1) for i=1:10] 147 | x1 = VT2{N, ET}[VT{N, ET}(1) for i=1:10] 148 | x2 = map(VT2, x) 149 | x3 = map(VT, x2) 150 | typeof(x) == Vector{VT{N, ET}} 151 | typeof(x1) == Vector{VT2{N, ET}} 152 | typeof(x2) == Vector{VT2{N, ET}} 153 | typeof(x3) == Vector{VT{N, ET}} 154 | x3 == x 155 | end 156 | end 157 | 158 | 159 | typeof(Vec3f(1,1,1)) == Vec{3, Float32} 160 | typeof(Vec3f(1,1f0,1)) == Vec{3, Float32} 161 | typeof(Vec3f(1f0,1,1.0)) == Vec{3, Float32} 162 | 163 | typeof(Vec3f(1)) == Vec{3, Float32} 164 | typeof(Vec3f(0)) == Vec{3, Float32} 165 | Vec3f(1.0) == Vec(1f0,1f0,1f0) 166 | Vec3f(1.0f0) == Vec(1f0,1f0,1f0) 167 | Vec3f(1.0f0) == Vec3f(1) 168 | Vec(1.0, 1.0, 1.0) == Vec3d(1) 169 | Vec2d(Vec3d(1)) == Vec(1.0, 1.0) 170 | Vec(Vec3d(1), 1.0) == Vec4d(1) 171 | Vec(Vec3d(1), 1) == Vec4d(1) 172 | Vec3d(Vec3f(1.0)) == Vec3d(1.0) 173 | 174 | v2 = Vec(6.0,5.0,4.0) 175 | v1 = Vec(1.0,2.0,3.0) 176 | v2 = Vec(6.0,5.0,4.0) 177 | 178 | setindex(v1, 88.9, 1) == Vec(88.9,2.0,3.0) 179 | v1[1] == 1.0 180 | v1[2] == 2.0 181 | v1[3] == 3.0 182 | v1[1:3] == (1.0, 2.0, 3.0) 183 | v1[1:2] == (1.0, 2.0) 184 | v1[1:1] == (1.0,) 185 | v1[(1,2)] == (1.0,2.0) 186 | v1[(2,1)] == (2.0,1.0) 187 | m = Mat{4,4,Int}( 188 | (1,2,3,4), 189 | (5,6,7,8), 190 | (9,10,11,12), 191 | (13,14,15,16) 192 | ) 193 | setindex(m, 42.0, 2,2) == Mat{4,4,Int}( 194 | (1,2,3,4), 195 | (5,42.0,7,8), 196 | (9,10,11,12), 197 | (13,14,15,16) 198 | ) 199 | m[1] == 1 200 | m[2] == 2 201 | m[10] == 10 202 | m[2,2] == 6 203 | m[3,4] == 15 204 | m[1:4, 1] == (1,5,9,13) 205 | m[1, 1:4] == (1,2,3,4) 206 | 207 | 208 | -v1 == Vec(-1.0,-2.0,-3.0) 209 | isa(-v1, Vec3d) == true 210 | v1+v2 == Vec3d(7.0,7.0,7.0) 211 | v2-v1 == Vec3d(5.0,3.0,1.0) 212 | v1.*v2 == Vec3d(6.0,10.0,12.0) 213 | v1 ./ v1 == Vec3d(1.0,1.0,1.0) 214 | 1.0 + v1 == Vec3d(2.0,3.0,4.0) 215 | 1.0 .+ v1 == Vec3d(2.0,3.0,4.0) 216 | v1 + 1.0 == Vec3d(2.0,3.0,4.0) 217 | v1 .+ 1.0 == Vec3d(2.0,3.0,4.0) 218 | 1 + v1 == Vec3d(2.0,3.0,4.0) 219 | 1 .+ v1 == Vec3d(2.0,3.0,4.0) 220 | v1 + 1 == Vec3d(2.0,3.0,4.0) 221 | v1 .+ 1 == Vec3d(2.0,3.0,4.0) 222 | 223 | v1 - 1.0 == Vec3d(0.0,1.0,2.0) 224 | v1 .- 1.0 == Vec3d(0.0,1.0,2.0) 225 | 1.0 - v1 == Vec3d(0.0,-1.0,-2.0) 226 | 1.0 .- v1 == Vec3d(0.0,-1.0,-2.0) 227 | v1 - 1 == Vec3d(0.0,1.0,2.0) 228 | v1 .- 1 == Vec3d(0.0,1.0,2.0) 229 | 1 - v1 == Vec3d(0.0,-1.0,-2.0) 230 | 1 .- v1 == Vec3d(0.0,-1.0,-2.0) 231 | 232 | 2.0 * v1 == Vec3d(2.0,4.0,6.0) 233 | 2.0 .* v1 == Vec3d(2.0,4.0,6.0) 234 | v1 * 2.0 == Vec3d(2.0,4.0,6.0) 235 | v1 .* 2.0 == Vec3d(2.0,4.0,6.0) 236 | 2 * v1 == Vec3d(2.0,4.0,6.0) 237 | 2 .* v1 == Vec3d(2.0,4.0,6.0) 238 | v1 * 2 == Vec3d(2.0,4.0,6.0) 239 | v1 .* 2 == Vec3d(2.0,4.0,6.0) 240 | 241 | v1 / 2.0 == Vec3d(0.5,1.0,1.5) 242 | v1 ./ 2.0 == Vec3d(0.5,1.0,1.5) 243 | v1 / 2 == Vec3d(0.5,1.0,1.5) 244 | v1 ./ 2 == Vec3d(0.5,1.0,1.5) 245 | 246 | 12.0 ./ v1 == Vec3d(12.0,6.0,4.0) 247 | 12 ./ v1 == Vec3d(12.0,6.0,4.0) 248 | 249 | (v1 .^ 2) == Vec3d(1.0,4.0,9.0) 250 | (v1 .^ 2.0) == Vec3d(1.0,4.0,9.0) 251 | (2.0 .^ v1) == Vec3d(2.0,4.0,8.0) 252 | (2 .^ v1) == Vec3d(2.0,4.0,8.0) 253 | norm(Vec3d(1.0,2.0,2.0)) == 3.0 254 | 255 | # cross product 256 | cross(v1,v2) == Vec3d(-7.0,14.0,-7.0) 257 | isa(cross(v1,v2),Vec3d) == true 258 | a = Vec{2,Int}(1,2) 259 | b = Vec{2,Float64}(1.,2.) 260 | hypot(a) == 2.23606797749979 261 | hypot(b) == 2.23606797749979 262 | hypot(a) == hypot(b) == true 263 | 264 | 265 | 266 | 267 | # type conversion 268 | isa(convert(Vec3f,v1), Vec3f) == true 269 | 270 | isa(convert(Vector{Float64}, v1), Vector{Float64}) == true 271 | convert(Vector{Float64}, v1) == [1.0,2.0,3.0] 272 | 273 | # matrix operations 274 | 275 | #typealias Mat1d Matrix1x1{Float64} 276 | 277 | zeromat = Mat2d((0.0,0.0),(0.0,0.0)) 278 | 279 | 280 | length(Mat2d) == 4 281 | length(zeromat) == 4 282 | 283 | size(Mat2d) == (2,2) 284 | size(zeromat) == (2,2) 285 | 286 | zero(Mat2d) == zeromat 287 | 288 | for i=1:4, j=1:4 289 | x1 = rand(i,j) 290 | Mat(x1') == Mat(x1)' 291 | end 292 | 293 | 294 | v = Vec(1.0,2.0,3.0,4.0) 295 | r = row(v) 296 | c = column(v) 297 | 298 | #r' == c 299 | #c' == r 300 | 301 | a = c*r 302 | 303 | b = Mat( 304 | (1.0,2.0,3.0,4.0), 305 | (2.0,4.0,6.0,8.0), 306 | (3.0,6.0,9.0,12.0), 307 | (4.0,8.0,12.0,16.0) 308 | ) 309 | 310 | length(b) == 16 311 | 312 | a==b 313 | mat30 = Mat(((30.0,),)) 314 | r*c == mat30 315 | 316 | #row(r, 1) == v 317 | #column(c,1) == v 318 | #row(r+c',1) == 2*v 319 | sum(r) == sum(v) 320 | prod(c) == prod(v) 321 | 322 | eye(Mat3d) == Mat((1.0,0.0,0.0), 323 | (0.0,1.0,0.0), 324 | (0.0,0.0,1.0)) 325 | #v*eye(Mat4d)*v == 30.0 326 | x = -r 327 | y = -1.0*r 328 | jm = rand(4,4) 329 | lowl = Mat(jm) 330 | jm2 = convert(Array{Float64,2}, lowl) 331 | 332 | #Single valued constructor 333 | Mat4d(0.0) 334 | a = Vec4d(0) 335 | b = Vec4d(0,0,0,0) 336 | 337 | v = rand(4) 338 | m = rand(4,4) 339 | vfs = Vec(v) 340 | mfs = Mat(m) 341 | 342 | for i=1:4, j=1:4 343 | vfs = rand(Vec{j, Float64}) 344 | mfs = rand(Mat{i,j, Float64}) 345 | vm = m * v 346 | fsvm = mfs * vfs 347 | if i == j 348 | fmm = mfs * mfs 349 | fmm = det(mfs) 350 | fmm = inv(mfs) 351 | fmm = expm(mfs) 352 | end 353 | end 354 | mm = m' 355 | fmm = mfs' 356 | 357 | 358 | ac = rand(3) 359 | bc = rand(3) 360 | 361 | a = rand(4) 362 | b = rand(4) 363 | c = rand(4,4) 364 | 365 | d = cross(ac, bc) 366 | d2 = a+b 367 | f = c*a 368 | g = c*b 369 | h = c*f 370 | i = dot(f, a) 371 | j = dot(a, g) 372 | k = abs(f) 373 | l = abs(-f) 374 | 375 | acfs = Vec(ac) 376 | bcfs = Vec(bc) 377 | 378 | afs = Vec(a) 379 | bfs = Vec(b) 380 | cfs = Mat(c) 381 | 382 | dfs = cross(acfs, bcfs) 383 | d2fs = afs+bfs 384 | ffs = cfs*afs 385 | gfs = cfs*bfs 386 | hfs = cfs*ffs 387 | ifs = dot(ffs, afs) 388 | jfs = dot(afs, gfs) 389 | kfs = abs(ffs) 390 | lfs = abs(-ffs) 391 | 392 | isapprox(acfs, ac) == true 393 | isapprox(bcfs, bc) == true 394 | 395 | isapprox(afs, a) == true 396 | isapprox(bfs, b) == true 397 | isapprox(cfs, c) == true 398 | 399 | isapprox(dfs, d) == true 400 | isapprox(d2fs, d2) == true 401 | isapprox(ffs, f) == true 402 | isapprox(gfs, g) == true 403 | isapprox(hfs, h) == true 404 | isapprox(ifs, i) == true 405 | isapprox(jfs, j) == true 406 | isapprox(kfs, k) == true 407 | isapprox(lfs, l) == true 408 | 409 | Vec{3, Int}(1) == Vec{3, Float64}(1) 410 | Vec(1,2,3) == Vec(1.0,2.0,3.0) 411 | Vec(1,2,3) == [1,2,3] 412 | Mat((1,2),(3,4)) == Mat((1,2),(3,4)) 413 | 414 | 415 | const unaryOps = ( 416 | -, ~, conj, abs, 417 | sin, cos, tan, sinh, cosh, tanh, 418 | asin, acos, atan, asinh, acosh, atanh, 419 | sec, csc, cot, asec, acsc, acot, 420 | sech, csch, coth, asech, acsch, acoth, 421 | sinc, cosc, cosd, cotd, cscd, secd, 422 | sind, tand, acosd, acotd, acscd, asecd, 423 | asind, atand, rad2deg, deg2rad, 424 | log, log2, log10, log1p, exponent, exp, 425 | exp2, expm1, cbrt, sqrt, erf, 426 | erfc, erfcx, erfi, dawson, 427 | 428 | #trunc, round, ceil, floor, #see JuliaLang/julia#12163 429 | significand, lgamma, 430 | gamma, lfact, frexp, modf, airy, airyai, 431 | airyprime, airyaiprime, airybi, airybiprime, 432 | besselj0, besselj1, bessely0, bessely1, 433 | eta, zeta, digamma 434 | ) 435 | 436 | # vec-vec and vec-scalar 437 | const binaryOps = ( 438 | .+, .-,.*, ./, .\, /, 439 | .==, .!=, .<, .<=, .>, .>=, +, -, 440 | min, max, 441 | 442 | atan2, besselj, bessely, hankelh1, hankelh2, 443 | besseli, besselk, beta, lbeta 444 | ) 445 | 446 | 447 | test1 = (Vec(1,2,typemax(Int)), Mat((typemin(Int),2,5), (2,3,5), (-2,3,6)), Vec{4, Float32}(0.777)) 448 | test2 = (Vec(1,0,typemax(Int)), Mat((typemin(Int),77,1), (2,typemax(Int),5), (-2,3,6)), Vec{4, Float32}(-23.2929)) 449 | for op in binaryOps 450 | for i=1:length(test1) 451 | v1 = test1[i] 452 | v2 = test2[i] 453 | try # really bad tests, but better than nothing... 454 | if applicable(op, v1[1], v2[1]) && typeof(op(v1[1], v2[1])) == eltype(v1) 455 | r = op(v1, v2) 456 | for j=1:length(v1) 457 | r[j] == op(v1[j], v2[j]) 458 | end 459 | 460 | end 461 | end 462 | end 463 | end 464 | 465 | test = (Vec(1,2,typemax(Int)), Mat((typemin(Int),2,5), (2,3,5), (-2,3,6)), Vec{4, Float32}(0.777)) 466 | for op in unaryOps 467 | for t in test 468 | try 469 | if applicable(op, t[1]) && typeof(op(t[1])) == eltype(t) 470 | v = op(t) 471 | for i=1:length(v) 472 | v[i] == op(t[i]) 473 | end 474 | end 475 | end 476 | end 477 | end 478 | 479 | 480 | end 481 | 482 | using SnoopCompile 483 | 484 | snoop_on() 485 | SnoopCompile.@snoop "/tmp/fsa_compiles.csv" begin 486 | test() 487 | end 488 | snoop_off() 489 | 490 | data = SnoopCompile.read("/tmp/fsa_compiles.csv") 491 | pc, discards = SnoopCompile.parcel(data[end:-1:1,2]) 492 | SnoopCompile.write(pc, "/tmp/precompile") 493 | 494 | -------------------------------------------------------------------------------- /src/ops.jl: -------------------------------------------------------------------------------- 1 | Base.promote_array_type{FSA <: FixedArray, T}(F, ::Type{T}, ::Type{FSA}) = FSA 2 | 3 | # operations 4 | const unaryOps = (:-, :~, :conj, :abs, 5 | :sin, :cos, :tan, :sinh, :cosh, :tanh, 6 | :asin, :acos, :atan, :asinh, :acosh, :atanh, 7 | :sec, :csc, :cot, :asec, :acsc, :acot, 8 | :sech, :csch, :coth, :asech, :acsch, :acoth, 9 | :sinc, :cosc, :cosd, :cotd, :cscd, :secd, 10 | :sind, :tand, :acosd, :acotd, :acscd, :asecd, 11 | :asind, :atand, :rad2deg, :deg2rad, 12 | :log, :log2, :log10, :log1p, :exponent, :exp, 13 | :exp2, :expm1, :cbrt, :sqrt, :erf, 14 | :erfc, :erfcx, :erfi, :dawson, :ceil, :floor, 15 | :trunc, :round, :significand, :lgamma, 16 | :gamma, :lfact, :frexp, :modf, :airy, :airyai, 17 | :airyprime, :airyaiprime, :airybi, :airybiprime, 18 | :besselj0, :besselj1, :bessely0, :bessely1, 19 | :eta, :zeta, :digamma) 20 | 21 | # vec-vec and vec-scalar 22 | const binaryOps = (:.+, :.-,:.*, :./, :.\, :.^, 23 | :.==, :.!=, :.<, :.<=, :.>, :.>=, :+, :-, 24 | :min, :max, 25 | :div, :fld, :rem, :mod, :mod1, :cmp, 26 | :atan2, :besselj, :bessely, :hankelh1, :hankelh2, 27 | :besseli, :besselk, :beta, :lbeta) 28 | 29 | const matrixOps = (:*, :/) 30 | 31 | const reductions = ((:sum,:+), (:prod,:*), (:minimum,:min), (:maximum,:max)) 32 | 33 | function gen_functor(func::Symbol, unary::Int) 34 | functor_name = gensym() 35 | arguments = ntuple(i->symbol("arg$i"), unary) 36 | functor_expr = quote 37 | immutable $functor_name <: Func{$unary} end 38 | @inline call(::$functor_name, $(arguments...)) = $func($(arguments...)) 39 | end 40 | return (functor_name, functor_expr) 41 | end 42 | 43 | for (callfun, reducefun) in reductions 44 | functor_name, functor_expr = gen_functor(reducefun, 2) 45 | eval(quote 46 | $functor_expr 47 | @inline $(callfun){T <: FixedArray}(x::T) = reduce($functor_name(), x) 48 | end) 49 | end 50 | 51 | for op in unaryOps 52 | functor_name, functor_expr = gen_functor(op, 1) 53 | eval(quote 54 | $functor_expr 55 | @inline $(op){T <: FixedArray}(x::T) = map($functor_name(), x) 56 | end) 57 | end 58 | 59 | for op in binaryOps 60 | functor_name, functor_expr = gen_functor(op, 2) 61 | eval(quote 62 | $functor_expr 63 | @inline $op{T <: FixedArray}(x::T, y::T) = map($functor_name(), x, y) 64 | @inline $op{T, T2, NDIM, SIZE}(x::FixedArray{T, NDIM, SIZE}, y::FixedArray{T2, NDIM, SIZE}) = $op(promote(x, y)...) 65 | @inline $op{T <: Number}(x::T, y::FixedArray{T}) = map($functor_name(), x, y) 66 | @inline $op{T1 <: Number, T2}(x::T1, y::FixedArray{T2}) = $op(promote(x, y)...) 67 | @inline $op{T <: Number}(x::FixedArray{T}, y::T) = map($functor_name(), x, y) 68 | @inline $op{T1, T2 <: Number}(x::FixedArray{T1}, y::T2) = $op(promote(x, y)...) 69 | end) 70 | end 71 | 72 | for op in matrixOps 73 | functor_name, functor_expr = gen_functor(op, 2) 74 | eval(quote 75 | $functor_expr 76 | @inline $op{T <: Number}(x::T, y::FixedArray{T}) = map($functor_name(), x, y) 77 | @inline $op{T1 <: Number, T2}(x::T1, y::FixedArray{T2}) = $op(promote(x, y)...) 78 | @inline $op{T <: Number}(x::FixedArray{T}, y::T) = map($functor_name(), x, y) 79 | @inline $op{T1, T2 <: Number}(x::FixedArray{T1}, y::T2) = $op(promote(x, y)...) 80 | end) 81 | end 82 | 83 | @inline function promote{T1 <: FixedArray, T2 <: FixedArray}(a::T1, b::T2) 84 | T = promote_type(eltype(T1), eltype(T2)) 85 | map(T, a), map(T, b) 86 | end 87 | function promote{T1, T2 <: Number}(a::FixedArray{T1}, b::T2) 88 | T = promote_type(T1, T2) 89 | map(T, a), T(b) 90 | end 91 | function promote{T1 <: Number, T2}(a::T1, b::FixedArray{T2}) 92 | T = promote_type(T1, T2) 93 | T(a), map(T, b) 94 | end 95 | 96 | function promote_rule{N, T, X<:Number}(::Type{Vec{N,T}}, ::Type{X}) 97 | Vec{N, promote_type(T, X)} 98 | end 99 | 100 | @inline ctranspose{R, C, T}(a::Mat{R, C, T}) = Mat(ntuple(CRowFunctor(a), Val{R})) 101 | @generated function ctranspose{N,T}(b::Vec{N,T}) 102 | expr = [:(b._[$i]',) for i=1:N] 103 | return quote 104 | Mat{1,N,T}($(expr...)) 105 | end 106 | end 107 | @inline transpose{R, C, T}(a::Mat{R, C, T}) = Mat(ntuple(RowFunctor(a), Val{R})) 108 | @generated function transpose{N,T}(b::Vec{N,T}) 109 | expr = [:(transpose(b._[$i]),) for i=1:N] 110 | return quote 111 | Mat{1,N,T}($(expr...)) 112 | end 113 | end 114 | @inline Base.hypot{T}(v::FixedVector{2,T}) = hypot(v[1],v[2]) 115 | 116 | immutable DotFunctor <: Func{2} end 117 | call(::DotFunctor, a, b) = a'*b 118 | @inline dot{T <: FixedArray}(a::T, b::T) = sum(map(DotFunctor(), a, b)) 119 | 120 | immutable BilinearDotFunctor <: Func{2} end 121 | call(::BilinearDotFunctor, a, b) = a*b 122 | @inline bilindot{T <: Union{FixedArray, Tuple}}(a::T, b::T) = sum(map(DotFunctor(), a, b)) 123 | @inline bilindot{T1 <: Tuple, T2 <: FixedArray}(a::T1, b::T2) = sum(map(DotFunctor(), a, b)) 124 | 125 | @inline bilindot{T}(a::NTuple{1,T}, b::NTuple{1,T}) = @inbounds return a[1]*b[1] 126 | @inline bilindot{T}(a::NTuple{2,T}, b::NTuple{2,T}) = @inbounds return (a[1]*b[1] + a[2]*b[2]) 127 | @inline bilindot{T}(a::NTuple{3,T}, b::NTuple{3,T}) = @inbounds return (a[1]*b[1] + a[2]*b[2] + a[3]*b[3]) 128 | @inline bilindot{T}(a::NTuple{4,T}, b::NTuple{4,T}) = @inbounds return (a[1]*b[1] + a[2]*b[2] + a[3]*b[3]+a[4]*b[4]) 129 | 130 | 131 | #cross{T}(a::FixedVector{2, T}, b::FixedVector{2, T}) = a[1]*b[2]-a[2]*b[1] # not really used!? 132 | @inline cross{T<:Number}(a::FixedVector{3, T}, b::FixedVector{3, T}) = @inbounds return typeof(a)( 133 | a[2]*b[3]-a[3]*b[2], 134 | a[3]*b[1]-a[1]*b[3], 135 | a[1]*b[2]-a[2]*b[1] 136 | ) 137 | 138 | @inline norm{T, N}(a::FixedVector{T, N}) = sqrt(dot(a,a)) 139 | @inline normalize{FSA <: FixedArray}(a::FSA) = a / norm(a) 140 | 141 | #Matrix 142 | @inline det{T}(A::FixedMatrix{1, 1, T}) = @inbounds return ( A[1] ) 143 | @inline det{T}(A::FixedMatrix{2, 2, T}) = @inbounds return ( A[1,1]*A[2,2] - A[1,2]*A[2,1]) 144 | @inline det{T}(A::FixedMatrix{3, 3, T}) = @inbounds return ( 145 | A[1,1]*(A[2,2]*A[3,3]-A[2,3]*A[3,2]) - 146 | A[1,2]*(A[2,1]*A[3,3]-A[2,3]*A[3,1]) + 147 | A[1,3]*(A[2,1]*A[3,2]-A[2,2]*A[3,1]) 148 | ) 149 | @inline det{T}(A::FixedMatrix{4, 4, T}) = @inbounds return ( 150 | A[13] * A[10] * A[7] * A[4] - A[9] * A[14] * A[7] * A[4] - 151 | A[13] * A[6] * A[11] * A[4] + A[5] * A[14] * A[11] * A[4] + 152 | A[9] * A[6] * A[15] * A[4] - A[5] * A[10] * A[15] * A[4] - 153 | A[13] * A[10] * A[3] * A[8] + A[9] * A[14] * A[3] * A[8] + 154 | A[13] * A[2] * A[11] * A[8] - A[1] * A[14] * A[11] * A[8] - 155 | A[9] * A[2] * A[15] * A[8] + A[1] * A[10] * A[15] * A[8] + 156 | A[13] * A[6] * A[3] * A[12] - A[5] * A[14] * A[3] * A[12] - 157 | A[13] * A[2] * A[7] * A[12] + A[1] * A[14] * A[7] * A[12] + 158 | A[5] * A[2] * A[15] * A[12] - A[1] * A[6] * A[15] * A[12] - 159 | A[9] * A[6] * A[3] * A[16] + A[5] * A[10] * A[3] * A[16] + 160 | A[9] * A[2] * A[7] * A[16] - A[1] * A[10] * A[7] * A[16] - 161 | A[5] * A[2] * A[11] * A[16] + A[1] * A[6] * A[11] * A[16] 162 | ) 163 | 164 | 165 | trace(A::FixedMatrix{1,1}) = A[1,1] 166 | trace(A::FixedMatrix{2,2}) = A[1,1] + A[2,2] 167 | trace(A::FixedMatrix{3,3}) = A[1,1] + A[2,2] + A[3,3] 168 | trace(A::FixedMatrix{4,4}) = A[1,1] + A[2,2] + A[3,3] + A[4,4] 169 | 170 | \{m,n,T}(mat::Mat{m,n,T}, v::Vec{n, T}) = inv(mat)*v 171 | 172 | @inline inv{T}(A::Mat{1, 1, T}) = @inbounds return Mat{1, 1, T}(inv(A[1])) 173 | @inline function inv{T}(A::Mat{2, 2, T}) 174 | determinant = det(A) 175 | @inbounds return Mat{2, 2, T}( 176 | (A[2,2] /determinant, -A[2,1]/determinant), 177 | (-A[1,2]/determinant, A[1,1] /determinant) 178 | ) 179 | end 180 | @inline function inv{T}(A::Mat{3, 3, T}) 181 | determinant = det(A) 182 | @inbounds return Mat{3, 3, T}( 183 | ((A[2,2]*A[3,3]-A[2,3]*A[3,2]) /determinant, 184 | -(A[2,1]*A[3,3]-A[2,3]*A[3,1])/determinant, 185 | (A[2,1]*A[3,2]-A[2,2]*A[3,1]) /determinant), 186 | 187 | (-(A[1,2]*A[3,3]-A[1,3]*A[3,2])/determinant, 188 | (A[1,1]*A[3,3]-A[1,3]*A[3,1]) /determinant, 189 | -(A[1,1]*A[3,2]-A[1,2]*A[3,1])/determinant), 190 | 191 | ((A[1,2]*A[2,3]-A[1,3]*A[2,2]) /determinant, 192 | -(A[1,1]*A[2,3]-A[1,3]*A[2,1])/determinant, 193 | (A[1,1]*A[2,2]-A[1,2]*A[2,1]) /determinant) 194 | ) 195 | end 196 | 197 | 198 | @inline function inv{T}(A::Mat{4, 4, T}) 199 | determinant = det(A) 200 | @inbounds return Mat{4, 4, T}( 201 | ((A[2,3]*A[3,4]*A[4,2] - A[2,4]*A[3,3]*A[4,2] + A[2,4]*A[3,2]*A[4,3] - A[2,2]*A[3,4]*A[4,3] - A[2,3]*A[3,2]*A[4,4] + A[2,2]*A[3,3]*A[4,4]) / determinant, 202 | (A[2,4]*A[3,3]*A[4,1] - A[2,3]*A[3,4]*A[4,1] - A[2,4]*A[3,1]*A[4,3] + A[2,1]*A[3,4]*A[4,3] + A[2,3]*A[3,1]*A[4,4] - A[2,1]*A[3,3]*A[4,4]) / determinant, 203 | (A[2,2]*A[3,4]*A[4,1] - A[2,4]*A[3,2]*A[4,1] + A[2,4]*A[3,1]*A[4,2] - A[2,1]*A[3,4]*A[4,2] - A[2,2]*A[3,1]*A[4,4] + A[2,1]*A[3,2]*A[4,4]) / determinant, 204 | (A[2,3]*A[3,2]*A[4,1] - A[2,2]*A[3,3]*A[4,1] - A[2,3]*A[3,1]*A[4,2] + A[2,1]*A[3,3]*A[4,2] + A[2,2]*A[3,1]*A[4,3] - A[2,1]*A[3,2]*A[4,3]) / determinant), 205 | 206 | ((A[1,4]*A[3,3]*A[4,2] - A[1,3]*A[3,4]*A[4,2] - A[1,4]*A[3,2]*A[4,3] + A[1,2]*A[3,4]*A[4,3] + A[1,3]*A[3,2]*A[4,4] - A[1,2]*A[3,3]*A[4,4]) / determinant, 207 | (A[1,3]*A[3,4]*A[4,1] - A[1,4]*A[3,3]*A[4,1] + A[1,4]*A[3,1]*A[4,3] - A[1,1]*A[3,4]*A[4,3] - A[1,3]*A[3,1]*A[4,4] + A[1,1]*A[3,3]*A[4,4]) / determinant, 208 | (A[1,4]*A[3,2]*A[4,1] - A[1,2]*A[3,4]*A[4,1] - A[1,4]*A[3,1]*A[4,2] + A[1,1]*A[3,4]*A[4,2] + A[1,2]*A[3,1]*A[4,4] - A[1,1]*A[3,2]*A[4,4]) / determinant, 209 | (A[1,2]*A[3,3]*A[4,1] - A[1,3]*A[3,2]*A[4,1] + A[1,3]*A[3,1]*A[4,2] - A[1,1]*A[3,3]*A[4,2] - A[1,2]*A[3,1]*A[4,3] + A[1,1]*A[3,2]*A[4,3]) / determinant), 210 | 211 | 212 | ((A[1,3]*A[2,4]*A[4,2] - A[1,4]*A[2,3]*A[4,2] + A[1,4]*A[2,2]*A[4,3] - A[1,2]*A[2,4]*A[4,3] - A[1,3]*A[2,2]*A[4,4] + A[1,2]*A[2,3]*A[4,4]) / determinant, 213 | (A[1,4]*A[2,3]*A[4,1] - A[1,3]*A[2,4]*A[4,1] - A[1,4]*A[2,1]*A[4,3] + A[1,1]*A[2,4]*A[4,3] + A[1,3]*A[2,1]*A[4,4] - A[1,1]*A[2,3]*A[4,4]) / determinant, 214 | (A[1,2]*A[2,4]*A[4,1] - A[1,4]*A[2,2]*A[4,1] + A[1,4]*A[2,1]*A[4,2] - A[1,1]*A[2,4]*A[4,2] - A[1,2]*A[2,1]*A[4,4] + A[1,1]*A[2,2]*A[4,4]) / determinant, 215 | (A[1,3]*A[2,2]*A[4,1] - A[1,2]*A[2,3]*A[4,1] - A[1,3]*A[2,1]*A[4,2] + A[1,1]*A[2,3]*A[4,2] + A[1,2]*A[2,1]*A[4,3] - A[1,1]*A[2,2]*A[4,3]) / determinant), 216 | 217 | 218 | ((A[1,4]*A[2,3]*A[3,2] - A[1,3]*A[2,4]*A[3,2] - A[1,4]*A[2,2]*A[3,3] + A[1,2]*A[2,4]*A[3,3] + A[1,3]*A[2,2]*A[3,4] - A[1,2]*A[2,3]*A[3,4]) / determinant, 219 | (A[1,3]*A[2,4]*A[3,1] - A[1,4]*A[2,3]*A[3,1] + A[1,4]*A[2,1]*A[3,3] - A[1,1]*A[2,4]*A[3,3] - A[1,3]*A[2,1]*A[3,4] + A[1,1]*A[2,3]*A[3,4]) / determinant, 220 | (A[1,4]*A[2,2]*A[3,1] - A[1,2]*A[2,4]*A[3,1] - A[1,4]*A[2,1]*A[3,2] + A[1,1]*A[2,4]*A[3,2] + A[1,2]*A[2,1]*A[3,4] - A[1,1]*A[2,2]*A[3,4]) / determinant, 221 | (A[1,2]*A[2,3]*A[3,1] - A[1,3]*A[2,2]*A[3,1] + A[1,3]*A[2,1]*A[3,2] - A[1,1]*A[2,3]*A[3,2] - A[1,2]*A[2,1]*A[3,3] + A[1,1]*A[2,2]*A[3,3]) / determinant) 222 | ) 223 | end 224 | 225 | 226 | lyap{T}(a::Mat{1, 1, T}, c::Mat{1, 1, T}) = Mat{1,1,T}(lyap(a[1,1],c[1,1])) 227 | function lyap{T}(a::Mat{2, 2, T}, c::Mat{2, 2, T}) 228 | d = det(a) 229 | t = trace(a) 230 | -(d*c + (a - t*I)*c*(a-t*I)')/(2*d*t) # http://www.nber.org/papers/w8956.pdf 231 | end 232 | lyap{m,T}(a::Mat{m,m,T},c::Mat{m,m,T}) = Mat(lyap(Matrix(a),Matrix(c))) 233 | 234 | chol{T<:Base.LinAlg.BlasFloat}(m::Mat{1, 1, T}) = Mat{1,1,T}(chol(m[1,1])) 235 | function chol{T<:Base.LinAlg.BlasFloat}(m::Mat{2,2,T}) 236 | m[1,2]==m[2,1]' || error("Matrix not symmetric") 237 | l11 = chol(m[1,1]) 238 | @inbounds return Mat{2, 2, T}( 239 | (l11, zero(T)), 240 | (inv(l11)*m[1,2], chol(m[2,2] - m[2,1]*inv(m[1,1])*m[1,2])) 241 | ) 242 | end 243 | chol{n,T<:Base.LinAlg.BlasFloat}(m::Mat{n,n,T}) = Mat{n,n,T}(full(Base.LinAlg.chol!(Matrix(m)))) 244 | chol!(m::Mat, ::Type{UpperTriangular}) = chol(m) 245 | chol!(m::Mat, ::Type{Val{:U}}) = chol!(m, UpperTriangular) 246 | 247 | # Matrix 248 | (*){T, M, N, O, K}(a::FixedMatrix{M, N, T}, b::FixedMatrix{O, K, T}) = throw(DimensionMismatch("$N != $O in $(typeof(a)) and $(typeof(b))")) 249 | (*){T, M, N, O}(a::FixedMatrix{M, N, T}, b::FixedVector{O, T}) = throw(DimensionMismatch("$N != $O in $(typeof(a)) and $(typeof(b))")) 250 | 251 | @generated function *{T, N}(a::FixedVector{N, T}, b::FixedMatrix{1, N, T}) 252 | expr = Expr(:tuple, [Expr(:tuple, [:(a[$i] * b[$j]) for i in 1:N]...) for j in 1:N]...) 253 | :( Mat($(expr)) ) 254 | end 255 | 256 | @generated function *{T, M, N}(a::Mat{M, N, T}, b::FixedVectorNoTuple{N, T}) 257 | expr = [:(bilindot(row(a, $i), b)) for i=1:M] 258 | :( Vec{M, T}($(expr...))) 259 | end 260 | 261 | @generated function *{T, M, N}(a::Mat{M, N, T}, b::Vec{N,T}) 262 | expr = [:(bilindot(row(a, $i), b.(1))) for i=1:M] 263 | :( Vec($(expr...)) ) 264 | end 265 | @generated function *{T, M, N, R}(a::Mat{M, N, T}, b::Mat{N, R, T}) 266 | expr = Expr(:tuple, [Expr(:tuple, [:(bilindot(row(a, $i), column(b,$j))) for i in 1:M]...) for j in 1:R]...) 267 | :( Mat($(expr)) ) 268 | end 269 | 270 | 271 | function (==)(a::FixedVectorNoTuple, b::FixedVectorNoTuple) 272 | s_a = size(a) 273 | s_b = size(b) 274 | s_a == s_b || return false 275 | @inbounds for i = 1:length(a) 276 | a[i] == b[i] || return false 277 | end 278 | true 279 | end 280 | (==)(a::FixedArray, b::FixedArray) = a.(1) == b.(1) 281 | 282 | (==){R, T, FSA <: FixedVector}(a::FSA, b::Mat{R, 1, T}) = a.(1) == column(b,1) 283 | (==){R, T, FSA <: FixedVector}(a::Mat{R, 1, T}, b::FSA) = column(a,1) == b.(1) 284 | function (==)(a::FixedArray, b::AbstractArray) 285 | s_a = size(a) 286 | s_b = size(b) 287 | s_a == s_b || return false 288 | @inbounds for i = 1:length(a) 289 | a[i] == b[i] || return false 290 | end 291 | true 292 | end 293 | 294 | (==)(a::AbstractArray, b::FixedArray) = b == a 295 | 296 | # To support @test_approx_eq 297 | Base.Test.approx_full(a::FixedArray) = a 298 | 299 | # UniformScaling 300 | 301 | *(J::Base.LinAlg.UniformScaling, A::FixedArray) = J.λ*A 302 | *(A::FixedArray, J::Base.LinAlg.UniformScaling) = A*J.λ 303 | /(A::FixedArray, J::Base.LinAlg.UniformScaling) = A/J.λ 304 | 305 | +{m, n, T}(A::Mat{m,n, T}, J::Base.LinAlg.UniformScaling) = A + J.λ*eye(Mat{m,n,T}) 306 | +{m, n, T}(J::Base.LinAlg.UniformScaling, A::Mat{m,n, T}) = A + J 307 | -{m, n, T}(A::Mat{m,n, T}, J::Base.LinAlg.UniformScaling) = A + (-J) 308 | -{m, n, T}(J::Base.LinAlg.UniformScaling, A::Mat{m,n, T}) = J.λ*eye(Mat{m,n,T}) - A 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | module FSAtesting 2 | 3 | using FixedSizeArrays 4 | using FactCheck, Base.Test 5 | 6 | immutable Normal{N, T} <: FixedVector{N, T} 7 | _::NTuple{N, T} 8 | end 9 | immutable D3{N1, N2, N3, T} <: FixedArray{T, 3, Tuple{N1, N2, N3}} 10 | _::NTuple{N1, NTuple{N2, NTuple{N3, T}}} 11 | end 12 | immutable RGB{T} <: FixedVectorNoTuple{3, T} 13 | r::T 14 | g::T 15 | b::T 16 | function RGB(a::NTuple{3, T}) 17 | new{T}(a[1], a[2], a[3]) 18 | end 19 | end 20 | 21 | typealias Vec1d Vec{1, Float64} 22 | typealias Vec2d Vec{2, Float64} 23 | typealias Vec3d Vec{3, Float64} 24 | typealias Vec4d Vec{4, Float64} 25 | typealias Vec3f Vec{3, Float32} 26 | typealias Mat2d Mat{2,2, Float64} 27 | typealias Mat3d Mat{3,3, Float64} 28 | typealias Mat4d Mat{4,4, Float64} 29 | 30 | # Compatibility hacks for 0.5 APL-style array slicing 31 | if VERSION < v"0.5.0-dev+1195" 32 | # Remove all dimensions of size 1 33 | compatsqueeze(A) = squeeze(A,(find(collect(size(A)).==1)...)) 34 | else 35 | compatsqueeze(A) = A 36 | end 37 | 38 | facts("FixedSizeArrays") do 39 | 40 | context("fsa macro") do 41 | a = 1 42 | a1 = @fsa([a,2,3]) 43 | a2 = @fsa([a 2 3]) 44 | a3 = @fsa([a;2;3]) 45 | a4 = @fsa([a 2;3 4]) 46 | a5 = @fsa([a 2 3;4 5 6]) 47 | a6 = @fsa([a 2;3 4;5 6]) 48 | 49 | @fact a1 --> Vec(a,2,3) 50 | @fact a2 --> Mat((a,),(2,),(3,)) 51 | @fact a3 --> Mat(((a,2,3),)) 52 | @fact a4 --> Mat(((a,3),(2,4))) 53 | @fact a5 --> Mat(((a,4),(2,5),(3,6))) 54 | @fact a6 --> Mat(((a,3,5),(2,4,6))) 55 | end 56 | 57 | context("core") do 58 | context("ndims") do 59 | @fact ndims(D3) --> 3 60 | @fact ndims(Mat) --> 2 61 | @fact ndims(Vec) --> 1 62 | @fact ndims(Vec(1,2,3)) --> 1 63 | 64 | @fact ndims(D3{3,3,3}) --> 3 65 | @fact ndims(Mat{3,3}) --> 2 66 | @fact ndims(Vec{3}) --> 1 67 | 68 | @fact ndims(D3{3,3,3,Int}) --> 3 69 | @fact ndims(Mat{3,3,Int}) --> 2 70 | @fact ndims(Vec{3,Int}) --> 1 71 | end 72 | context("size_or") do 73 | @fact size_or(Mat, nothing) --> nothing 74 | @fact size_or(Mat{4}, nothing) --> nothing 75 | @fact size_or(Mat{4,4}, nothing) --> (4,4) 76 | @fact size_or(Mat{4,4, Float32}, nothing) --> (4,4) 77 | 78 | @fact size_or(Vec, nothing) --> nothing 79 | @fact size_or(Vec{4}, nothing) --> (4,) 80 | @fact size_or(Vec{4,Float32}, nothing) --> (4,) 81 | @fact size_or(FixedArray, nothing) --> nothing 82 | 83 | end 84 | context("eltype_or") do 85 | @fact eltype_or(Mat, nothing) --> nothing 86 | @fact eltype_or(Mat{4}, nothing) --> nothing 87 | @fact eltype_or(Mat{4,4}, nothing) --> nothing 88 | @fact eltype_or(Mat{4,4, Float32}, nothing) --> Float32 89 | 90 | @fact eltype_or(Vec, nothing) --> nothing 91 | @fact eltype_or(Vec{4}, nothing) --> nothing 92 | @fact eltype_or(Vec{4,Float32}, nothing) --> Float32 93 | 94 | @fact eltype_or(FixedArray, nothing) --> nothing 95 | 96 | end 97 | context("ndims_or") do 98 | @fact ndims_or(Mat, nothing) --> 2 99 | @fact ndims_or(Mat{4}, nothing) --> 2 100 | @fact ndims_or(Mat{4,4}, nothing) --> 2 101 | @fact ndims_or(Mat{4,4, Float32}, nothing) --> 2 102 | 103 | @fact ndims_or(Vec, nothing) --> 1 104 | @fact ndims_or(Vec{4}, nothing) --> 1 105 | @fact ndims_or(Vec{4, Float64}, nothing) --> 1 106 | 107 | @fact ndims_or(FixedArray, nothing) --> nothing 108 | end 109 | context("similar") do 110 | @fact similar(Vec{3}, Float32) --> Vec{3, Float32} 111 | @fact similar(Vec, Float32, 3) --> Vec{3, Float32} 112 | end 113 | 114 | end 115 | 116 | 117 | context("Array of FixedArrays") do 118 | 119 | N = 100 120 | a = Point{3, Float32}[Point{3, Float32}(0.7132) for i=1:N] 121 | b = RGB{Float32}[RGB{Float32}(52.293) for i=1:N] 122 | 123 | c = Point{3, Float64}[Point{3, Float64}(typemin(Float64)), a..., Point{3, Float64}(typemax(Float64))] 124 | d = RGB{Float64}[RGB(typemin(Float64)), b..., RGB(typemax(Float64))] 125 | 126 | context("reduce") do 127 | sa = sum(a) 128 | ma = mean(a) 129 | sb = sum(b) 130 | mb = mean(b) 131 | for i=1:3 132 | @fact sa[i] --> roughly(Float32(0.7132*N)) 133 | @fact ma[i] --> roughly(Float32(0.7132*N)/ N) 134 | 135 | @fact sb[i] --> roughly(Float32(52.293*N)) 136 | @fact mb[i] --> roughly(Float32(52.293*N)/ N) 137 | end 138 | 139 | @fact maximum(c) --> Point{3, Float32}(typemax(Float64)) 140 | @fact minimum(c) --> Point{3, Float32}(typemin(Float64)) 141 | 142 | @fact maximum(d) --> RGB(typemax(Float64)) 143 | @fact minimum(d) --> RGB(typemin(Float64)) 144 | end 145 | 146 | context("array ops") do 147 | af = a + 1f0 148 | bf = b + 1f0 149 | aff = a + Point{3, Float32}(1) 150 | bff = b + RGB{Float32}(1) 151 | afd = a .+ 1f0 152 | bfd = b .+ 1f0 153 | @inferred(b .* 1f0) 154 | for i=1:N 155 | @fact a[1] + 1f0 --> af[i] 156 | @fact b[1] + 1f0 --> bf[i] 157 | @fact a[1] + 1f0 --> aff[i] 158 | @fact b[1] + 1f0 --> bff[i] 159 | @fact a[1] + 1f0 --> afd[i] 160 | @fact b[1] + 1f0 --> bfd[i] 161 | end 162 | end 163 | context("Show") do 164 | m1 = rand(Mat4d, 2) 165 | m2 = rand(RGB{Float32}, 2) 166 | m3 = rand(Vec3f, 2) 167 | println(m1) 168 | println(m2) 169 | println(m3) 170 | showcompact(Point(1,2,3)) 171 | end 172 | end 173 | 174 | 175 | context("Constructor FixedVectorNoTuple") do 176 | for T=[Float32, Float64, Int, UInt, UInt32, UInt8] 177 | context("$T") do 178 | r = rand(T) 179 | x = RGB{Int}[RGB(1) for i=1:10] 180 | @fact RGB{Float32}(["0.222", "9.8822", "29.999"]) --> RGB{Float32}(0.222, 9.8822, 29.999) 181 | @fact RGB(["0.222", "9.8822", "29.999"]) --> RGB{Float64}(0.222, 9.8822, 29.999) 182 | @fact typeof(map(RGB{Float32}, x)) --> Vector{RGB{Float32}} 183 | @fact RGB{T}(r) --> RGB(r,r,r) 184 | @fact RGB{T}([r,r,r]) --> RGB(r,r,r) 185 | @fact length(RGB{T}([r,r,r])) --> 3 186 | @fact length(RGB{T}) --> 3 187 | @fact eltype(RGB{T}([r,r,r])) --> T 188 | @fact eltype(RGB{T}) --> T 189 | @fact typeof(RGB(r,r,r)) --> RGB{T} 190 | @fact typeof(RGB{T}(1)) --> RGB{T} 191 | @fact typeof(RGB{T}(1,2,3)) --> RGB{T} 192 | @fact ndims(RGB{T}(1,2,3)) --> 1 193 | 194 | @fact typeof(RGB{T}(1f0)) --> RGB{T} 195 | @fact typeof(RGB{T}(1f0,2f0,3f0)) --> RGB{T} 196 | @fact typeof(RGB{T}(1f0, 2, 3.0)) --> RGB{T} 197 | @fact typeof(RGB(1f0, 2, 3.0)) --> RGB{Float64} 198 | @fact typeof(RGB{Int}(1f0, 2, 3.0)) --> RGB{Int} 199 | @fact_throws DimensionMismatch RGB((1,2,3), (2,3,4)) 200 | end 201 | end 202 | end 203 | 204 | # A little brutal, but hey.... Better redudantant tests, than not enough tests 205 | context("Constructor ") do 206 | context("Rand") do 207 | #Win32 seems to fail for rand(Vec4d) 208 | @fact typeof(rand(Vec4d)) --> Vec4d 209 | @fact typeof(rand(Mat4d)) --> Mat4d 210 | 211 | @fact typeof(rand(Mat{4,2, Int})) --> Mat{4,2, Int} 212 | @fact typeof(rand(Vec{7, Int})) --> Vec{7, Int} 213 | @fact typeof(rand(Vec{7, Int}, 1:7)) --> Vec{7, Int} 214 | @fact typeof(rand(Mat4d, -20f0:0.192f0:230f0)) --> Mat4d 215 | @fact typeof(rand(Mat{4,21,Float32}, -20f0:0.192f0:230f0)) --> Mat{4,21,Float32} 216 | 217 | x = rand(D3{4,4,4, Float32}) 218 | @fact typeof(x) --> D3{4,4,4, Float32} 219 | @fact eltype(x) --> Float32 220 | @fact size(x) --> (4,4,4) 221 | @fact typeof(rand(Vec4d, 5,5)) --> Matrix{Vec4d} 222 | end 223 | context("Randn") do 224 | @fact typeof(randn(Base.Random.GLOBAL_RNG, Vec4d)) --> Vec4d 225 | @fact typeof(randn(Vec4d)) --> Vec4d 226 | @fact typeof(randn(Mat4d)) --> Mat4d 227 | @fact typeof(randn(Mat{4,2, Complex{Float64}})) --> Mat{4,2, Complex{Float64}} 228 | @fact typeof(randn(Vec{7, Complex{Float64}})) --> Vec{7, Complex{Float64}} 229 | end 230 | context("Zero") do 231 | @fact typeof(zero(Vec4d)) --> Vec4d 232 | @fact typeof(zero(Mat4d)) --> Mat4d 233 | 234 | @fact typeof(zero(Mat{4,2, Int})) --> Mat{4,2, Int} 235 | @fact typeof(zero(Vec{7, Int})) --> Vec{7, Int} 236 | @fact zero(Vec((1,2))) --> Vec((0,0)) 237 | @fact zero(Vec((1.0,2.0))) --> Vec((0.0,0.0)) 238 | end 239 | 240 | context("eye") do 241 | @fact typeof(eye(Mat4d)) --> Mat4d 242 | @fact typeof(eye(Mat{4,2, Int})) --> Mat{4,2, Int} 243 | end 244 | context("one") do 245 | x = one(Mat{4,2, Int}) 246 | @fact typeof(one(Mat4d)) --> Mat4d 247 | @fact typeof(x) --> Mat{4,2, Int} 248 | @fact all(x-> x==1, x) --> true 249 | end 250 | 251 | context("unit") do 252 | u4 = unit(Vec4d, 1) 253 | u7 = unit(Vec{7, Int}, 7) 254 | @fact typeof(u4) --> Vec4d 255 | @fact typeof(u7) --> Vec{7, Int} 256 | @fact u4[1] --> 1.0 257 | @fact u4[2:end] --> (0.,0.,0.) 258 | 259 | @fact u7[end] --> 1 260 | @fact u7[1:end-1] --> (0,0,0,0,0,0) 261 | end 262 | for N=(1,10) 263 | context("construction, conversion, $N") do 264 | for VT=[Point, Vec], VT2=[Normal, Vec], ET=[Float32, Int, UInt], ET2=[Float64, UInt, Float32] 265 | rand_range = ET(1):ET(10) 266 | rand_range2 = ET2(1):ET2(10) 267 | rn = rand(rand_range, N) 268 | v0 = VT(rn) 269 | # parse constructor: 270 | @fact VT{N, ET}(map(string, rn)) --> v0 271 | # multi constructor 272 | v1 = VT{N, ET}(rn...) 273 | @fact v1 --> v0 274 | @fact typeof(v1) --> VT{N, ET} 275 | @fact length(v1) --> N 276 | @fact eltype(v1) --> ET 277 | @fact ndims(v1) --> 1 278 | 279 | @fact length(typeof(v1)) --> N 280 | @fact eltype(typeof(v1)) --> ET 281 | 282 | for i=1:N 283 | @fact v1[i] --> rn[i] 284 | end 285 | # from other FSA without parameters 286 | v2 = VT2(v1) 287 | 288 | @fact typeof(v2) --> VT2{N, ET} 289 | @fact length(v2) --> N 290 | @fact eltype(v2) --> ET 291 | for i=1:N 292 | @fact v2[i] --> v1[i] 293 | end 294 | # from other FSA with parameters 295 | for i=1:N 296 | v3 = VT2{i, ET2}(v1) 297 | @fact typeof(v3) --> VT2{i, ET2} 298 | @fact length(v3) --> i 299 | @fact eltype(v3) --> ET2 300 | for i=1:i 301 | @fact v3[i] --> ET2(v2[i]) 302 | end 303 | end 304 | # from single 305 | r = rand(rand_range) 306 | r2 = rand(rand_range2) 307 | v1 = VT{N, ET}(r) 308 | v2 = VT{N, ET2}(r) 309 | v3 = VT{N, ET}(r2) 310 | v4 = VT{N, ET2}(r2) 311 | 312 | for i=1:N 313 | @fact v1[i] --> r 314 | @fact v2[i] --> ET2(r) 315 | @fact v3[i] --> r2 316 | @fact v4[i] --> ET2(r2) 317 | end 318 | x = VT{N, ET}[VT{N, ET}(1) for i=1:10] 319 | x1 = VT2{N, ET}[VT{N, ET}(1) for i=1:10] 320 | x2 = map(VT2, x) 321 | x3 = map(VT, x2) 322 | @fact typeof(x) --> Vector{VT{N, ET}} 323 | @fact typeof(x1) --> Vector{VT2{N, ET}} 324 | @fact typeof(x2) --> Vector{VT2{N, ET}} 325 | @fact typeof(x3) --> Vector{VT{N, ET}} 326 | @fact x3 --> x 327 | 328 | # Construction with only N, issue #56 329 | @fact VT{N}(ET(1)) --> Vec{N, ET}(1) 330 | @fact VT{N}(ntuple(x->ET(1), N)...) --> Vec{N, ET}(1) 331 | end 332 | end 333 | end 334 | end 335 | 336 | 337 | context("Constructors") do 338 | context("FixedVector: unary, from FixedVector") do 339 | @fact typeof(Vec3f(1,1,1)) --> Vec{3, Float32} 340 | @fact typeof(Vec3f(1,1f0,1)) --> Vec{3, Float32} 341 | @fact typeof(Vec3f(1f0,1,1.0)) --> Vec{3, Float32} 342 | @fact eltype(Vec3f(1f0,1,1.0)) --> Float32 343 | 344 | @fact typeof(Vec3f(1)) --> Vec{3, Float32} 345 | @fact typeof(Vec3f(0)) --> Vec{3, Float32} 346 | @fact Vec3f(1.0) --> Vec(1f0,1f0,1f0) 347 | @fact Vec3f(1.0f0) --> Vec(1f0,1f0,1f0) 348 | @fact Vec3f(1.0f0) --> Vec3f(1) 349 | @fact Vec(1.0, 1.0, 1.0) --> Vec3d(1) 350 | @fact Vec2d(Vec3d(1)) --> Vec(1.0, 1.0) 351 | @fact Vec(Vec3d(1), 1.0) --> Vec4d(1) 352 | @fact Vec(Vec3d(1), 1) --> Vec4d(1) 353 | @fact Vec3d(Vec3f(1.0)) --> Vec3d(1.0) 354 | end 355 | end 356 | 357 | 358 | 359 | 360 | v2 = Vec(6.0,5.0,4.0) 361 | v1 = Vec(1.0,2.0,3.0) 362 | v2 = Vec(6.0,5.0,4.0) 363 | v1c = Vec(6.0+3.im,5.0-2im,4.0+0.im) 364 | v2c = v1 + v2*im 365 | v2c = Vec(1.0 + 6.0im, 2.0 + 5.0im, 3.0 + 4.0im) 366 | 367 | context("Complex Ops") do 368 | context("dot product") do 369 | @fact dot(v1c,v2c) --> dot([6.0+3.im,5.0-2im,4.0+0.im], [1.0,2.0,3.0] + [6.0,5.0,4.0]*im) 370 | @fact Vector(transpose(v1c)*v2c) --> [6.0+3.im 5.0-2im 4.0+0.im]*([1.0,2.0,3.0] + [6.0,5.0,4.0]*im) 371 | @fact Matrix(v2c*transpose(v1c)) --> ([1.0,2.0,3.0] + [6.0,5.0,4.0]*im)*[6.0+3.im 5.0-2im 4.0+0.im] 372 | end 373 | end 374 | 375 | context("Destructure") do 376 | rgb_ref = [1 2 3 4; 377 | 2 4 6 8; 378 | 3 6 9 12] 379 | rgb_ref_set = [1 10 10 4; 380 | 2 10 10 8; 381 | 3 10 10 12] 382 | # Test destructure 383 | rgb = [RGB(i,2*i,3*i) for i=1:4] 384 | @fact destructure(rgb) --> rgb_ref 385 | destructure(rgb)[:,2:end-1] = 10 386 | @fact destructure(rgb) --> rgb_ref_set 387 | 388 | # Explicitly test DestructuredArray. This wrapper type isn't used by 389 | # destructure() for plain old dense arrays, since a reinterpret is faster. 390 | rgb = [RGB(i,2*i,3*i) for i=1:4] 391 | @fact FixedSizeArrays.DestructuredArray(rgb) --> rgb_ref 392 | destructure(rgb)[:,2:end-1] = 10 393 | @fact FixedSizeArrays.DestructuredArray(rgb) --> rgb_ref_set 394 | 395 | # destructure() with 2D FSA 396 | A = [@fsa([i 2*i; 3*i 4*i]) for i=1:2] 397 | @fact destructure(A) --> cat(3, [1 2; 3 4], [2 4; 6 8]) 398 | end 399 | 400 | context("Indexing") do 401 | context("FixedVector") do 402 | @fact setindex(v1, 88.9, 1) --> Vec(88.9,2.0,3.0) 403 | @fact v1[1] --> 1.0 404 | @fact v1[2] --> 2.0 405 | @fact v1[3] --> 3.0 406 | @fact v1[1:3] --> (1.0, 2.0, 3.0) 407 | @fact v1[1:2] --> (1.0, 2.0) 408 | @fact v1[1:1] --> (1.0,) 409 | @fact v1[(1,2)] --> (1.0,2.0) 410 | @fact v1[(2,1)] --> (2.0,1.0) 411 | @fact_throws BoundsError v1[-1] 412 | @fact_throws BoundsError v1[0] 413 | @fact_throws BoundsError v1[4] 414 | @fact row(v1, 1) --> (1.0,) 415 | end 416 | m = Mat{4,4,Int}( 417 | (1,2,3,4), 418 | (5,6,7,8), 419 | (9,10,11,12), 420 | (13,14,15,16) 421 | ) 422 | context("FixedMatrix") do 423 | @fact setindex(m, 42.0, 2,2) --> Mat{4,4,Int}( 424 | (1,2,3,4), 425 | (5,42.0,7,8), 426 | (9,10,11,12), 427 | (13,14,15,16) 428 | ) 429 | @fact m[1] --> 1 430 | @fact m[2] --> 2 431 | @fact m[10] --> 10 432 | @fact m[2,2] --> 6 433 | @fact m[3,4] --> 15 434 | @fact m[1:4, 1] --> (1,5,9,13) 435 | @fact m[1, 1:4] --> (1,2,3,4) 436 | @fact_throws BoundsError m[-1] 437 | @fact_throws BoundsError m[0] 438 | @fact_throws BoundsError m[17] 439 | @fact_throws BoundsError m[5,1] 440 | @fact_throws BoundsError m[-1,1] 441 | @fact_throws BoundsError m[0,0] 442 | 443 | @fact row(m, 1) --> (1,5,9,13) 444 | 445 | 446 | 447 | end 448 | 449 | context("fslice") do 450 | context("getindex") do 451 | rgb = [RGB(i,2*i,3*i) for i=1:10] 452 | 453 | # Plain indexing 454 | @fact @fslice(rgb[1,2]) --> rgb[2].r 455 | @fact @fslice(rgb[2,5]) --> rgb[5].g 456 | @fact @fslice(rgb[3,8]) --> rgb[8].b 457 | 458 | # Slicing along fixed dims 459 | @fact @fslice(rgb[:,1]) --> rgb[1] 460 | @fact @fslice(rgb[:,end]) --> rgb[end] 461 | 462 | # Slicing across fixed dims 463 | @fact compatsqueeze(@fslice(rgb[1,:])) --> [c.r for c in rgb] 464 | @fact compatsqueeze(@fslice(rgb[2,:])) --> [c.g for c in rgb] 465 | @fact compatsqueeze(@fslice(rgb[3,:])) --> [c.b for c in rgb] 466 | # Slicing across fixed dims with field names 467 | @fact compatsqueeze(@fslice(rgb[:r,:])) --> [c.r for c in rgb] 468 | @fact compatsqueeze(@fslice(rgb[:g,:])) --> [c.g for c in rgb] 469 | @fact compatsqueeze(@fslice(rgb[:b,:])) --> [c.b for c in rgb] 470 | 471 | # Slicing FSAs with two fixed dimensions 472 | N = 3 473 | A = Mat{2,2,Int}[@fsa([i 2*i; 3*j 4*j]) for i=1:N, j=1:N] 474 | for i=1:N,j=1:N 475 | @fact compatsqueeze(@fslice(A[:,:,i,j])) --> A[i,j] 476 | end 477 | @fact compatsqueeze(@fslice(A[1,1,:,1])) --> [A[i,1][1,1] for i=1:N] 478 | @fact compatsqueeze(@fslice(A[end,end,end,:])) --> [A[end,j][end,end] for j=1:N] 479 | @fact compatsqueeze(@fslice(A[1,[1,end],1,1])) --> [A[1,1][1,1], A[1,1][1,end]] 480 | end 481 | 482 | context("setindex") do 483 | rgb = [RGB(i,2*i,3*i) for i=1:10] 484 | 485 | @fslice rgb[:r,:] = -1 486 | @fslice rgb[3,:] = -3 487 | @fact rgb --> [RGB(-1,2*i,-3) for i=1:10] 488 | end 489 | end 490 | end 491 | 492 | 493 | 494 | context("Ops") do 495 | context("Negation") do 496 | @fact @inferred(-v1) --> Vec(-1.0,-2.0,-3.0) 497 | @fact isa(-v1, Vec3d) --> true 498 | end 499 | 500 | context("Negation") do 501 | @fact @inferred(v1+v2) --> Vec3d(7.0,7.0,7.0) 502 | end 503 | context("Negation") do 504 | @fact @inferred(v2-v1) --> Vec3d(5.0,3.0,1.0) 505 | end 506 | context("Multiplication") do 507 | @fact @inferred(v1.*v2) --> Vec3d(6.0,10.0,12.0) 508 | end 509 | context("Division") do 510 | @fact @inferred(v1 ./ v1) --> Vec3d(1.0,1.0,1.0) 511 | end 512 | 513 | context("Scalar") do 514 | @fact @inferred(1.0 + v1) --> Vec3d(2.0,3.0,4.0) 515 | @fact @inferred(1.0 .+ v1) --> Vec3d(2.0,3.0,4.0) 516 | @fact @inferred(v1 + 1.0) --> Vec3d(2.0,3.0,4.0) 517 | @fact @inferred(v1 .+ 1.0) --> Vec3d(2.0,3.0,4.0) 518 | @fact @inferred(1 + v1) --> Vec3d(2.0,3.0,4.0) 519 | @fact @inferred(1 .+ v1) --> Vec3d(2.0,3.0,4.0) 520 | @fact @inferred(v1 + 1) --> Vec3d(2.0,3.0,4.0) 521 | @fact @inferred(v1 .+ 1) --> Vec3d(2.0,3.0,4.0) 522 | 523 | @fact @inferred(v1 - 1.0) --> Vec3d(0.0,1.0,2.0) 524 | @fact @inferred(v1 .- 1.0) --> Vec3d(0.0,1.0,2.0) 525 | @fact @inferred(1.0 - v1) --> Vec3d(0.0,-1.0,-2.0) 526 | @fact @inferred(1.0 .- v1) --> Vec3d(0.0,-1.0,-2.0) 527 | @fact @inferred(v1 - 1) --> Vec3d(0.0,1.0,2.0) 528 | @fact @inferred(v1 .- 1) --> Vec3d(0.0,1.0,2.0) 529 | @fact @inferred(1 - v1) --> Vec3d(0.0,-1.0,-2.0) 530 | @fact @inferred(1 .- v1) --> Vec3d(0.0,-1.0,-2.0) 531 | 532 | @fact @inferred(2.0 * v1) --> Vec3d(2.0,4.0,6.0) 533 | @fact @inferred(2.0 .* v1) --> Vec3d(2.0,4.0,6.0) 534 | @fact @inferred(v1 * 2.0) --> Vec3d(2.0,4.0,6.0) 535 | @fact @inferred(v1 .* 2.0) --> Vec3d(2.0,4.0,6.0) 536 | @fact @inferred(2 * v1) --> Vec3d(2.0,4.0,6.0) 537 | @fact @inferred(2 .* v1) --> Vec3d(2.0,4.0,6.0) 538 | @fact @inferred(v1 * 2) --> Vec3d(2.0,4.0,6.0) 539 | @fact @inferred(v1 .* 2) --> Vec3d(2.0,4.0,6.0) 540 | 541 | @fact @inferred(v1 / 2.0) --> Vec3d(0.5,1.0,1.5) 542 | @fact @inferred(v1 ./ 2.0) --> Vec3d(0.5,1.0,1.5) 543 | @fact @inferred(v1 / 2) --> Vec3d(0.5,1.0,1.5) 544 | @fact @inferred(v1 ./ 2) --> Vec3d(0.5,1.0,1.5) 545 | 546 | @fact @inferred(12.0 ./ v1) --> Vec3d(12.0,6.0,4.0) 547 | @fact @inferred(12 ./ v1) --> Vec3d(12.0,6.0,4.0) 548 | 549 | @fact @inferred((v1 .^ 2)) --> Vec3d(1.0,4.0,9.0) 550 | @fact @inferred((v1 .^ 2.0)) --> Vec3d(1.0,4.0,9.0) 551 | @fact @inferred((2.0 .^ v1)) --> Vec3d(2.0,4.0,8.0) 552 | @fact @inferred((2 .^ v1)) --> Vec3d(2.0,4.0,8.0) 553 | 554 | a = Vec(3.2f0) 555 | @fact @inferred(a+0.2) --> Vec1d(3.2f0+0.2) 556 | @fact @inferred(0.2+a) --> Vec1d(3.2f0+0.2) 557 | @fact @inferred(a*0.2) --> Vec1d(3.2f0*0.2) 558 | @fact @inferred(0.2*a) --> Vec1d(3.2f0*0.2) 559 | @fact @inferred(a+0.2f0) --> Vec{1,Float32}(3.4f0) 560 | @fact @inferred(0.2f0+a) --> Vec{1,Float32}(3.4f0) 561 | @fact @inferred(a*0.2f0) --> Vec{1,Float32}(3.2f0*0.2f0) 562 | @fact @inferred(0.2f0*a) --> Vec{1,Float32}(3.2f0*0.2f0) 563 | end 564 | context("vector norm+cross product") do 565 | @fact norm(Vec3d(1.0,2.0,2.0)) --> 3.0 566 | 567 | # cross product 568 | @fact cross(v1,v2) --> Vec3d(-7.0,14.0,-7.0) 569 | @fact isa(cross(v1,v2),Vec3d) --> true 570 | end 571 | 572 | context("hypot") do 573 | a = Vec{2,Int}(1,2) 574 | b = Vec{2,Float64}(1.,2.) 575 | @fact hypot(a) --> 2.23606797749979 576 | @fact hypot(b) --> 2.23606797749979 577 | @fact hypot(a) == hypot(b) --> true 578 | end 579 | context("normalize") do 580 | a = Vec(3,4) 581 | b = Vec(3.,4.) 582 | @fact normalize(a) --> Vec(0.6,0.8) 583 | @fact normalize(b) --> Vec(0.6,0.8) 584 | end 585 | 586 | context("reduce") do 587 | a = rand(Vec{7, Float32}) 588 | x = reduce(Base.AddFun(), a) 589 | y = 0f0 590 | for elem in a 591 | y += elem 592 | end 593 | @fact y --> x 594 | 595 | a = rand(Mat{7, 9, Cuint}) 596 | x2 = reduce(Base.AddFun(), a) 597 | y2 = Cuint(0) 598 | for elem in a 599 | y2 += elem 600 | end 601 | @fact y2 --> x2 602 | end 603 | end 604 | 605 | 606 | context("Promotion") do 607 | @fact promote_type(Vec{2,Float64}, Int) --> Vec{2,Float64} 608 | end 609 | 610 | 611 | # type conversion 612 | context("Conversion 2") do 613 | @fact isa(convert(Vec3f,v1), Vec3f) --> true 614 | 615 | @fact isa(convert(Vector{Float64}, v1), Vector{Float64}) --> true 616 | @fact convert(Vector{Float64}, v1) --> [1.0,2.0,3.0] 617 | end 618 | 619 | for T in [UInt, Int, Float32, Float64] 620 | context("Conversion to Vec{N,$T}") do 621 | X = map(T, (1,2,3,4,5)) 622 | 623 | context("single value conversion") do 624 | x = X[1] 625 | for N in 1:4 626 | @fact convert(Vec{N,T}, x) --> Vec{N,T}(repeated(x,N)...) 627 | end 628 | end 629 | 630 | context("conversion from vararg, tuple & array") do 631 | for N in 1:4 632 | tup = X[1:N] 633 | arr = [tup...] 634 | @fact convert(Vec{N,T}, tup...) --> Vec{N,T}(tup...) "Vec{$N,$T} from vararg" 635 | @fact convert(Vec{N,T}, tup) --> Vec{N,T}(tup...) "Vec{$N,$T} from tuple" 636 | @fact convert(Vec{N,T}, arr) --> Vec{N,T}(tup...) "Vec{$N,$T} from array" 637 | @fact convert(Vec, tup...) --> Vec{N,T}(tup...) "Vec from vararg" 638 | @fact convert(Vec, tup) --> Vec{N,T}(tup...) "Vec from tuple" 639 | @fact convert(Vec, arr) --> Vec{N,T}(tup...) "Vec from array" 640 | end 641 | end 642 | 643 | context("conversion from too many args should fail") do 644 | for N in 1:4 645 | tup = X[1:N+1] 646 | arr = [tup...] 647 | @fact_throws convert(Vec{N,T}, tup...) 648 | @fact_throws convert(Vec{N,T}, tup) 649 | @fact_throws convert(Vec{N,T}, arr) 650 | end 651 | end 652 | 653 | context("conversion from too few args should fail") do 654 | for N in 3:5 655 | tup = X[1:N-1] 656 | arr = [tup...] 657 | @fact_throws convert(Vec{N,T}, tup...) 658 | @fact_throws convert(Vec{N,T}, tup) 659 | @fact_throws convert(Vec{N,T}, arr) 660 | end 661 | end 662 | end 663 | end 664 | 665 | # matrix operations 666 | 667 | #typealias Mat1d Matrix1x1{Float64} 668 | 669 | 670 | zeromat = Mat2d((0.0,0.0),(0.0,0.0)) 671 | 672 | 673 | 674 | 675 | context("Matrix") do 676 | @fact map(Float64, zeromat) --> zeromat 677 | @fact length(Mat2d) --> 4 678 | @fact length(zeromat) --> 4 679 | 680 | @fact size(Mat2d) --> (2,2) 681 | @fact size(zeromat) --> (2,2) 682 | 683 | @fact zero(Mat2d) --> zeromat 684 | 685 | for i=1:4, j=1:4 686 | x1 = rand(i,j) 687 | @fact @inferred(ctranspose(Mat(x1))) --> Mat(x1') 688 | end 689 | 690 | 691 | v = Vec(1.0,2.0,3.0,4.0) 692 | r = row(v) 693 | c = column(v) 694 | 695 | #@fact r' --> c 696 | #@fact c' --> r 697 | 698 | a = c*r 699 | 700 | b = Mat( 701 | (1.0,2.0,3.0,4.0), 702 | (2.0,4.0,6.0,8.0), 703 | (3.0,6.0,9.0,12.0), 704 | (4.0,8.0,12.0,16.0) 705 | ) 706 | 707 | x = Mat( 708 | (1,1,1,), 709 | (2,2,2,), 710 | (3,3,3,), 711 | ) 712 | @fact transpose(x) --> Mat( 713 | (1,2,3), 714 | (1,2,3), 715 | (1,2,3), 716 | ) 717 | @fact transpose(b) --> b 718 | 719 | @fact length(b) --> 16 720 | 721 | @fact a-->b 722 | mat30 = Mat(((30.0,),)) 723 | @fact r*c --> mat30 724 | 725 | 726 | #@fact row(r, 1) --> v 727 | #@fact column(c,1) --> v 728 | #@fact row(r+c',1) --> 2*v 729 | @fact sum(r) --> sum(v) 730 | @fact prod(c) --> prod(v) 731 | 732 | @fact eye(Mat3d) --> Mat((1.0,0.0,0.0), 733 | (0.0,1.0,0.0), 734 | (0.0,0.0,1.0)) 735 | #@fact v*eye(Mat4d)*v --> 30.0 736 | @fact -r --> -1.0*r 737 | #@fact diag(diagm(v)) --> v 738 | 739 | # type conversion 740 | #@fact isa(convert(Matrix1x4{Float32},r),Matrix1x4{Float32}) 741 | jm = rand(4,4) 742 | im = Mat(jm) 743 | for i=1:4*2 744 | @fact jm[i] --> im[i] 745 | end 746 | #im = Matrix4x4(jm) 747 | @fact isa(im, Mat4d) --> true 748 | 749 | jm2 = convert(Array{Float64,2}, im) 750 | @fact isa(jm2, Array{Float64,2}) --> true 751 | @fact jm --> jm2 752 | 753 | #Single valued constructor 754 | @fact Mat4d(0.0) --> zero(Mat4d) 755 | 756 | a = Vec4d(0) 757 | b = Vec4d(0,0,0,0) 758 | @fact a --> b 759 | 760 | v = rand(4) 761 | m = rand(4,4) 762 | vfs = Vec(v) 763 | mfs = Mat(m) 764 | @fact typeof(vfs) --> Vec4d 765 | @fact typeof(mfs) --> Mat4d 766 | 767 | # issue #65, wrong 768 | a = Mat((1,2), (3,4)) 769 | @fact Mat(a) --> a 770 | b = Mat([1,2,3,4]) 771 | @fact b --> Mat((1,2,3,4)) 772 | @fact b --> Mat([1,2,3,4]'') 773 | end 774 | context("Matrix Math") do 775 | for i=1:4, j=1:4 776 | v = rand(j) 777 | m = rand(i,j) 778 | m2 = rand(i,j) 779 | mc = rand(i,j) + im*rand(i,j) 780 | vfs = Vec(v) 781 | mfs = Mat(m) 782 | m2fs = Mat(m2) 783 | mfsc = Mat(mc) 784 | 785 | context("Matrix{$i, $j} * Vector{$j}") do 786 | vm = m * v 787 | @fact isapprox(@inferred(mfs * vfs), vm) --> true 788 | end 789 | context("Matrix{$i, $j} * Matrix{$j, $i}") do 790 | mm = m * m2' 791 | @fact isapprox(@inferred(mfs * m2fs'), mm) --> true 792 | end 793 | context("Matrix{$i, $j}*(2I)") do 794 | mm = m*(2) 795 | @fact isapprox(@inferred(m*(2I)), mm) --> true 796 | end 797 | 798 | if i == j 799 | context("(2*I + I*M)\\v") do 800 | mm = (2*I+I*m) \ v 801 | @fact isapprox(@inferred((2*I+I*mfs) \ vfs), mm) --> true 802 | end 803 | context("det(M)") do 804 | mm = det(m) 805 | fmm = det(mfs) 806 | @fact isapprox(fmm, mm) --> true 807 | end 808 | context("trace(M)") do 809 | mm = trace(m) 810 | fmm = trace(mfs) 811 | @fact isapprox(fmm, mm) --> true 812 | end 813 | context("inv(M)") do 814 | mm = inv(m) 815 | fmm = inv(mfs) 816 | @fact isapprox(fmm, mm) --> true 817 | end 818 | context("expm(M)") do 819 | mm = expm(m) 820 | fmm = expm(mfs) 821 | @fact isapprox(fmm, mm) --> true 822 | 823 | mm = expm(mc) 824 | fmm = expm(mfsc) 825 | @fact isapprox(fmm, mm) --> true 826 | end 827 | context("lyap(M,M2*M2')") do 828 | mm = lyap(m, m2*m2') 829 | fmm = lyap(mfs, m2fs*m2fs') 830 | @fact isapprox(fmm, mm) --> true 831 | end 832 | context("chol(M2*M2')") do 833 | mm = full(chol(m2*m2')) 834 | mm2 = full(chol(map(Mat, m2*m2'))) # Matrix of Mat's 835 | fmm = chol(m2fs*m2fs') 836 | @fact isapprox(fmm, mm) --> true 837 | @fact isapprox(mm, map(first, mm2)) --> true 838 | 839 | end 840 | 841 | else 842 | context("Matrix{$i, $j} * Matrix{$i, $j}") do 843 | @fact_throws DimensionMismatch mfs * mfs 844 | end 845 | end 846 | 847 | context("transpose M") do 848 | mm = m' 849 | fmm = mfs' 850 | @fact isapprox(fmm, mm) --> true 851 | end 852 | 853 | context("ctranspose M") do 854 | mm = mc' 855 | fmm = mfsc' 856 | @fact isapprox(fmm, mm) --> true 857 | end 858 | end 859 | context("expm(M::Mat{3,3,Float64})") do 860 | # in practice the precision is eps(), if m has not a triple eigenvalue 861 | for i in 1:30 862 | m = (rand(0:1,3,3).*randn(3,3) .+ rand(-3:3,3,3)) # some entries are natural numbers to have higher chance of multiple eigenvalues to trigger all branches 863 | @fact norm(Matrix(expm(Mat(m))) - expm(m))/norm(expm(m)) <= 1E-9 --> true 864 | m = m + m' # symmetric 865 | @fact norm(Matrix(expm(Mat(m))) - expm(m))/norm(expm(m)) <= 1E-9 --> true 866 | m = 1. *rand(-1:1,3,3) # eigenvalues equal with high probability to test worse case 867 | @fact norm(Matrix(expm(Mat(m))) - expm(m))/norm(expm(m)) <= 1E-9 --> true 868 | m = m + m' 869 | @fact norm(Matrix(expm(Mat(m))) - expm(m))/norm(expm(m)) <= 1E-9 --> true 870 | end 871 | end 872 | context("expm(M::Mat{3,3, BigFloat})") do 873 | @fact norm(Matrix(expm(Mat(big([0.0 0.0 1.0; -1.0 1.0 0.0; -1.0 0.0 2.0])))) - big([-0.0 0.0 1.0; -0.5 1.0 -0.5; -1.0 0.0 2.0])*e,1) < 10eps(big(1.)) --> true 874 | end 875 | 876 | context("Matrix * FixedVectorNoTuple") do 877 | rgb = rand(3) 878 | m = rand(3,3) 879 | rgbfs = RGB(rgb) 880 | mfs = Mat(m) 881 | @fact isapprox(mfs * rgbfs, m * rgb) --> true 882 | end 883 | 884 | end 885 | 886 | 887 | 888 | 889 | 890 | ac = rand(3) 891 | bc = rand(3) 892 | 893 | a = rand(4) 894 | b = rand(4) 895 | c = rand(4,4) 896 | 897 | d = cross(ac, bc) 898 | d2 = a+b 899 | f = c*a 900 | g = c*b 901 | h = c*f 902 | i = dot(f, a) 903 | j = dot(a, g) 904 | k = abs(f) 905 | l = abs(-f) 906 | 907 | acfs = Vec(ac) 908 | bcfs = Vec(bc) 909 | 910 | afs = Vec(a) 911 | bfs = Vec(b) 912 | cfs = Mat(c) 913 | 914 | dfs = cross(acfs, bcfs) 915 | d2fs = afs+bfs 916 | ffs = cfs*afs 917 | gfs = cfs*bfs 918 | hfs = cfs*ffs 919 | ifs = dot(ffs, afs) 920 | jfs = dot(afs, gfs) 921 | kfs = abs(ffs) 922 | lfs = abs(-ffs) 923 | 924 | context("Meta") do 925 | sym, expr = FixedSizeArrays.gen_functor(:+, 2) 926 | @fact typeof(sym) --> Symbol 927 | @fact typeof(expr) --> Expr 928 | end 929 | 930 | context("Vector Math") do 931 | context("all") do 932 | @fact isapprox(acfs, ac) --> true 933 | @fact isapprox(bcfs, bc) --> true 934 | 935 | @fact isapprox(afs, a) --> true 936 | @fact isapprox(bfs, b) --> true 937 | @fact isapprox(cfs, c) --> true 938 | 939 | @fact isapprox(dfs, d) --> true 940 | @fact isapprox(d2fs, d2) --> true 941 | @fact isapprox(ffs, f) --> true 942 | @fact isapprox(gfs, g) --> true 943 | @fact isapprox(hfs, h) --> true 944 | @fact isapprox(ifs, i) --> true 945 | @fact isapprox(jfs, j) --> true 946 | @fact isapprox(kfs, k) --> true 947 | @fact isapprox(lfs, l) --> true 948 | @fact isapprox(lfs, lfs) --> true 949 | end 950 | end 951 | 952 | context("Equality") do 953 | @fact Vec{3, Int}(1) --> Vec{3, Float64}(1) 954 | @fact Vec{2, Int}(1) --> not(Vec{3, Float64}(1)) 955 | @fact Vec(1,2,3) --> Vec(1.0,2.0,3.0) 956 | @fact Vec(1,2,3) --> not(Vec(1.0,4.0,3.0)) 957 | @fact Vec(1,2,3) --> [1,2,3] 958 | @fact Mat((1,2),(3,4)) --> Mat((1,2),(3,4)) 959 | @fact one(Mat{4,1, Float32}) --> one(Vec{4, Float32}) 960 | end 961 | #= 962 | #don't have this yet 963 | let 964 | a = rand(16) 965 | b = Mat4d(a) 966 | @fact b --> reshape(a, (4,4)) 967 | @fact reshape(a, (4,4)) --> b 968 | @fact b --> not(reshape(a, (2,8))) 969 | end 970 | =# 971 | 972 | const unaryOps = ( 973 | -, ~, conj, abs, 974 | sin, cos, tan, sinh, cosh, tanh, 975 | asin, acos, atan, asinh, acosh, atanh, 976 | sec, csc, cot, asec, acsc, acot, 977 | sech, csch, coth, asech, acsch, acoth, 978 | sinc, cosc, cosd, cotd, cscd, secd, 979 | sind, tand, acosd, acotd, acscd, asecd, 980 | asind, atand, rad2deg, deg2rad, 981 | log, log2, log10, log1p, exponent, exp, 982 | exp2, expm1, cbrt, sqrt, erf, 983 | erfc, erfcx, erfi, dawson, 984 | 985 | #trunc, round, ceil, floor, #see JuliaLang/julia#12163 986 | significand, lgamma, 987 | gamma, lfact, frexp, modf, airy, airyai, 988 | airyprime, airyaiprime, airybi, airybiprime, 989 | besselj0, besselj1, bessely0, bessely1, 990 | eta, zeta, digamma 991 | ) 992 | 993 | # vec-vec and vec-scalar 994 | const binaryOps = ( 995 | .+, .-,.*, ./, .\, /, 996 | .==, .!=, .<, .<=, .>, .>=, +, -, 997 | min, max, 998 | 999 | atan2, besselj, bessely, hankelh1, hankelh2, 1000 | besseli, besselk, beta, lbeta 1001 | ) 1002 | 1003 | 1004 | 1005 | 1006 | context("mapping operators") do 1007 | context("binary: ") do 1008 | test1 = (Vec(1,2,typemax(Int)), Mat((typemin(Int),2,5), (2,3,5), (-2,3,6)), Vec{4, Float32}(0.777)) 1009 | test2 = (Vec(1,0,typemax(Int)), Mat((typemin(Int),77,1), (2,typemax(Int),5), (-2,3,6)), Vec{4, Float32}(-23.2929)) 1010 | for op in binaryOps 1011 | for i=1:length(test1) 1012 | v1 = test1[i] 1013 | v2 = test2[i] 1014 | context("$op with $v1 and $v2") do 1015 | try # really bad tests, but better than nothing... 1016 | if applicable(op, v1[1], v2[1]) && typeof(op(v1[1], v2[1])) == eltype(v1) 1017 | r = op(v1, v2) 1018 | for j=1:length(v1) 1019 | @fact r[j] --> op(v1[j], v2[j]) 1020 | end 1021 | 1022 | end 1023 | end 1024 | end 1025 | end 1026 | end 1027 | end 1028 | context("unary: ") do 1029 | test = (Vec(1,2,typemax(Int)), Mat((typemin(Int),2,5), (2,3,5), (-2,3,6)), Vec{4, Float32}(0.777)) 1030 | for op in unaryOps 1031 | for t in test 1032 | context("$op with $t") do 1033 | try 1034 | if applicable(op, t[1]) && typeof(op(t[1])) == eltype(t) 1035 | v = op(t) 1036 | for i=1:length(v) 1037 | @fact v[i] --> op(t[i]) 1038 | end 1039 | end 1040 | end 1041 | end 1042 | end 1043 | end 1044 | end 1045 | end 1046 | 1047 | context("Base.Test") do 1048 | a = rand(2) 1049 | @fact Base.Test.@test_approx_eq(a, Vec(a)) --> nothing 1050 | end 1051 | 1052 | end 1053 | 1054 | end 1055 | 1056 | FactCheck.exitstatus() 1057 | --------------------------------------------------------------------------------