mgmg 1.2.4 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/mgmg/poly.rb ADDED
@@ -0,0 +1,356 @@
1
+ module Mgmg
2
+ using Refiner
3
+ class TPolynomial
4
+ def initialize(mat, kind, star, main_m, sub_m)
5
+ @mat, @kind, @star, @main, @sub = mat, kind, star, main_m, sub_m
6
+ end
7
+ attr_accessor :mat, :kind, :star, :main, :sub
8
+ def initialize_copy(obj)
9
+ @mat, @kind, @star, @main, @sub = obj.mat.dup, obj.kind, obj.star, obj.main, obj.sub
10
+ end
11
+ def evaluate(smith, comp=smith)
12
+ @mat.map_with_index do |e, i, j|
13
+ e * (smith**i) * (comp**j)
14
+ end.sum
15
+ end
16
+ def to_s(fmt=nil)
17
+ foo = []
18
+ (@mat.col_size-1).downto(0) do |c|
19
+ bar = []
20
+ (@mat.row_size-1).downto(0) do |s|
21
+ value = @mat.body[s][c]
22
+ baz = str(value, fmt)
23
+ case s
24
+ when 0
25
+ # nothing to do
26
+ when 1
27
+ baz << 'S'
28
+ else
29
+ baz << "S^#{s}"
30
+ end
31
+ bar << baz if value != 0
32
+ end
33
+ case bar.length
34
+ when 0
35
+ next
36
+ when 1
37
+ bar = bar[0]
38
+ else
39
+ bar = "(#{bar.join('+')})"
40
+ end
41
+ case c
42
+ when 0
43
+ # nothing to do
44
+ when 1
45
+ bar << 'C'
46
+ else
47
+ bar << "C^#{c}"
48
+ end
49
+ foo << bar
50
+ end
51
+ foo.join('+').tap{|r| break str(0.quo(1), fmt) if r==''}
52
+ end
53
+ private def str(value, fmt)
54
+ ret = case fmt
55
+ when NilClass
56
+ value.to_s
57
+ when String
58
+ fmt % value
59
+ when Symbol
60
+ value.__send__(fmt)
61
+ when Proc
62
+ fmt.call(value)
63
+ else
64
+ raise
65
+ end
66
+ if ret[0] == '-' || ( /\//.match(ret) && ret[0] != '(' )
67
+ "(#{ret})"
68
+ else
69
+ ret
70
+ end
71
+ end
72
+ def inspect(fmt=->(r){"Rational(#{r.numerator}, #{r.denominator})"})
73
+ foo = []
74
+ (@mat.col_size-1).downto(0) do |c|
75
+ bar = []
76
+ (@mat.row_size-1).downto(0) do |s|
77
+ value = @mat.body[s][c]
78
+ bar << str(value, fmt)
79
+ end
80
+ buff = bar[0]
81
+ buff = "#{buff}*s+#{bar[1]}" if 1 < bar.length
82
+ 2.upto(bar.length-1) do |i|
83
+ buff = "(#{buff})*s+#{bar[i]}"
84
+ end
85
+ foo << buff
86
+ end
87
+ ret = foo[0]
88
+ 1.upto(foo.length-1) do |i|
89
+ ret = "(#{ret})*c+#{foo[i]}"
90
+ end
91
+ ret
92
+ end
93
+ def leading(fmt=nil)
94
+ value = self[-1, -1]
95
+ if fmt.nil?
96
+ value
97
+ else
98
+ str(value, fmt)
99
+ end
100
+ end
101
+ def smith_balance(other, order=-1)
102
+ o_org = order
103
+ order += @mat.col_size if order < 0
104
+ if order < 0 || @mat.col_size <= order || other.mat.col_size <= order then
105
+ raise ArgumentError, "given order #{o_org} is out of range [-max(#{@mat.col_size}, #{other.mat.col_size}), max(#{@mat.col_size}, #{other.mat.col_size})-1]"
106
+ end
107
+ a, b, c, d = @mat.body[1][order], @mat.body[0][order], other.mat.body[1][order], other.mat.body[0][order]
108
+ if a == c
109
+ return( b == d )
110
+ else
111
+ return( (d-b).quo(a-c) )
112
+ end
113
+ end
114
+ def smith_fix(smith, fmt=nil)
115
+ foo = []
116
+ (@mat.col_size-1).downto(0) do |c|
117
+ bar = 0
118
+ (@mat.row_size-1).downto(0) do |s|
119
+ bar += ( @mat.body[s][c] * (smith**s) )
120
+ end
121
+ bar = str(bar, fmt)
122
+ case c
123
+ when 0
124
+ # nothing to do
125
+ when 1
126
+ bar << 'C'
127
+ else
128
+ bar << "C^#{c}"
129
+ end
130
+ foo << bar
131
+ end
132
+ foo.join('+')
133
+ end
134
+
135
+ alias :+@ :dup
136
+ def -@
137
+ ret = self.dup
138
+ ret.mat.scalar!(-1)
139
+ ret
140
+ end
141
+ def +(other)
142
+ other = self.coerce(other)[0] unless other.kind_of?(self.class)
143
+ mat = @mat.padd(other.mat)
144
+ self.class.new(mat, 28, 0, 12, 12)
145
+ end
146
+ def -(other)
147
+ other = self.coerce(other)[0] unless other.kind_of?(self.class)
148
+ mat = @mat.padd(other.mat.scalar(-1))
149
+ self.class.new(mat, 28, 0, 12, 12)
150
+ end
151
+ def scalar(val)
152
+ ret = self.dup
153
+ ret.mat.scalar!(val)
154
+ ret
155
+ end
156
+ alias :* :scalar
157
+ def quo(val)
158
+ ret = self.dup
159
+ ret.mat.scalar!(1.quo(val))
160
+ ret
161
+ end
162
+ alias :/ :quo
163
+
164
+ def partial_derivative(variable)
165
+ case variable.to_s
166
+ when /\Ac/i
167
+ if @mat.col_size <= 1
168
+ self.class.new(Mat.new(1, 1, 0), 28, 0, 12, 12)
169
+ else
170
+ mat = Mat.new(@mat.row_size, @mat.col_size-1) do |i, j|
171
+ @mat.body[i][j+1] * (j+1)
172
+ end
173
+ self.class.new(mat, 28, 0, 12, 12)
174
+ end
175
+ when /\As/i
176
+ if @mat.row_size <= 1
177
+ self.class.new(Mat.new(1, 1, 0), 28, 0, 12, 12)
178
+ else
179
+ mat = Mat.new(@mat.row_size-1, @mat.col_size) do |i, j|
180
+ @mat.body[i+1][j] * (i+1)
181
+ end
182
+ self.class.new(mat, 28, 0, 12, 12)
183
+ end
184
+ else
185
+ raise ArgumentError, "the argument must be `s' or `c', not `#{variable}'"
186
+ end
187
+ end
188
+ def smith_eff(smith, comp=smith)
189
+ partial_derivative('s').evaluate(smith, comp).quo(2*(smith-1))
190
+ end
191
+ def comp_eff(smith, comp=smith)
192
+ partial_derivative('c').evaluate(smith, comp).quo(4*(comp-1))
193
+ end
194
+ def eff(smith, comp=smith)
195
+ [smith_eff(smith, comp), comp_eff(smith, comp)]
196
+ end
197
+
198
+ def [](i, j)
199
+ if (i < 0 && @mat.body.size < -i) || (j < 0 && @mat.body[0].size < -j)
200
+ raise IndexError, "(#{i}, #{j}) is out of (#{@mat.body.size}, #{@mat.body[0].size})"
201
+ end
202
+ begin
203
+ ret = @mat.body[i][j]
204
+ rescue NoMethodError
205
+ return 0
206
+ end
207
+ ret.nil? ? 0 : ret
208
+ end
209
+
210
+ def coerce(other)
211
+ [self.class.new(Mat.new(1, 1, other), 28, 0, 12, 12), self]
212
+ end
213
+ def <(other)
214
+ foo = self-other
215
+ (foo.mat.row_size-1).downto(0) do |s|
216
+ (foo.mat.col_size-1).downto(0) do |c|
217
+ bar = foo.mat.body[s][c]
218
+ if bar < 0
219
+ return true
220
+ elsif 0 < bar
221
+ return false
222
+ end
223
+ end
224
+ end
225
+ false
226
+ end
227
+ def <=(other)
228
+ foo = self-other
229
+ (foo.mat.row_size-1).downto(0) do |s|
230
+ (foo.mat.col_size-1).downto(0) do |c|
231
+ bar = foo.mat.body[s][c]
232
+ if bar < 0
233
+ return true
234
+ elsif 0 < bar
235
+ return false
236
+ end
237
+ end
238
+ end
239
+ true
240
+ end
241
+ def >(other)
242
+ foo = other-self
243
+ (foo.mat.row_size-1).downto(0) do |s|
244
+ (foo.mat.col_size-1).downto(0) do |c|
245
+ bar = foo.mat.body[s][c]
246
+ if bar < 0
247
+ return true
248
+ elsif 0 < bar
249
+ return false
250
+ end
251
+ end
252
+ end
253
+ false
254
+ end
255
+ def >=(other)
256
+ foo = other-self
257
+ (foo.mat.row_size-1).downto(0) do |s|
258
+ (foo.mat.col_size-1).downto(0) do |c|
259
+ bar = foo.mat.body[s][c]
260
+ if bar < 0
261
+ return true
262
+ elsif 0 < bar
263
+ return false
264
+ end
265
+ end
266
+ end
267
+ true
268
+ end
269
+ def ==(other)
270
+ foo = self-other
271
+ (foo.mat.row_size-1).downto(0) do |s|
272
+ (foo.mat.col_size-1).downto(0) do |c|
273
+ bar = foo.mat.body[s][c]
274
+ return false if bar != 0
275
+ end
276
+ end
277
+ true
278
+ end
279
+ def <=>(other)
280
+ if self == other
281
+ 0
282
+ elsif self < other
283
+ -1
284
+ else
285
+ 1
286
+ end
287
+ end
288
+ end
289
+ class << TPolynomial
290
+ ParamIndex = Hash.new
291
+ %i|attack phydef magdef hp mp str dex speed magic|.each.with_index do |s, i|
292
+ ParamIndex.store(s, i)
293
+ ParamIndex.store(i, i)
294
+ ParamIndex.store(Equip::ParamList[i], i)
295
+ end
296
+ def from_equip(equip, para)
297
+ new(Mat.new(1, 1, equip.para[ParamIndex[para]]), equip.kind, equip.star, equip.main, equip.sub)
298
+ end
299
+ def smith(str, para)
300
+ unless m = /\A(.+)\((.+\d+),?(.+\d+)\)\Z/.match(str)
301
+ raise ArgumentError.new("given string `#{str}' is unparsable as a smithing recipe")
302
+ end
303
+ kind = EquipIndex[m[1].to_sym]
304
+ main_m, main_s, main_mc = Equip.__send__(:parse_material, m[2])
305
+ sub_m, sub_s, sub_mc = Equip.__send__(:parse_material, m[3])
306
+ para = ParamIndex[para]
307
+
308
+ c = ( Equip9[kind][para] * Main9[main_m][para] ).cdiv(100).quo( main_mc==sub_mc ? 200 : 100 )
309
+ new(Mat.v_array(c*Sub9[sub_m][para], c), kind, (main_s+sub_s).div(2), main_mc, sub_mc)
310
+ end
311
+ def compose(main, sub, para)
312
+ main_k, sub_k = main.kind, sub.kind
313
+ main_s, sub_s = main.star, sub.star
314
+ main_main, sub_main = main.main, sub.main
315
+ main_sub, sub_sub = main.sub, sub.sub
316
+ para = ParamIndex[para]
317
+
318
+ if Equip9[main_k][para] == 0
319
+ c = 0.quo(1)
320
+ else
321
+ c = ( 100 + Equip9[main_k][para] - Equip9[sub_k][para] + Material9[main_main][para] - Material9[sub_main][para] +
322
+ (main_s-sub_s)*5 - ( ( main_main==sub_main && main_main != 9 ) ? 30 : 0 ) ).quo( main_k==sub_k ? 40000 : 20000 )
323
+ end
324
+ mat = main.mat.padd(sub.mat.pprod(Mat.h_array(c*Equip9[main_k][para], c)))
325
+ new(mat, main_k, main_s+sub_s, main_sub, sub_main)
326
+ end
327
+ def build(str, para, left_associative: true)
328
+ str = Mgmg.check_string(str)
329
+ _para = ParamIndex[para]
330
+ if _para.nil?
331
+ raise ArgumentError, "unknown parameter symbol `#{para.inspect}' given"
332
+ end
333
+ stack, str = build_sub0([], str, _para)
334
+ build_sub(stack, str, _para, left_associative)
335
+ end
336
+ private def build_sub0(stack, str, para)
337
+ SystemEquip.each do |k, v|
338
+ stack << from_equip(v, para)
339
+ str = str.gsub(k, "<#{stack.length-1}>")
340
+ end
341
+ [stack, str]
342
+ end
343
+ private def build_sub(stack, str, para, lassoc)
344
+ if m = /\A(.*\+?)\[([^\[\]]+)\](\+?[^\[]*)\Z/.match(str)
345
+ stack << build_sub(stack, m[2], para, lassoc)
346
+ build_sub(stack, "#{m[1]}<#{stack.length-1}>#{m[3]}", para, lassoc)
347
+ elsif m = ( lassoc ? /\A(.+)\+(.+?)\Z/ : /\A(.+?)\+(.+)\Z/ ).match(str)
348
+ compose(build_sub(stack, m[1], para, lassoc), build_sub(stack, m[2], para, lassoc), para)
349
+ elsif m = /\A\<(\d+)\>\Z/.match(str)
350
+ stack[m[1].to_i]
351
+ else
352
+ smith(str, para)
353
+ end
354
+ end
355
+ end
356
+ end
@@ -0,0 +1,254 @@
1
+ class String
2
+ def smith_search(para, target, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
3
+ if smith_min.nil?
4
+ if min_smith
5
+ smith_min = self.min_smith.min_smith
6
+ else
7
+ smith_min = build(-1, -1, left_associative: left_associative).min_level
8
+ end
9
+ end
10
+ if smith_max < smith_min
11
+ raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
12
+ elsif cut_exp < Float::INFINITY
13
+ begin
14
+ smith_max = [smith_max, Mgmg.invexp2(cut_exp, comp)].min
15
+ rescue
16
+ raise Mgmg::SearchCutException
17
+ end
18
+ end
19
+ if target <= build(smith_min, comp, left_associative: left_associative).para_call(para)
20
+ return smith_min
21
+ elsif build(smith_max, comp, left_associative: left_associative).para_call(para) < target
22
+ raise Mgmg::SearchCutException
23
+ end
24
+ while 1 < smith_max - smith_min do
25
+ smith = (smith_max - smith_min).div(2) + smith_min
26
+ if build(smith, comp, left_associative: left_associative).para_call(para) < target
27
+ smith_min = smith
28
+ else
29
+ smith_max = smith
30
+ end
31
+ end
32
+ smith_max
33
+ end
34
+ def comp_search(para, target, smith, comp_min=nil, comp_max=10000, left_associative: true)
35
+ comp_min = min_comp(left_associative: left_associative)
36
+ if comp_max < comp_min
37
+ raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
38
+ end
39
+ if target <= build(smith, comp_min, left_associative: left_associative).para_call(para)
40
+ return comp_min
41
+ elsif build(smith, comp_max, left_associative: left_associative).para_call(para) < target
42
+ raise Mgmg::SearchCutException
43
+ end
44
+ while 1 < comp_max - comp_min do
45
+ comp = (comp_max - comp_min).div(2) + comp_min
46
+ if build(smith, comp, left_associative: left_associative).para_call(para) < target
47
+ comp_min = comp
48
+ else
49
+ comp_max = comp
50
+ end
51
+ end
52
+ comp_max
53
+ end
54
+ def search(para, target, smith_min=nil, comp_min=nil, smith_max=10000, comp_max=10000, left_associative: true, step: 1, cut_exp: Float::INFINITY, min_smith: false)
55
+ if smith_min.nil?
56
+ if min_smith
57
+ smith_min = self.min_smith
58
+ else
59
+ smith_min = build(-1, -1, left_associative: left_associative).min_level
60
+ end
61
+ end
62
+ comp_min = min_comp(left_associative: left_associative) if comp_min.nil?
63
+ comp_min = comp_search(para, target, smith_max, comp_min, comp_max, left_associative: left_associative)
64
+ smith_max = smith_search(para, target, comp_min, smith_min, smith_max, left_associative: left_associative)
65
+ smith_min = smith_search(para, target, comp_max, smith_min, smith_max, left_associative: left_associative)
66
+ raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, comp_min)
67
+ comp_max = comp_search(para, target, smith_min, comp_min, comp_max, left_associative: left_associative)
68
+ minex, ret = Mgmg.exp(smith_min, comp_max), [smith_min, comp_max]
69
+ exp = Mgmg.exp(smith_max, comp_min)
70
+ minex, ret = exp, [smith_max, comp_min] if exp < minex
71
+ (comp_min+step).step(comp_max-1, step) do |comp|
72
+ break if minex < Mgmg.exp(smith_min, comp)
73
+ smith = smith_search(para, target, comp, smith_min, smith_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
74
+ exp = Mgmg.exp(smith, comp)
75
+ if exp < minex
76
+ minex, ret = exp, [smith, comp]
77
+ elsif exp == minex
78
+ if build(*ret).para_call(para) < build(smith, comp).para_call(para)
79
+ ret = [smith, comp]
80
+ end
81
+ end
82
+ rescue Mgmg::SearchCutException
83
+ end
84
+ raise Mgmg::SearchCutException, "the result exceeds given cut_exp=#{cut_exp}" if cut_exp < minex
85
+ ret
86
+ end
87
+ end
88
+ module Enumerable
89
+ def smith_search(para, target, armor, comp, smith_min=nil, smith_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
90
+ if smith_min.nil?
91
+ if min_smith
92
+ smith_min = self.min_smith[0]
93
+ else
94
+ smith_min = build(-1, -1, -1, left_associative: left_associative).min_level[0]
95
+ end
96
+ end
97
+ if smith_max < smith_min
98
+ raise ArgumentError, "smith_min <= smith_max is needed, (smith_min, smith_max) = (#{smith_min}, #{smith_max}) are given"
99
+ elsif cut_exp < Float::INFINITY
100
+ begin
101
+ smith_max = [smith_max, Mgmg.invexp3(cut_exp, armor, comp)].min
102
+ rescue
103
+ raise Mgmg::SearchCutException
104
+ end
105
+ end
106
+ if build(smith_max, armor, comp, left_associative: left_associative).para_call(para) < target
107
+ raise Mgmg::SearchCutException
108
+ elsif target <= build(smith_min, armor, comp, left_associative: left_associative).para_call(para)
109
+ return smith_min
110
+ end
111
+ while 1 < smith_max - smith_min do
112
+ smith = (smith_max - smith_min).div(2) + smith_min
113
+ if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
114
+ smith_min = smith
115
+ else
116
+ smith_max = smith
117
+ end
118
+ end
119
+ smith_max
120
+ end
121
+ def armor_search(para, target, smith, comp, armor_min=nil, armor_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
122
+ if armor_min.nil?
123
+ if min_smith
124
+ armor_min = self.min_smith[1]
125
+ else
126
+ armor_min = build(-1, -1, -1, left_associative: left_associative).min_level[1]
127
+ end
128
+ end
129
+ if armor_max < armor_min
130
+ raise ArgumentError, "armor_min <= armor_max is needed, (armor_min, armor_max) = (#{armor_min}, #{armor_max}) are given"
131
+ elsif cut_exp < Float::INFINITY
132
+ begin
133
+ armor_max = [armor_max, Mgmg.invexp3(cut_exp, smith, comp)].min
134
+ rescue
135
+ raise Mgmg::SearchCutException
136
+ end
137
+ end
138
+ if build(smith, armor_max, comp, left_associative: left_associative).para_call(para) < target
139
+ raise Mgmg::SearchCutException
140
+ elsif target <= build(smith, armor_min, comp, left_associative: left_associative).para_call(para)
141
+ return armor_min
142
+ end
143
+ while 1 < armor_max - armor_min do
144
+ armor = (armor_max - armor_min).div(2) + armor_min
145
+ if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
146
+ armor_min = armor
147
+ else
148
+ armor_max = armor
149
+ end
150
+ end
151
+ armor_max
152
+ end
153
+ def sa_search(para, target, comp, smith_min=nil, armor_min=nil, smith_max=10000, armor_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
154
+ if min_smith
155
+ s, a = self.min_smith
156
+ else
157
+ s, a = build(-1, -1, -1, left_associative: left_associative).min_level
158
+ end
159
+ smith_min = s if smith_min.nil?
160
+ armor_min = a if armor_min.nil?
161
+ smith_min = smith_search(para, target, armor_max, comp, smith_min, smith_max, left_associative: true)
162
+ armor_min = armor_search(para, target, smith_max, comp, armor_min, armor_max, left_associative: true)
163
+ raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, armor_min, comp)
164
+ smith_max = smith_search(para, target, armor_min, comp, smith_min, smith_max, left_associative: true)
165
+ armor_max = armor_search(para, target, smith_min, comp, armor_min, armor_max, left_associative: true)
166
+ minex, ret = Mgmg.exp(smith_min, armor_max, comp), [smith_min, armor_max]
167
+ exp = Mgmg.exp(smith_max, armor_min, comp)
168
+ if exp < minex
169
+ minex, ret = exp, [smith_max, armor_min]
170
+ (armor_min+1).upto(armor_max-1) do |armor|
171
+ break if minex < Mgmg.exp(smith_min, armor, comp)
172
+ smith = smith_search(para, target, armor, comp, smith_min, smith_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
173
+ exp = Mgmg.exp(smith, armor, comp)
174
+ if exp < minex
175
+ minex, ret = exp, [smith, armor]
176
+ elsif exp == minex
177
+ if build(*ret, comp).para_call(para) < build(smith, armor, comp).para_call(para)
178
+ ret = [smith, armor]
179
+ end
180
+ end
181
+ rescue Mgmg::SearchCutException
182
+ end
183
+ else
184
+ (smith_min+1).upto(smith_max-1) do |smith|
185
+ break if minex < Mgmg.exp(smith, armor_min, comp)
186
+ armor = armor_search(para, target, smith, comp, armor_min, armor_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
187
+ exp = Mgmg.exp(smith, armor, comp)
188
+ if exp < minex
189
+ minex, ret = exp, [smith, armor]
190
+ elsif exp == minex
191
+ if build(*ret, comp).para_call(para) < build(smith, armor, comp).para_call(para)
192
+ ret = [smith, armor]
193
+ end
194
+ end
195
+ rescue Mgmg::SearchCutException
196
+ end
197
+ end
198
+ raise Mgmg::SearchCutException if cut_exp < minex
199
+ ret
200
+ end
201
+ def comp_search(para, target, smith, armor, comp_min=nil, comp_max=10000, left_associative: true)
202
+ comp_min = min_comp(left_associative: left_associative)
203
+ if comp_max < comp_min
204
+ raise ArgumentError, "comp_min <= comp_max is needed, (comp_min, comp_max) = (#{comp_min}, #{comp_max}) are given"
205
+ end
206
+ if target <= build(smith, armor, comp_min, left_associative: left_associative).para_call(para)
207
+ return comp_min
208
+ elsif build(smith, armor, comp_max, left_associative: left_associative).para_call(para) < target
209
+ raise ArgumentError, "given comp_max=#{comp_max} does not satisfies the target"
210
+ end
211
+ while 1 < comp_max - comp_min do
212
+ comp = (comp_max - comp_min).div(2) + comp_min
213
+ if build(smith, armor, comp, left_associative: left_associative).para_call(para) < target
214
+ comp_min = comp
215
+ else
216
+ comp_max = comp
217
+ end
218
+ end
219
+ comp_max
220
+ end
221
+ def search(para, target, smith_min=nil, armor_min=nil, comp_min=nil, smith_max=10000, armor_max=10000, comp_max=10000, left_associative: true, cut_exp: Float::INFINITY, min_smith: false)
222
+ if min_smith
223
+ s, a = self.min_smith
224
+ else
225
+ s, a = build(-1, -1, -1, left_associative: left_associative).min_level
226
+ end
227
+ smith_min = s if smith_min.nil?
228
+ armor_min = a if armor_min.nil?
229
+ comp_min = min_comp(left_associative: left_associative) if comp_min.nil?
230
+ comp_min = comp_search(para, target, smith_max, armor_max, comp_min, comp_max, left_associative: left_associative)
231
+ smith_max, armor_max = sa_search(para, target, comp_min, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative)
232
+ smith_min, armor_min = sa_search(para, target, comp_max, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative)
233
+ raise Mgmg::SearchCutException if cut_exp < Mgmg.exp(smith_min, armor_min, comp_min)
234
+ comp_max = comp_search(para, target, smith_min, armor_min, comp_min, comp_max, left_associative: left_associative)
235
+ minex, ret = Mgmg.exp(smith_min, armor_min, comp_max), [smith_min, armor_min, comp_max]
236
+ exp = Mgmg.exp(smith_max, armor_max, comp_min)
237
+ minex, ret = exp, [smith_max, armor_max, comp_min] if exp < minex
238
+ (comp_min+1).upto(comp_max-1) do |comp|
239
+ break if minex < Mgmg.exp(smith_min, armor_min, comp)
240
+ smith, armor = sa_search(para, target, comp, smith_min, armor_min, smith_max, armor_max, left_associative: left_associative, cut_exp: [minex, cut_exp].min)
241
+ exp = Mgmg.exp(smith, armor, comp)
242
+ if exp < minex
243
+ minex, ret = exp, [smith, armor, comp]
244
+ elsif exp == minex
245
+ if build(*ret).para_call(para) < build(smith, armor, comp).para_call(para)
246
+ ret = [smith, armor, comp]
247
+ end
248
+ end
249
+ rescue Mgmg::SearchCutException
250
+ end
251
+ raise Mgmg::SearchCutException, "the result exceeds given cut_exp=#{cut_exp}" if cut_exp < minex
252
+ ret
253
+ end
254
+ end