11module MultiplesOfPi
22
3+ export PiExpTimes
34export PiTimes
45export Pi
56
67"""
7- PiTimes (x)
8+ PiExpTimes (x)
89
9- Construct a number that behaves as `π*x ` for a real `x`
10+ Construct a number that behaves as `x*π^n ` for a real `x` and an integer `n `
1011"""
11- struct PiTimes{ T<: Real } <: AbstractIrrational
12+ struct PiExpTimes{N, T<: Real } <: AbstractIrrational
1213 x :: T
14+ function PiExpTimes {N,T} (x) where {N,T<: Real }
15+ @assert N isa Integer
16+ new {N,T} (T (x))
17+ end
1318end
1419
20+ # Type T is inferred from x
21+ PiExpTimes {N} (x:: T ) where {N,T<: Real } = PiExpTimes {N,T} (x)
22+ PiExpTimes {0} (x:: Real ) = x
23+ PiExpTimes {0} (p:: PiExpTimes ) = p
24+
25+ PiExpTimes {N} (p:: PiExpTimes{M} ) where {N,M} = PiExpTimes {N+M} (p. x)
26+ PiExpTimes {N,T} (p:: PiExpTimes{N,T} ) where {N,T<: Real } = PiExpTimes {2N} (p. x)
27+ PiExpTimes {N,T} (p:: PiExpTimes{N} ) where {N,T<: Real } = PiExpTimes {2N} (T (p. x))
28+
29+ PiExpTimes {N} (:: Irrational{:π} ) where {N} = PiExpTimes {N+1} (1 )
30+ PiExpTimes {N,Irrational{:π}} (:: Irrational{:π} ) where {N} = PiExpTimes {N+1} (1 )
31+
32+ """
33+ PiTimes(x)
34+
35+ Construct a number that behaves as `x*π` for a real `x`. `PiTimes` is an alias for
36+ `PiExpTimes{1}`
37+ """
38+ const PiTimes{T<: Real } = PiExpTimes{1 ,T}
39+
1540"""
1641 Pi
1742
1843The number `PiTimes(1)`, numerically equivalent to `pi`
1944"""
2045const Pi = PiTimes (1 )
2146
22- Base.:(< )(p:: PiTimes ,q:: PiTimes ) = p. x < q. x
23- Base.:(== )(p:: PiTimes ,q:: PiTimes ) = p. x == q. x
24- Base.:(== )(p:: PiTimes ,y:: Real ) = float (p) == y
25- Base.:(== )(y:: Real ,p:: PiTimes ) = float (p) == y
47+ Base.:(< )(p:: PiExpTimes{N} ,q:: PiExpTimes{N} ) where {N} = p. x < q. x
48+ function Base.:(< )(p:: PiExpTimes{M} ,q:: PiExpTimes{N} ) where {M,N}
49+ p. x == q. x ? M < N : float (p) < float (q)
50+ end
51+
52+ Base.:(< )(p:: PiTimes ,:: Irrational{:π} ) = p. x < one (p. x)
53+ Base.:(< )(:: Irrational{:π} ,p:: PiTimes ) = p. x > one (p. x)
54+
55+ Base.:(== )(p:: PiExpTimes{N} ,q:: PiExpTimes{N} ) where {N} = p. x == q. x
56+ function Base.:(== )(p:: PiExpTimes ,q:: PiExpTimes )
57+ iszero (p. x) && iszero (q. x) ? true : float (p) == float (q)
58+ end
59+ Base.:(== )(p:: PiExpTimes ,y:: Real ) = float (p) == y
60+ Base.:(== )(y:: Real ,p:: PiExpTimes ) = float (p) == y
61+
62+ Base.:(== )(:: Irrational{:π} ,p:: PiExpTimes ) = false
63+ Base.:(== )(p:: PiExpTimes ,:: Irrational{:π} ) = false
2664
2765Base.:(== )(p:: PiTimes ,:: Irrational{:π} ) = isone (p. x)
2866Base.:(== )(:: Irrational{:π} ,p:: PiTimes ) = isone (p. x)
2967
3068for T in (:BigFloat ,:Float64 ,:Float32 ,:Float16 )
31- @eval Base.$ T (p:: PiTimes ) = π* $ T (p. x)
69+ @eval begin
70+ function Base. $T (p:: PiExpTimes{N} ) where {N}
71+ if N >= 0
72+ y = foldl (* ,(pi for i= 1 : N),init= one ($ T))
73+ return y* $ T (p. x)
74+ else
75+ y = foldl (/ ,(pi for i= 1 : - N),init= one ($ T))
76+ return y* $ T (p. x)
77+ end
78+ end
79+ Base.$ T (p:: PiExpTimes{0} ) = $ T (p. x)
80+ end
3281end
3382
3483for f in (:iszero ,:isfinite ,:isnan )
35- @eval Base.$ f (p:: PiTimes ) = $ f (p. x)
84+ @eval Base.$ f (p:: PiExpTimes ) = $ f (p. x)
3685end
3786
3887# Unfortunately Irrational numbers do not have a multiplicative identity of the same type,
3988# so we make do with something that works
40- Base. one (:: PiTimes{T} ) where {T} = one (PiTimes{T})
41- Base. one (:: Type{PiTimes{T}} ) where {T} = true
89+ Base. one (:: T ) where {T<: PiExpTimes } = one (T)
90+ Base. one (:: Type{<:PiExpTimes} ) = true
91+ Base. one (:: Type{<:PiExpTimes{0,T}} ) where {T} = one (T)
4292
43- Base. zero (:: PiTimes{T} ) where {T} = zero (PiTimes{T})
44- Base. zero (:: Type{PiTimes{T}} ) where {T} = PiTimes (zero (T))
93+ Base. zero (:: T ) where {T<: PiExpTimes } = zero (T)
94+ Base. zero (:: Type{PiExpTimes{N,T}} ) where {N,T} = PiExpTimes {N} (zero (T))
95+
96+ Base. sign (p:: PiExpTimes ) = sign (p. x)
97+ Base. signbit (p:: PiExpTimes ) = signbit (p. x)
4598
4699# Define trigonometric functions
47100
@@ -51,6 +104,9 @@ Base.zero(::Type{PiTimes{T}}) where {T} = PiTimes(zero(T))
51104
52105@inline Base. cis (p:: PiTimes ) = Complex (cos (p),sin (p))
53106
107+ Base. sinc (p:: PiExpTimes ) = sinc (float (p))
108+ Base. sinc (p:: PiExpTimes{0} ) = sinc (p. x)
109+
54110function Base. tan (p:: PiTimes{T} ) where {T}
55111 iszero (p. x) && return p. x
56112 r = abs (rem (p. x,one (p. x)))
@@ -62,69 +118,191 @@ end
62118
63119# Hyperbolic functions
64120
65- function Base. tanh (z:: Complex{PiTimes{T}} ) where {T}
121+ function Base. tanh (z:: Complex{<: PiTimes} )
66122 iszero (real (z)) && return im* tan (imag (z))
67123 tanh (float (z))
68124end
69125
70126# Arithmetic operators
71127
72- Base.:(+ )(p1:: PiTimes ,p2:: PiTimes ) = PiTimes (p1. x + p2. x)
128+ Base.:(+ )(p1:: PiExpTimes{N} ,p2:: PiExpTimes{N} ) where {N} = PiExpTimes {N} (p1. x + p2. x)
129+ Base.:(+ )(p1:: PiExpTimes{0} ,p2:: PiExpTimes{0} ) = p1. x + p2. x
130+
73131Base.:(+ )(p:: PiTimes ,:: Irrational{:π} ) = PiTimes (p. x + one (p. x))
74132Base.:(+ )(:: Irrational{:π} ,p:: PiTimes ) = PiTimes (p. x + one (p. x))
75133
76- Base.:(- )(p:: PiTimes ) = PiTimes (- p. x)
77- Base.:(- )(p1:: PiTimes ,p2:: PiTimes ) = PiTimes (p1. x- p2. x)
134+ Base.:(- )(p:: PiExpTimes{N} ) where {N} = PiExpTimes {N} (- p. x)
135+ Base.:(- )(p:: PiExpTimes{0} ) = - p. x
136+
137+ Base.:(- )(p1:: PiExpTimes{N} ,p2:: PiExpTimes{N} ) where {N} = PiExpTimes {N} (p1. x- p2. x)
138+ Base.:(- )(p1:: PiExpTimes{0} ,p2:: PiExpTimes{0} ) = p1. x- p2. x
139+
78140Base.:(- )(p:: PiTimes ,:: Irrational{:π} ) = PiTimes (p. x - one (p. x))
79141Base.:(- )(:: Irrational{:π} ,p:: PiTimes ) = PiTimes (one (p. x) - p. x)
80142
81- Base.:(/ )(p1:: PiTimes ,p2:: PiTimes ) = p1. x/ p2. x
82- Base.:(/ )(p1:: PiTimes ,y :: Real ) = PiTimes (p1. x/ y )
143+ Base.:(/ )(p1:: PiExpTimes{N} ,p2:: PiExpTimes{N} ) where {N} = p1. x/ p2. x
144+ Base.:(/ )(p1:: PiExpTimes{M} ,p2 :: PiExpTimes{N} ) where {M,N} = PiExpTimes {M-N} (p1. x/ p2 . x )
83145
84- Base.:(/ )(:: Irrational{:π} ,p:: PiTimes ) = inv (float (p . x) )
85- Base.:(/ )(p :: PiTimes , :: Irrational{:π} ) = float (p . x )
146+ Base.:(/ )(y :: Real ,p:: PiExpTimes ) = y * inv (p )
147+ Base.:(/ )(p1 :: PiExpTimes{N} ,y :: Real ) where {N} = PiExpTimes {N} (p1 . x / y )
86148
87- Base.:(* )(p1:: PiTimes ,y:: Real ) = PiTimes (p1. x* y)
88- Base.:(* )(y:: Real ,p1:: PiTimes ) = PiTimes (p1. x* y)
89- Base.:(* )(p1:: PiTimes ,p2:: PiTimes ) = float (p1) * float (p2)
90- Base.:(* )(z:: Complex{Bool} ,p:: PiTimes ) = Complex (real (z)* p,imag (z)* p)
91- Base.:(* )(p:: PiTimes ,z:: Complex{Bool} ) = Complex (real (z)* p,imag (z)* p)
92- Base.:(* )(x:: Bool ,p:: PiTimes ) = ifelse (x,p,zero (p))
93- Base.:(* )(p:: PiTimes ,x:: Bool ) = ifelse (x,p,zero (p))
149+ Base.:(/ )(:: Irrational{:π} ,p:: PiExpTimes{N} ) where {N} = PiExpTimes {1-N} (inv (p. x))
150+ Base.:(/ )(:: Irrational{:π} ,p:: PiTimes ) = inv (p. x)
151+ Base.:(/ )(p:: PiExpTimes{N} ,:: Irrational{:π} ) where {N} = PiExpTimes {N-1} (p. x)
152+ Base.:(/ )(p:: PiTimes ,:: Irrational{:π} )= p. x
94153
95- for op in Symbol[:+ ,:- ,:/ ,:* ]
96- @eval Base.$ op (p:: PiTimes ,x:: AbstractIrrational ) = $ op (Float64 (p),Float64 (x))
97- @eval Base.$ op (x:: AbstractIrrational ,p:: PiTimes ) = $ op (Float64 (x),Float64 (p))
154+ function Base.:(/ )(p:: PiExpTimes{N} ,z:: Complex{<:PiExpTimes{M}} ) where {M,N}
155+ are,aim = p. x, zero (p. x)
156+ bre,bim = real (z). x, imag (z). x
157+ Complex (are,aim)/ Complex (bre,bim) * PiExpTimes {N-M} (1 )
98158end
99159
100- Base.:(// )(p:: PiTimes ,n) = PiTimes (p. x// n)
160+ function Base.:(/ )(p:: PiExpTimes{N} ,z:: Complex{<:PiExpTimes{N}} ) where {N}
161+ are,aim = p. x, zero (p. x)
162+ bre,bim = real (z). x, imag (z). x
163+ Complex (are,aim)/ Complex (bre,bim)
164+ end
101165
102- # Conversion and promotion
166+ function Base.:(/ )(a:: Complex{<:PiExpTimes{N}} ,b:: Complex{<:Real} ) where {N}
167+ are,aim = real (a). x, imag (a). x
168+ bre,bim = reim (b)
169+ Complex (are,aim)/ Complex (bre,bim) * PiExpTimes {N} (1 )
170+ end
171+
172+ function Base.:(/ )(a:: Complex{<:PiExpTimes{N}} ,b:: Complex{<:PiExpTimes{M}} ) where {M,N}
173+ are,aim = real (a). x, imag (a). x
174+ bre,bim = real (b). x, imag (b). x
175+ Complex (are,aim)/ Complex (bre,bim) * PiExpTimes {N-M} (1 )
176+ end
177+
178+ function Base.:(/ )(a:: Complex{<:PiExpTimes{N}} ,b:: Complex{<:PiExpTimes{N}} ) where {N}
179+ are,aim = real (a). x, imag (a). x
180+ bre,bim = real (b). x, imag (b). x
181+ Complex (are,aim)/ Complex (bre,bim)
182+ end
183+
184+ Base. inv (p:: PiExpTimes{N} ) where {N} = PiExpTimes {-N} (inv (p. x))
103185
104- for t in (Int8, Int16, Int32, Int64, Int128, Bool, UInt8, UInt16, UInt32, UInt64, UInt128)
105- @eval Base. promote_rule (:: Type{PiTimes{Float16}} , :: Type{PiTimes{$t}} ) = PiTimes{Float16}
106- @eval Base. promote_rule (:: Type{PiTimes{$t}} ,:: Type{PiTimes{Float16}} ) = PiTimes{Float16}
186+ Base.:(* )(p1:: PiExpTimes{N} ,y:: Real ) where {N} = PiExpTimes {N} (p1. x* y)
187+ Base.:(* )(y:: Real ,p1:: PiExpTimes{N} ) where {N} = PiExpTimes {N} (p1. x* y)
188+
189+ Base.:(* )(b:: Bool ,p:: PiExpTimes ) = ifelse (b,p,flipsign (zero (p),p. x))
190+ Base.:(* )(p:: PiExpTimes ,b:: Bool ) = ifelse (b,p,flipsign (zero (p),p. x))
191+
192+ function Base.:(* )(p1:: PiExpTimes{M} ,p2:: PiExpTimes{N} ) where {M,N}
193+ PiExpTimes {M+N} (p1. x* p2. x)
194+ end
195+
196+ Base.:(* )(z:: Complex{Bool} ,p:: PiExpTimes ) = Complex (real (z)* p,imag (z)* p)
197+ Base.:(* )(p:: PiExpTimes ,z:: Complex{Bool} ) = Complex (real (z)* p,imag (z)* p)
198+
199+ Base.:(* )(:: Irrational{:π} ,p:: PiExpTimes ) = PiTimes (p)
200+ Base.:(* )(p:: PiExpTimes ,:: Irrational{:π} ) = PiTimes (p)
201+
202+ # Not type-stable!
203+ Base.:(^ )(p:: PiExpTimes{N} ,n:: Integer ) where {N} = PiExpTimes {N*n} (p. x^ n)
204+
205+ for op in Symbol[:/ ,:* ]
206+ @eval Base.$ op (p:: PiExpTimes ,x:: AbstractIrrational ) = $ op (Float64 (p),Float64 (x))
207+ @eval Base.$ op (x:: AbstractIrrational ,p:: PiExpTimes ) = $ op (Float64 (x),Float64 (p))
208+ end
209+
210+ # Rational divide
211+ Base.:(// )(p:: PiExpTimes{N} , n:: Real ) where {N} = PiExpTimes {N} (p. x// n)
212+ Base.:(// )(n:: Real , p:: PiExpTimes{N} ) where {N} = PiExpTimes {-N} (n// p. x)
213+ Base.:(// )(p:: PiExpTimes{M} ,q:: PiExpTimes{N} ) where {M,N} = PiExpTimes {M-N} (p. x// q. x)
214+ Base.:(// )(p:: PiExpTimes{N} ,q:: PiExpTimes{N} ) where {N} = p. x// q. x
215+
216+ Base.:(// )(:: Irrational{:π} ,p:: PiExpTimes{N} ) where {N} = PiExpTimes {1-N} (1 // p. x)
217+ Base.:(// )(p:: PiExpTimes{N} ,:: Irrational{:π} ) where {N} = PiExpTimes {N-1} (p. x// 1 )
218+ Base.:(// )(:: Irrational{:π} ,p:: PiTimes ) = 1 // p. x
219+ Base.:(// )(p:: PiTimes ,:: Irrational{:π} ) = p. x// 1
220+
221+ Base.:(// )(p:: Complex{<:PiExpTimes} ,q:: PiExpTimes ) = Complex (real (p)// q,imag (p)// q)
222+
223+ function Base.:(// )(p:: PiExpTimes{N} ,z:: Complex{<:PiExpTimes{M}} ) where {M,N}
224+ are,aim = p. x, zero (p. x)
225+ bre,bim = real (z). x, imag (z). x
226+ Complex (are,aim)// Complex (bre,bim) * PiExpTimes {N-M} (1 )
227+ end
228+
229+ function Base.:(// )(p:: PiExpTimes{N} ,z:: Complex{<:PiExpTimes{N}} ) where {N}
230+ are,aim = p. x, zero (p. x)
231+ bre,bim = real (z). x, imag (z). x
232+ Complex (are,aim)// Complex (bre,bim)
107233end
108234
109- for t1 in (Float32, Float64)
110- for t2 in (Int8, Int16, Int32, Int64, Bool, UInt8, UInt16, UInt32, UInt64)
111- @eval begin
112- Base. promote_rule (:: Type{PiTimes{$t1}} , :: Type{PiTimes{$t2}} ) = PiTimes{$ t1}
113- Base. promote_rule (:: Type{PiTimes{$t2}} , :: Type{PiTimes{$t1}} ) = PiTimes{$ t1}
114- end
115- end
235+ function Base.:(// )(a:: Complex{<:PiExpTimes{N}} ,b:: Complex{<:PiExpTimes{M}} ) where {M,N}
236+ are,aim = real (a). x, imag (a). x
237+ bre,bim = real (b). x, imag (b). x
238+ Complex (are,aim)// Complex (bre,bim) * PiExpTimes {N-M} (1 )
239+ end
240+
241+ function Base.:(// )(a:: Complex{<:PiExpTimes{N}} ,b:: Complex{<:PiExpTimes{N}} ) where {N}
242+ are,aim = real (a). x, imag (a). x
243+ bre,bim = real (b). x, imag (b). x
244+ Complex (are,aim)// Complex (bre,bim)
245+ end
246+
247+ # Conversion and promotion
248+ function Base. promote_rule (:: Type{PiExpTimes{N,R}} ,:: Type{PiExpTimes{N,S}} ) where {N,R,S}
249+ PiExpTimes{N,promote_type (R,S)}
116250end
117251
118252Base. promote_rule (:: Type{PiTimes{T}} , :: Type{Irrational{:π}} ) where {T} = PiTimes{T}
119253Base. promote_rule (:: Type{Irrational{:π}} , :: Type{PiTimes{T}} ) where {T} = PiTimes{T}
120254
121- Base. convert (:: Type{PiTimes} ,x:: Real ) = PiTimes (x/ π)
122- Base. convert (:: Type{PiTimes{T}} ,x:: Real ) where {T} = PiTimes {T} (x/ π)
123- Base. convert (:: Type{PiTimes{T}} ,p:: PiTimes ) where {T} = PiTimes {T} (p. x)
255+ function Base. promote_rule (:: Type{Complex{PiTimes{T}}} , :: Type{Irrational{:π}} ) where {T}
256+ Complex{PiTimes{T}}
257+ end
258+ function Base. promote_rule (:: Type{Irrational{:π}} , :: Type{Complex{PiTimes{T}}} ) where {T}
259+ Complex{PiTimes{T}}
260+ end
124261
125- function Base. show (io:: IO ,p:: PiTimes )
126- pxstr = isone (p. x) ? " " : string (p. x)
127- str = iszero (p) ? string (p. x) : pxstr* " Pi"
262+ # The choices for conversion aren't unique as π^n can not be computed
263+ # for negative integers. Here we resort to
264+ function Base. convert (:: Type{PiExpTimes{N}} ,x:: Real ) where {N}
265+ den = N < 0 ? π^ float (N) : π^ N
266+ PiExpTimes {N} (x/ den)
267+ end
268+ function Base. convert (:: Type{PiExpTimes{N,T}} ,x:: Real ) where {N,T}
269+ den = N < 0 ? π^ float (N) : π^ N
270+ PiExpTimes {N} (T (x/ den))
271+ end
272+ function Base. convert (:: Type{PiExpTimes{N}} ,:: Irrational{:π} ) where {N}
273+ den = N < 1 ? π^ float (N- 1 ) : π^ (N- 1 )
274+ PiExpTimes {N} (1 / den)
275+ end
276+ function Base. convert (:: Type{PiExpTimes{N,T}} ,:: Irrational{:π} ) where {N,T}
277+ den = N < 1 ? π^ float (N- 1 ) : π^ (N- 1 )
278+ PiExpTimes {N} (T (1 / den))
279+ end
280+ function Base. convert (:: Type{PiExpTimes{N}} ,p:: PiExpTimes{M} ) where {M,N}
281+ den = N < M ? π^ float (N- M) : π^ (N- M)
282+ PiExpTimes {N} (p. x/ den)
283+ end
284+ function Base. convert (:: Type{PiExpTimes{N,T}} ,p:: PiExpTimes{M} ) where {M,N,T}
285+ den = N < M ? π^ float (N- M) : π^ (N- M)
286+ PiExpTimes {N} (T (p. x/ den))
287+ end
288+ Base. convert (:: Type{PiExpTimes{N}} ,p:: PiExpTimes{N} ) where {N} = p
289+ Base. convert (:: Type{PiExpTimes{N,T}} ,p:: PiExpTimes{N} ) where {N,T} = PiExpTimes {N} (T (p. x))
290+ Base. convert (:: Type{PiExpTimes{N,T}} ,p:: PiExpTimes{N,T} ) where {N,T} = p
291+
292+ function Base. show (io:: IO ,p:: PiExpTimes{N} ) where {N}
293+ x = p. x
294+ function expstr (p)
295+ ifelse (isone (N)," Pi" ," Pi^" * string (N))
296+ end
297+
298+ xstrexp (x,p) = isone (x) ? expstr (p) : string (x)* " *" * expstr (p)
299+ xstrexp (x:: Integer ,p) = isone (x) ? expstr (p) : string (x)* expstr (p)
300+ xstrexp (x:: AbstractFloat ,p) = isone (x) ? expstr (p) :
301+ isinf (x) || isnan (x) ? string (x) :
302+ string (x)* " *" * expstr (p)
303+ xstrexp (x:: Rational ,p) = " (" * string (x)* " )" * expstr (p)
304+
305+ str = iszero (x) ? string (x) : xstrexp (x,p)
128306 print (io,str)
129307end
130308
0 commit comments