/
dimensions.jl
93 lines (78 loc) · 2.64 KB
/
dimensions.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""
```
*(a0::Dimensions, a::Dimensions...)
```
Given however many dimensions, multiply them together.
Collect [`Unitful.Dimension`](@ref) objects from the type parameter of the
[`Unitful.Dimensions`](@ref) objects. For identical dimensions, collect powers
and sort uniquely by the name of the `Dimension`.
Examples:
```jldoctest
julia> u"𝐌*𝐋/𝐓^2"
𝐋 𝐌 𝐓^-2
julia> u"𝐋*𝐌/𝐓^2"
𝐋 𝐌 𝐓^-2
julia> typeof(u"𝐋*𝐌/𝐓^2") == typeof(u"𝐌*𝐋/𝐓^2")
true
```
"""
@generated function *(a0::Dimensions, a::Dimensions...)
# Implementation is very similar to *(::Units, ::Units...)
b = Vector{Dimension}()
a0p = a0.parameters[1]
length(a0p) > 0 && append!(b, a0p)
for x in a
xp = x.parameters[1]
length(xp) > 0 && append!(b, xp)
end
sort!(b, by=x->power(x))
sort!(b, by=x->name(x))
c = Vector{Dimension}()
if !isempty(b)
next = iterate(b)
p = 0//1
oldvalue = next[1]
while next !== nothing
(value, state) = next
if name(value) == name(oldvalue)
p += power(value)
else
if p != 0
push!(c, Dimension{name(oldvalue)}(p))
end
p = power(value)
end
oldvalue = value
next = iterate(b, state)
end
if p != 0
push!(c, Dimension{name(oldvalue)}(p))
end
end
d = (c...,)
:(Dimensions{$d}())
end
/(x::Dimensions, y::Dimensions) = *(x,inv(y))
//(x::Dimensions, y::Dimensions) = x/y
# Both methods needed for ambiguity resolution
^(x::Dimension{T}, y::Integer) where {T} = Dimension{T}(power(x)*y)
^(x::Dimension{T}, y::Number) where {T} = Dimension{T}(power(x)*y)
# A word of caution:
# Exponentiation is not type-stable for `Dimensions` objects in many cases
^(x::Dimensions{T}, y::Integer) where {T} = *(Dimensions{map(a->a^y, T)}())
^(x::Dimensions{T}, y::Number) where {T} = *(Dimensions{map(a->a^y, T)}())
@generated function Base.literal_pow(::typeof(^), x::Dimensions{T}, ::Val{p}) where {T,p}
z = *(Dimensions{map(a->a^p, T)}())
:($z)
end
# Since exponentiation is not type stable, we define a special `inv` method to enable fast
# division. For julia 0.6.0, the appropriate methods for ^ and * need to be defined before
# this one!
for (fun,pow) in ((:inv, -1//1), (:sqrt, 1//2), (:cbrt, 1//3))
# The following are generated functions to ensure type stability.
@eval @generated function ($fun)(x::Dimensions)
dimtuple = map(x->x^($pow), x.parameters[1])
y = *(Dimensions{dimtuple}()) # sort appropriately
:($y)
end
end