-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmultiplicable.rb
More file actions
128 lines (114 loc) · 2.86 KB
/
Copy pathmultiplicable.rb
File metadata and controls
128 lines (114 loc) · 2.86 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
require_relative 'ext'
require_relative 'math'
require_relative 'coercible'
# Use binary algorithm to raise +x+ to the power of +n+.
#
# Any binary operation can be provided for +mul+, which defaults to the * method of +x+.
#
# If +n+ is zero, then +one+ is returned, defaulting to +x.class.one+. This is the only
# case in which one is required. Nothing is ever multiplied by one.
#
# Negative values of +n+ are supported if +x+ responds to :reciprocal:.
#
# Examples:
#
# binary_pow(2, 8)
# => 256
# binary_pow(5, 11, &:+)
# => 55
# binary_pow('wtf',5) {|a, b| "#{a} #{b}" }
# => "wtf wtf wtf wtf wtf"
#
def binary_pow(x, n, one=nil, &mul)
mul ||= proc{|a, b| a * b }
case n
when 0
one || x.class.one
when 1
x
else
if n.negative?
# Should we reciprocate before or after??
binary_pow(x.reciprocal, -n, one, &mul)
else
sq = x
x = nil
loop do
n, r = n.divmod(2)
if r.one?
if x.nil?
x = sq
else
x = mul[x, sq]
end
break x if n.zero?
end
sq = mul[sq, sq]
end
end
end
end
module Multiplicable
extend ActiveSupport::Concern
include Coercible
class_methods do
abstract_method :mul_identity
protected :mul_identity
def one
@mul_identity ||= mul_identity
end
end
abstract_method :one?, :reciprocal, :mul, :can_mul?
protected :reciprocal, :mul
def validate_mul(x)
can_mul?(x) or raise TypeError, "Cannot multiply #{self.class} with #{x.class}"
end
def *(x)
if can_mul?(x)
if one?
x
elsif x.one?
self
else
mul(x)
end
elsif x.respond_to? :coerce
a, b = x.coerce(self)
a * b
end
end
def pow(n)
binary_pow(self, n)
end
def **(n)
n.integer? or raise Math::DomainError, "#{self.class} raised to non-integer power is undefined"
if one?
self
elsif n.zero?
self.class.one
elsif n.one?
self
elsif n.negative?
reciprocal**(-n)
else
pow(n)
end
end
# x * self * x**(-1)
def conjugate(x)
validate_mul(x)
if one? || x.one?
self
else
x.mul(mul(x.reciprocal))
end
end
def commutator(x)
validate_mul(x)
if one? || x.one?
self.class.one
else
mul(x).mul(reciprocal.mul(x.reciprocal))
end
end
end