BOAST 2.0.2 → 2.1.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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/BOAST.gemspec +4 -3
  3. data/lib/BOAST.rb +1 -0
  4. data/lib/BOAST/Language/Arithmetic.rb +5 -1
  5. data/lib/BOAST/Language/BOAST_OpenCL.rb +6 -6
  6. data/lib/BOAST/Language/Case.rb +11 -11
  7. data/lib/BOAST/Language/Comment.rb +2 -2
  8. data/lib/BOAST/Language/Config.rb +5 -5
  9. data/lib/BOAST/Language/DataTypes.rb +31 -29
  10. data/lib/BOAST/Language/Expression.rb +16 -16
  11. data/lib/BOAST/Language/For.rb +6 -6
  12. data/lib/BOAST/Language/FuncCall.rb +7 -7
  13. data/lib/BOAST/Language/HighLevelOperators.rb +6 -6
  14. data/lib/BOAST/Language/If.rb +7 -7
  15. data/lib/BOAST/Language/Index.rb +31 -31
  16. data/lib/BOAST/Language/Intrinsics.rb +27 -27
  17. data/lib/BOAST/Language/OpenMP.rb +19 -19
  18. data/lib/BOAST/Language/Operators.rb +62 -50
  19. data/lib/BOAST/Language/Pragma.rb +4 -4
  20. data/lib/BOAST/Language/Procedure.rb +47 -47
  21. data/lib/BOAST/Language/Slice.rb +14 -14
  22. data/lib/BOAST/Language/State.rb +1 -1
  23. data/lib/BOAST/Language/Transitions.rb +1 -1
  24. data/lib/BOAST/Language/Variable.rb +83 -90
  25. data/lib/BOAST/Language/While.rb +4 -4
  26. data/lib/BOAST/Optimization/Optimization.rb +61 -37
  27. data/lib/BOAST/Runtime/AffinityProbe.rb +99 -15
  28. data/lib/BOAST/Runtime/CRuntime.rb +18 -6
  29. data/lib/BOAST/Runtime/CUDARuntime.rb +11 -7
  30. data/lib/BOAST/Runtime/CoExecute.rb +77 -0
  31. data/lib/BOAST/Runtime/CompiledRuntime.rb +274 -110
  32. data/lib/BOAST/Runtime/Compilers.rb +15 -15
  33. data/lib/BOAST/Runtime/Config.rb +3 -0
  34. data/lib/BOAST/Runtime/EnergyProbe.rb +86 -71
  35. data/lib/BOAST/Runtime/FFIRuntime.rb +1 -1
  36. data/lib/BOAST/Runtime/FORTRANRuntime.rb +15 -5
  37. data/lib/BOAST/Runtime/MPPARuntime.rb +30 -19
  38. data/lib/BOAST/Runtime/OpenCLRuntime.rb +2 -2
  39. data/lib/BOAST/Runtime/Probe.rb +122 -41
  40. metadata +29 -8
@@ -38,7 +38,7 @@ module BOAST
38
38
  class BasicBinaryOperator < Operator
39
39
 
40
40
  def BasicBinaryOperator.string(arg1, arg2, return_type)
41
- if lang == C and (arg1.class == Variable and arg2.class == Variable) and (arg1.type.vector_length > 1 or arg2.type.vector_length > 1) then
41
+ if lang == C and (arg1.instance_of? Variable and arg2.instance_of? Variable) and (arg1.type.vector_length > 1 or arg2.type.vector_length > 1) then
42
42
  instruction = intrinsics(intr_symbol, return_type.type)
43
43
  a1 = convert(arg1, return_type.type)
44
44
  a2 = convert(arg2, return_type.type)
@@ -357,7 +357,7 @@ module BOAST
357
357
  elsif values.kind_of?(Array) then
358
358
  raise OperatorError, "Wrong number of mask values (#{values.length} for #{length})!" if length and values.length != length
359
359
  s = "0x"
360
- s += values.collect { |v| v != 0 ? 1 : 0 }.reverse.join
360
+ s << values.collect { |v| v != 0 ? 1 : 0 }.reverse.join
361
361
  @value = Int( s, :signed => false, :size => values.length / 8 + ( values.length % 8 > 0 ? 1 : 0 ), :constant => s )
362
362
  @length = values.length
363
363
  @pos_values = values.reject { |e| e == 0 }.length
@@ -434,7 +434,7 @@ module BOAST
434
434
  if lang == CL then
435
435
  return @return_type.copy("(#{@return_type.type.decl})( #{eff_src} )", DISCARD_OPTIONS) if lang == CL
436
436
  end
437
- if (@source.is_a?(Numeric) and @source == 0) or (@source.class == Variable and @source.constant == 0) then
437
+ if (@source.is_a?(Numeric) and @source == 0) or (@source.instance_of? Variable and @source.constant == 0) then
438
438
  begin
439
439
  instruction = intrinsics(:SETZERO, @return_type.type)
440
440
  return @return_type.copy("#{instruction}( )", DISCARD_OPTIONS) if instruction
@@ -464,9 +464,9 @@ module BOAST
464
464
 
465
465
  def pr
466
466
  s=""
467
- s += indent
468
- s += to_s
469
- s += ";" if [C, CL, CUDA].include?( lang )
467
+ s << indent
468
+ s << to_s
469
+ s << ";" if [C, CL, CUDA].include?( lang )
470
470
  output.puts s
471
471
  return self
472
472
  end
@@ -500,9 +500,9 @@ module BOAST
500
500
  tar = @target.to_var if @target.respond_to?(:to_var)
501
501
  src = @source
502
502
  src = @source.to_var if @source.respond_to?(:to_var)
503
- if tar.class == Variable and tar.type.vector_length > 1 then
503
+ if tar.instance_of? Variable and tar.type.vector_length > 1 then
504
504
  return @target.copy("#{@target} = #{Load(@source, @target, @options)}", DISCARD_OPTIONS)
505
- elsif src.class == Variable and src.type.vector_length > 1 then
505
+ elsif src.instance_of? Variable and src.type.vector_length > 1 then
506
506
  r_t, _ = transition(tar, src, Affectation)
507
507
  opts = @options.clone
508
508
  opts[:store_type] = r_t
@@ -517,9 +517,9 @@ module BOAST
517
517
 
518
518
  def pr
519
519
  s=""
520
- s += indent
521
- s += to_s
522
- s += ";" if [C, CL, CUDA].include?( lang )
520
+ s << indent
521
+ s << to_s
522
+ s << ";" if [C, CL, CUDA].include?( lang )
523
523
  output.puts s
524
524
  return self
525
525
  end
@@ -555,7 +555,7 @@ module BOAST
555
555
  if lang == C or lang == CL then
556
556
  if @source.kind_of?(Array) then
557
557
  return Set(@source, @return_type).to_var
558
- elsif @source.class == Variable or @source.respond_to?(:to_var) then
558
+ elsif @source.instance_of? Variable or @source.respond_to?(:to_var) then
559
559
  src_var = source.to_var
560
560
  if src_var.type == @return_type.type then
561
561
  return src_var
@@ -574,14 +574,14 @@ module BOAST
574
574
  if mask and not mask.full? then
575
575
  return Set(0, @return_type) if @zero and mask.empty?
576
576
  return @return_type if mask.empty?
577
- sym += "MASK"
578
- sym += "Z" if @zero
579
- sym += "_"
577
+ sym << "MASK"
578
+ sym << "Z" if @zero
579
+ sym << "_"
580
580
  end
581
581
  if src_var.alignment and @return_type.type.total_size and ( src_var.alignment % @return_type.type.total_size ) == 0 then
582
- sym += "LOADA"
582
+ sym << "LOADA"
583
583
  else
584
- sym += "LOAD"
584
+ sym << "LOAD"
585
585
  end
586
586
  instruction = intrinsics( sym.to_sym, @return_type.type)
587
587
  if mask and not mask.full? then
@@ -596,7 +596,7 @@ module BOAST
596
596
  elsif lang == FORTRAN then
597
597
  if @source.kind_of?(Array) then
598
598
  return Set(@source, @return_type).to_var
599
- elsif @source.class == Variable or @source.respond_to?(:to_var) then
599
+ elsif @source.instance_of? Variable or @source.respond_to?(:to_var) then
600
600
  if @source.to_var.type == @return_type.type then
601
601
  return @source.to_var
602
602
  elsif @source.kind_of?(Index) and @return_type.type.vector_length > 1 then
@@ -613,9 +613,9 @@ module BOAST
613
613
 
614
614
  def pr
615
615
  s=""
616
- s += indent
617
- s += to_s
618
- s += ";" if [C, CL, CUDA].include?( lang )
616
+ s << indent
617
+ s << to_s
618
+ s << ";" if [C, CL, CUDA].include?( lang )
619
619
  output.puts s
620
620
  return self
621
621
  end
@@ -667,7 +667,7 @@ module BOAST
667
667
  src = src[1..-1]
668
668
  end
669
669
  p_type = type.copy(:vector_length => 1)
670
- s += "#{instruction}( (#{p_type.decl} * ) #{src}, #{get_mask} )"
670
+ s << "#{instruction}( (#{p_type.decl} * ) #{src}, #{get_mask} )"
671
671
  return @return_type.copy( s, DISCARD_OPTIONS)
672
672
  end
673
673
 
@@ -677,9 +677,9 @@ module BOAST
677
677
 
678
678
  def pr
679
679
  s=""
680
- s += indent
681
- s += to_s
682
- s += ";" if [C, CL, CUDA].include?( lang )
680
+ s << indent
681
+ s << to_s
682
+ s << ";" if [C, CL, CUDA].include?( lang )
683
683
  output.puts s
684
684
  return self
685
685
  end
@@ -725,11 +725,11 @@ module BOAST
725
725
  mask = nil
726
726
  mask = Mask(@mask, :length => @store_type.type.vector_length) if @mask
727
727
  return "" if mask and mask.empty?
728
- sym += "MASK_" if mask and not mask.full?
728
+ sym << "MASK_" if mask and not mask.full?
729
729
  if @dest.alignment and type.total_size and ( @dest.alignment % type.total_size ) == 0 then
730
- sym += "STOREA"
730
+ sym << "STOREA"
731
731
  else
732
- sym += "STORE"
732
+ sym << "STORE"
733
733
  end
734
734
  instruction = intrinsics(sym.to_sym, type)
735
735
  p_type = type.copy(:vector_length => 1)
@@ -746,9 +746,9 @@ module BOAST
746
746
 
747
747
  def pr
748
748
  s=""
749
- s += indent
750
- s += to_s
751
- s += ";" if [C, CL, CUDA].include?( lang )
749
+ s << indent
750
+ s << to_s
751
+ s << ";" if [C, CL, CUDA].include?( lang )
752
752
  output.puts s
753
753
  return self
754
754
  end
@@ -799,16 +799,16 @@ module BOAST
799
799
  dst = dst[1..-1]
800
800
  end
801
801
  p_type = type.copy(:vector_length => 1)
802
- return s += "#{instruction}( (#{p_type.decl} * ) #{dst}, #{get_mask}, #{Operator.convert(@source, type)} )"
802
+ return s << "#{instruction}( (#{p_type.decl} * ) #{dst}, #{get_mask}, #{Operator.convert(@source, type)} )"
803
803
  end
804
804
 
805
805
  def pr
806
806
  ss = to_s
807
807
  if ss then
808
808
  s=""
809
- s += indent
810
- s += ss
811
- s += ";" if [C, CL, CUDA].include?( lang )
809
+ s << indent
810
+ s << ss
811
+ s << ";" if [C, CL, CUDA].include?( lang )
812
812
  output.puts s
813
813
  end
814
814
  return self
@@ -877,9 +877,9 @@ module BOAST
877
877
 
878
878
  def pr
879
879
  s=""
880
- s += indent
881
- s += to_s
882
- s += ";" if [C, CL, CUDA].include?( lang )
880
+ s << indent
881
+ s << to_s
882
+ s << ";" if [C, CL, CUDA].include?( lang )
883
883
  output.puts s
884
884
  return self
885
885
  end
@@ -948,9 +948,9 @@ module BOAST
948
948
 
949
949
  def pr
950
950
  s=""
951
- s += indent
952
- s += to_s
953
- s += ";" if [C, CL, CUDA].include?( lang )
951
+ s << indent
952
+ s << to_s
953
+ s << ";" if [C, CL, CUDA].include?( lang )
954
954
  output.puts s
955
955
  return self
956
956
  end
@@ -982,9 +982,9 @@ module BOAST
982
982
 
983
983
  def pr
984
984
  s=""
985
- s += indent
986
- s += to_s
987
- s += ";" if [C, CL, CUDA].include?( lang )
985
+ s << indent
986
+ s << to_s
987
+ s << ";" if [C, CL, CUDA].include?( lang )
988
988
  output.puts s
989
989
  return self
990
990
  end
@@ -1002,7 +1002,7 @@ module BOAST
1002
1002
  def to_s_fortran
1003
1003
  op1, op2 = op_to_var
1004
1004
  if @return_type and @return_type.type.kind_of?(Real) and ( not op1.type.kind_of?(Real) or not op2.type.kind_of?(Real) ) then
1005
- return "modulo(real(#{op1}, #{@return_type.type.size}), #{op2})" if not op1.type.kind_of?(Real)
1005
+ return "modulo(real(#{op1}, #{@return_type.type.size}), #{op2})" unless op1.type.kind_of?(Real)
1006
1006
  return "modulo(#{op1}, real(#{op2}, #{@return_type.type.size}))"
1007
1007
  else
1008
1008
  return "modulo(#{op1}, #{op2})"
@@ -1013,12 +1013,24 @@ module BOAST
1013
1013
  op1, op2 = op_to_var
1014
1014
  if @return_type and @return_type.type.kind_of?(Real) then
1015
1015
  if @return_type.type.size <= 4 then
1016
- return "((#{op1} < 0) ^ (#{op2} < 0) ? fmodf(#{op1}, #{op2}) + #{op2} : fmodf(#{op1}, #{op2}));"
1016
+ return "((#{op1} < 0) ^ (#{op2} < 0) ? fmodf(#{op1}, #{op2}) + #{op2} : fmodf(#{op1}, #{op2}))"
1017
1017
  else
1018
1018
  return "((#{op1} < 0) ^ (#{op2} < 0) ? fmod(#{op1}, #{op2}) + #{op2} : fmod(#{op1}, #{op2}))"
1019
1019
  end
1020
1020
  else
1021
- return "((#{op1} < 0) ^ (#{op2} < 0) ? (#{op1} % #{op2}) + #{op2} : #{op1} % #{op2})"
1021
+ test_op1 = true
1022
+ test_op1 = false if op1.respond_to?(:type) and op1.type.respond_to?(:signed?) and not op1.type.signed?
1023
+ test_op2 = true
1024
+ test_op2 = false if op2.respond_to?(:type) and op2.type.respond_to?(:signed?) and not op2.type.signed?
1025
+ if test_op1 and test_op2 then
1026
+ return "((#{op1} < 0) ^ (#{op2} < 0) ? (#{op1} % #{op2}) + #{op2} : #{op1} % #{op2})"
1027
+ elsif test_op1 then
1028
+ return "( (#{op1} < 0) ? (#{op1} % #{op2.cast(op1)}) + #{op2} : #{op1} % #{op2})"
1029
+ elsif test_op2 then
1030
+ return "( (#{op2} < 0) ? (#{op1.cast(op2)} % #{op2}) + #{op2} : #{op1} % #{op2})"
1031
+ else
1032
+ return "(#{op1} % #{op2})"
1033
+ end
1022
1034
  end
1023
1035
  end
1024
1036
 
@@ -1056,9 +1068,9 @@ module BOAST
1056
1068
 
1057
1069
  def pr
1058
1070
  s=""
1059
- s += indent
1060
- s += to_s
1061
- s += ";" if [C, CL, CUDA].include?( lang )
1071
+ s << indent
1072
+ s << to_s
1073
+ s << ";" if [C, CL, CUDA].include?( lang )
1062
1074
  output.puts s
1063
1075
  return self
1064
1076
  end
@@ -17,19 +17,19 @@ module BOAST
17
17
  def to_s
18
18
  s = ""
19
19
  if lang == FORTRAN then
20
- s += "!#{@name}$"
20
+ s << "!#{@name}$"
21
21
  else
22
- s += "#pragma #{name}"
22
+ s << "#pragma #{name}"
23
23
  end
24
24
  @options.each{ |opt|
25
- s += " #{opt}"
25
+ s << " #{opt}"
26
26
  }
27
27
  return s
28
28
  end
29
29
 
30
30
  def pr
31
31
  s=""
32
- s += to_s
32
+ s << to_s
33
33
  output.puts s
34
34
  return self
35
35
  end
@@ -39,14 +39,14 @@ module BOAST
39
39
  # @private
40
40
  def boast_header(lang=C)
41
41
  s = boast_header_s(lang)
42
- s += ";\n"
42
+ s << ";\n"
43
43
  output.print s
44
44
  return self
45
45
  end
46
46
 
47
47
  def call(*parameters)
48
48
  prefix = ""
49
- prefix += "call " if lang==FORTRAN and @properties[:return].nil?
49
+ prefix << "call " if lang==FORTRAN and @properties[:return].nil?
50
50
  f = FuncCall::new(@name, *parameters, :return => @properties[:return] )
51
51
  f.prefix = prefix
52
52
  return f
@@ -114,50 +114,50 @@ module BOAST
114
114
  s = ""
115
115
  if lang == CL then
116
116
  if @properties[:local] then
117
- s += "#if __OPENCL_C_VERSION__ && __OPENCL_C_VERSION__ >= 120\n"
118
- s += "static\n"
119
- s += "#endif\n"
117
+ s << "#if __OPENCL_C_VERSION__ && __OPENCL_C_VERSION__ >= 120\n"
118
+ s << "static\n"
119
+ s << "#endif\n"
120
120
  else
121
- s += "__kernel "
121
+ s << "__kernel "
122
122
  wgs = @properties[:reqd_work_group_size]
123
123
  if wgs then
124
- s += "__attribute__((reqd_work_group_size(#{wgs[0]},#{wgs[1]},#{wgs[2]}))) "
124
+ s << "__attribute__((reqd_work_group_size(#{wgs[0]},#{wgs[1]},#{wgs[2]}))) "
125
125
  end
126
126
  end
127
127
  elsif lang == CUDA then
128
128
  if @properties[:local] then
129
- s += "static __device__ "
129
+ s << "static __device__ "
130
130
  else
131
- s += "__global__ "
131
+ s << "__global__ "
132
132
  wgs = @properties[:reqd_work_group_size]
133
133
  if wgs then
134
- s += "__launch_bounds__(#{wgs[0]}*#{wgs[1]}*#{wgs[2]}) "
134
+ s << "__launch_bounds__(#{wgs[0]}*#{wgs[1]}*#{wgs[2]}) "
135
135
  end
136
136
  end
137
137
  elsif lang == C then
138
138
  if @properties[:local] then
139
- s += "static "
139
+ s << "static "
140
140
  end
141
141
  if @properties[:inline] then
142
- s+= "inline "
142
+ s << "inline "
143
143
  end
144
144
  end
145
145
  if @properties[:qualifiers] then
146
- s += "#{@properties[:qualifiers]} "
146
+ s << "#{@properties[:qualifiers]} "
147
147
  end
148
148
  if @properties[:return] then
149
- s += "#{@properties[:return].type.decl} "
149
+ s << "#{@properties[:return].type.decl} "
150
150
  else
151
- s += "void "
151
+ s << "void "
152
152
  end
153
- s += "#{@name}("
153
+ s << "#{@name}("
154
154
  if @parameters.first then
155
- s += @parameters.first.send(:decl_c_s, @properties[:local])
155
+ s << @parameters.first.send(:decl_c_s, @properties[:local])
156
156
  @parameters[1..-1].each { |p|
157
- s += ", "+p.send(:decl_c_s, @properties[:local])
157
+ s << ", "+p.send(:decl_c_s, @properties[:local])
158
158
  }
159
159
  end
160
- s += ")"
160
+ s << ")"
161
161
  return s
162
162
  end
163
163
 
@@ -170,13 +170,13 @@ module BOAST
170
170
  def to_s_fortran
171
171
  s = ""
172
172
  if @properties[:return] then
173
- s += "#{@properties[:return].type.decl} FUNCTION "
173
+ s << "#{@properties[:return].type.decl} FUNCTION "
174
174
  else
175
- s += "SUBROUTINE "
175
+ s << "SUBROUTINE "
176
176
  end
177
- s += "#{@name}("
178
- s += @parameters.collect(&:name).join(", ")
179
- s += ")"
177
+ s << "#{@name}("
178
+ s << @parameters.collect(&:name).join(", ")
179
+ s << ")"
180
180
  end
181
181
 
182
182
  def open_c
@@ -205,7 +205,7 @@ module BOAST
205
205
 
206
206
  def open_fortran
207
207
  s = indent + to_s_fortran
208
- s += "\n"
208
+ s << "\n"
209
209
  increment_indent_level
210
210
  tmp_buff = StringIO::new
211
211
  push_env( :output => tmp_buff ) {
@@ -214,8 +214,8 @@ module BOAST
214
214
  }
215
215
  }
216
216
  tmp_buff.rewind
217
- s += tmp_buff.read
218
- s += indent + "integer, parameter :: wp=kind(1.0d0)"
217
+ s << tmp_buff.read
218
+ s << indent + "integer, parameter :: wp=kind(1.0d0)"
219
219
  output.puts s
220
220
  @constants.each { |c|
221
221
  BOAST::decl c
@@ -247,9 +247,9 @@ module BOAST
247
247
 
248
248
  def close_c
249
249
  s = ""
250
- s += indent + "return #{@properties[:return]};\n" if @properties[:return]
250
+ s << indent + "return #{@properties[:return]};\n" if @properties[:return]
251
251
  decrement_indent_level
252
- s += indent + "}"
252
+ s << indent + "}"
253
253
  output.puts s
254
254
  return self
255
255
  end
@@ -257,12 +257,12 @@ module BOAST
257
257
  def close_fortran
258
258
  s = ""
259
259
  if @properties[:return] then
260
- s += indent + "#{@name} = #{@properties[:return]}\n"
260
+ s << indent + "#{@name} = #{@properties[:return]}\n"
261
261
  decrement_indent_level
262
- s += indent + "END FUNCTION #{@name}"
262
+ s << indent + "END FUNCTION #{@name}"
263
263
  else
264
264
  decrement_indent_level
265
- s += indent + "END SUBROUTINE #{@name}"
265
+ s << indent + "END SUBROUTINE #{@name}"
266
266
  end
267
267
  output.puts s
268
268
  return self
@@ -271,38 +271,38 @@ module BOAST
271
271
  def boast_header_s( lang=C )
272
272
  s = ""
273
273
  headers.each { |h|
274
- s += "#include <#{h}>\n"
274
+ s << "#include <#{h}>\n"
275
275
  }
276
276
  if lang == CL then
277
- s += "__kernel "
277
+ s << "__kernel "
278
278
  wgs = @properties[:reqd_work_group_size]
279
279
  if wgs then
280
- s += "__attribute__((reqd_work_group_size(#{wgs[0]},#{wgs[1]},#{wgs[2]}))) "
280
+ s << "__attribute__((reqd_work_group_size(#{wgs[0]},#{wgs[1]},#{wgs[2]}))) "
281
281
  end
282
282
  end
283
283
  trailer = ""
284
- trailer += "_" if lang == FORTRAN
285
- trailer += "_wrapper" if lang == CUDA
284
+ trailer << "_" if lang == FORTRAN
285
+ trailer << "_wrapper" if lang == CUDA
286
286
  if @properties[:return] then
287
- s += "#{@properties[:return].type.decl} "
287
+ s << "#{@properties[:return].type.decl} "
288
288
  elsif lang == CUDA
289
- s += "unsigned long long int "
289
+ s << "unsigned long long int "
290
290
  else
291
- s += "void "
291
+ s << "void "
292
292
  end
293
- s += "#{@name}#{trailer}("
293
+ s << "#{@name}#{trailer}("
294
294
  if @parameters.first then
295
- s += @parameters.first.boast_header(lang)
295
+ s << @parameters.first.boast_header(lang)
296
296
  @parameters[1..-1].each { |p|
297
- s += ", "
298
- s += p.boast_header(lang)
297
+ s << ", "
298
+ s << p.boast_header(lang)
299
299
  }
300
300
  end
301
301
  if lang == CUDA then
302
- s += ", " if parameters.first
303
- s += "size_t *_boast_block_number, size_t *_boast_block_size, int _boast_repeat"
302
+ s << ", " if parameters.first
303
+ s << "size_t *_boast_block_number, size_t *_boast_block_size, int _boast_repeat"
304
304
  end
305
- s += ")"
305
+ s << ")"
306
306
  return s
307
307
  end
308
308