irb 1.8.3 → 1.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 332023cd842d367c49ca245616a926f8f8df1f724c16b22d18447f1456710290
4
- data.tar.gz: c6b7ab4d628a6fb766bed6f93b124490b1c364ba79b7ce8c1e4e21d67df64545
3
+ metadata.gz: 810aa12a1fbfdbd0ff9f9c4d5c4ac71e62e8241f87cb1a306be1ff81aaef08cb
4
+ data.tar.gz: 781d169232ac355b9723e8dc03d6c0de4c16327b4a4a3339cd1e90bad7e1c0b1
5
5
  SHA512:
6
- metadata.gz: 8962b3e7359c4292bb09c75f3a5f1548b5dd53c3053a4a736a0a295c83126b065eca7578a185a0bf218468053399a3f029e47a06860ac69713756fb346945d08
7
- data.tar.gz: '0109b52f17e50b944d5fb3e9e40b432a3a3bafbf45ed9b0dd5725de43527e5ca3926db65b44bf39c646f76c38c4dee07cd2eb8fcb33690dc071dcd4a66b99b87'
6
+ metadata.gz: 51d97e80d977c6ce100f66cc0ad4becae17600d36653be88cc7c2f1e6cd0b7127cec01344bc69b04de1b043f8d45a1d07f6b4b8ad3fad4584585aca269cfa65f
7
+ data.tar.gz: a197cf8c07a1ea728a3c9d0d70fdd28a2a5b9a4e9ebf72a8fd8d6495627cf4c369e6831879a766c5dd8819438bb1f126b0d4d669becc4c63a7ad6c4ea51a3a79
data/Gemfile CHANGED
@@ -17,3 +17,10 @@ gem "rake"
17
17
  gem "test-unit"
18
18
  gem "test-unit-ruby-core"
19
19
  gem "debug", github: "ruby/debug"
20
+
21
+ gem "racc"
22
+
23
+ if RUBY_VERSION >= "3.0.0"
24
+ gem "rbs"
25
+ gem "prism", ">= 0.17.1"
26
+ end
data/README.md CHANGED
@@ -52,6 +52,10 @@ $ gem install irb
52
52
 
53
53
  ## Usage
54
54
 
55
+ > **Note**
56
+ >
57
+ > We're working hard to match Pry's variety of powerful features in IRB, and you can track our progress or find contribution ideas in [this document](https://github.com/ruby/irb/blob/master/COMPARED_WITH_PRY.md).
58
+
55
59
  ### The `irb` Executable
56
60
 
57
61
  You can start a fresh IRB session by typing `irb` in your terminal.
@@ -231,6 +235,79 @@ However, there are also some limitations to be aware of:
231
235
  2. As IRB [doesn't currently support remote-connection](https://github.com/ruby/irb/issues/672), it can't be used with `debug.gem`'s remote debugging feature.
232
236
  3. Access to the previous return value via the underscore `_` is not supported.
233
237
 
238
+ ## Type Based Completion
239
+
240
+ IRB's default completion `IRB::RegexpCompletor` uses Regexp. IRB has another experimental completion `IRB::TypeCompletion` that uses type analysis.
241
+
242
+ ### How to Enable IRB::TypeCompletion
243
+
244
+ To enable IRB::TypeCompletion, run IRB with `--type-completor` option
245
+ ```
246
+ $ irb --type-completor
247
+ ```
248
+ Or write the code below to IRB's rc-file.
249
+ ```ruby
250
+ IRB.conf[:COMPLETOR] = :type # default is :regexp
251
+ ```
252
+ You also need `gem prism` and `gem rbs` to use this feature.
253
+
254
+ To check if it's enabled, type `irb_info` into IRB and see the `Completion` section.
255
+ ```
256
+ irb(main):001> irb_info
257
+ ...
258
+ # Enabled
259
+ Completion: Autocomplete, TypeCompletion::Completor(Prism: 0.17.1, RBS: 3.3.0)
260
+ # Not enabled
261
+ Completion: Autocomplete, RegexpCompletor
262
+ ...
263
+ ```
264
+ If you have `sig/` directory or `rbs_collection.lock.yaml` in current directory, IRB will load it.
265
+
266
+ ### Advantage over Default IRB::RegexpCompletor
267
+
268
+ IRB::TypeCompletion can autocomplete chained methods, block parameters and more if type information is available.
269
+ These are some examples IRB::RegexpCompletor cannot complete.
270
+
271
+ ```ruby
272
+ irb(main):001> 'Ruby'.upcase.chars.s # Array methods (sample, select, shift, size)
273
+ ```
274
+
275
+ ```ruby
276
+ irb(main):001> 10.times.map(&:to_s).each do |s|
277
+ irb(main):002> s.up # String methods (upcase, upcase!, upto)
278
+ ```
279
+
280
+ ```ruby
281
+ irb(main):001> class User < ApplicationRecord
282
+ irb(main):002> def foo
283
+ irb(main):003> sa # save, save!
284
+ ```
285
+
286
+ As a trade-off, completion calculation takes more time than IRB::RegexpCompletor.
287
+
288
+ ### Difference between Steep's Completion
289
+
290
+ Compared with Steep, IRB::TypeCompletion has some difference and limitations.
291
+ ```ruby
292
+ [0, 'a'].sample.
293
+ # Steep completes intersection of Integer methods and String methods
294
+ # IRB::TypeCompletion completes both Integer and String methods
295
+ ```
296
+
297
+ Some features like type narrowing is not implemented.
298
+ ```ruby
299
+ def f(arg = [0, 'a'].sample)
300
+ if arg.is_a?(String)
301
+ arg. # Completes both Integer and String methods
302
+ ```
303
+
304
+ Unlike other static type checker, IRB::TypeCompletion uses runtime information to provide better completion.
305
+ ```ruby
306
+ irb(main):001> a = [1]
307
+ => [1]
308
+ irb(main):002> a.first. # Completes Integer methods
309
+ ```
310
+
234
311
  ## Configuration
235
312
 
236
313
  ### Environment Variables
@@ -269,6 +346,30 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
269
346
 
270
347
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/irb.
271
348
 
349
+ ### Set up the environment
350
+
351
+ 1. Fork the project to your GithHub account
352
+ 2. Clone the fork with `git clone git@github.com:[your_username]/irb.git`
353
+ 3. Run `bundle install`
354
+ 4. Run `bundle exec rake` to make sure tests pass locally
355
+
356
+ ### Run integration tests
357
+
358
+ If your changes affect component rendering, such as the autocompletion's dialog/dropdown, you may need to run IRB's integration tests, known as `yamatanarroroti`.
359
+
360
+ Before running these tests, ensure that you have `libvterm` installed. If you're using Homebrew, you can install it by running:
361
+
362
+ ```bash
363
+ brew install libvterm
364
+ ```
365
+
366
+ After installing `libvterm`, you can run the integration tests using the following commands:
367
+
368
+ ```bash
369
+ WITH_VTERM=1 bundle install
370
+ WITH_VTERM=1 bundle exec rake test test_yamatanooroti
371
+ ```
372
+
272
373
  ## Releasing
273
374
 
274
375
  ```
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test" << "test/lib"
6
6
  t.libs << "lib"
7
7
  t.ruby_opts << "-rhelper"
8
- t.test_files = FileList["test/irb/test_*.rb"]
8
+ t.test_files = FileList["test/irb/test_*.rb", "test/irb/type_completion/test_*.rb"]
9
9
  end
10
10
 
11
11
  # To make sure they have been correctly setup for Ruby CI.
@@ -13,7 +13,7 @@ desc "Run each irb test file in isolation."
13
13
  task :test_in_isolation do
14
14
  failed = false
15
15
 
16
- FileList["test/irb/test_*.rb"].each do |test_file|
16
+ FileList["test/irb/test_*.rb", "test/irb/type_completion/test_*.rb"].each do |test_file|
17
17
  ENV["TEST"] = test_file
18
18
  begin
19
19
  Rake::Task["test"].execute
@@ -14,6 +14,7 @@ module IRB
14
14
  str = "Ruby version: #{RUBY_VERSION}\n"
15
15
  str += "IRB version: #{IRB.version}\n"
16
16
  str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
17
+ str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
17
18
  str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
18
19
  str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
19
20
  str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
@@ -9,6 +9,30 @@ require_relative 'ruby-lex'
9
9
 
10
10
  module IRB
11
11
  class BaseCompletor # :nodoc:
12
+
13
+ # Set of reserved words used by Ruby, you should not use these for
14
+ # constants or variables
15
+ ReservedWords = %w[
16
+ __ENCODING__ __LINE__ __FILE__
17
+ BEGIN END
18
+ alias and
19
+ begin break
20
+ case class
21
+ def defined? do
22
+ else elsif end ensure
23
+ false for
24
+ if in
25
+ module
26
+ next nil not
27
+ or
28
+ redo rescue retry return
29
+ self super
30
+ then true
31
+ undef unless until
32
+ when while
33
+ yield
34
+ ]
35
+
12
36
  def completion_candidates(preposing, target, postposing, bind:)
13
37
  raise NotImplementedError
14
38
  end
@@ -94,28 +118,9 @@ module IRB
94
118
  end
95
119
  }
96
120
 
97
- # Set of reserved words used by Ruby, you should not use these for
98
- # constants or variables
99
- ReservedWords = %w[
100
- __ENCODING__ __LINE__ __FILE__
101
- BEGIN END
102
- alias and
103
- begin break
104
- case class
105
- def defined? do
106
- else elsif end ensure
107
- false for
108
- if in
109
- module
110
- next nil not
111
- or
112
- redo rescue retry return
113
- self super
114
- then true
115
- undef unless until
116
- when while
117
- yield
118
- ]
121
+ def inspect
122
+ 'RegexpCompletor'
123
+ end
119
124
 
120
125
  def complete_require_path(target, preposing, postposing)
121
126
  if target =~ /\A(['"])([^'"]+)\Z/
data/lib/irb/context.rb CHANGED
@@ -86,14 +86,14 @@ module IRB
86
86
  when nil
87
87
  if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
88
88
  # Both of multiline mode and singleline mode aren't specified.
89
- @io = RelineInputMethod.new
89
+ @io = RelineInputMethod.new(build_completor)
90
90
  else
91
91
  @io = nil
92
92
  end
93
93
  when false
94
94
  @io = nil
95
95
  when true
96
- @io = RelineInputMethod.new
96
+ @io = RelineInputMethod.new(build_completor)
97
97
  end
98
98
  unless @io
99
99
  case use_singleline?
@@ -149,6 +149,43 @@ module IRB
149
149
  @command_aliases = IRB.conf[:COMMAND_ALIASES]
150
150
  end
151
151
 
152
+ private def build_completor
153
+ completor_type = IRB.conf[:COMPLETOR]
154
+ case completor_type
155
+ when :regexp
156
+ return RegexpCompletor.new
157
+ when :type
158
+ completor = build_type_completor
159
+ return completor if completor
160
+ else
161
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
162
+ end
163
+ # Fallback to RegexpCompletor
164
+ RegexpCompletor.new
165
+ end
166
+
167
+ TYPE_COMPLETION_REQUIRED_PRISM_VERSION = '0.17.1'
168
+
169
+ private def build_type_completor
170
+ unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') && RUBY_ENGINE != 'truffleruby'
171
+ warn 'TypeCompletion requires RUBY_VERSION >= 3.0.0'
172
+ return
173
+ end
174
+ begin
175
+ require 'prism'
176
+ rescue LoadError => e
177
+ warn "TypeCompletion requires Prism: #{e.message}"
178
+ return
179
+ end
180
+ unless Gem::Version.new(Prism::VERSION) >= Gem::Version.new(TYPE_COMPLETION_REQUIRED_PRISM_VERSION)
181
+ warn "TypeCompletion requires Prism::VERSION >= #{TYPE_COMPLETION_REQUIRED_PRISM_VERSION}"
182
+ return
183
+ end
184
+ require 'irb/type_completion/completor'
185
+ TypeCompletion::Types.preload_in_thread
186
+ TypeCompletion::Completor.new
187
+ end
188
+
152
189
  def save_history=(val)
153
190
  IRB.conf[:SAVE_HISTORY] = val
154
191
  end
@@ -98,18 +98,26 @@ module IRB
98
98
  end
99
99
  end
100
100
 
101
+ private def easter_egg_logo(type)
102
+ @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
103
+ .split(/TYPE: ([A-Z]+)\n/)[1..]
104
+ .each_slice(2)
105
+ .to_h
106
+ @easter_egg_logos[type.to_s.upcase]
107
+ end
108
+
101
109
  private def easter_egg(type = nil)
102
110
  type ||= [:logo, :dancing].sample
103
111
  case type
104
112
  when :logo
105
- File.open(File.join(__dir__, 'ruby_logo.aa')) do |f|
106
- require "rdoc"
107
- RDoc::RI::Driver.new.page do |io|
108
- IO.copy_stream(f, io)
109
- end
113
+ require "rdoc"
114
+ RDoc::RI::Driver.new.page do |io|
115
+ io.write easter_egg_logo(:large)
110
116
  end
111
117
  when :dancing
112
- begin
118
+ STDOUT.cooked do
119
+ interrupted = false
120
+ prev_trap = trap("SIGINT") { interrupted = true }
113
121
  canvas = Canvas.new(Reline.get_screen_size)
114
122
  Reline::IOGate.set_winch_handler do
115
123
  canvas = Canvas.new(Reline.get_screen_size)
@@ -125,10 +133,12 @@ module IRB
125
133
  buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m"
126
134
  print "\e[H" + buff
127
135
  sleep 0.05
136
+ break if interrupted
128
137
  end
129
138
  rescue Interrupt
130
139
  ensure
131
140
  print "\e[0m\e[?1049l"
141
+ trap("SIGINT", prev_trap)
132
142
  end
133
143
  end
134
144
  end
data/lib/irb/init.rb CHANGED
@@ -5,6 +5,41 @@
5
5
  #
6
6
 
7
7
  module IRB # :nodoc:
8
+ @CONF = {}
9
+ # Displays current configuration.
10
+ #
11
+ # Modifying the configuration is achieved by sending a message to IRB.conf.
12
+ #
13
+ # See IRB@Configuration for more information.
14
+ def IRB.conf
15
+ @CONF
16
+ end
17
+
18
+ def @CONF.inspect
19
+ array = []
20
+ for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name}
21
+ case k
22
+ when :MAIN_CONTEXT, :__TMP__EHV__
23
+ array.push format("CONF[:%s]=...myself...", k.id2name)
24
+ when :PROMPT
25
+ s = v.collect{
26
+ |kk, vv|
27
+ ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"}
28
+ format(":%s=>{%s}", kk.id2name, ss.join(", "))
29
+ }
30
+ array.push format("CONF[:%s]={%s}", k.id2name, s.join(", "))
31
+ else
32
+ array.push format("CONF[:%s]=%s", k.id2name, v.inspect)
33
+ end
34
+ end
35
+ array.join("\n")
36
+ end
37
+
38
+ # Returns the current version of IRB, including release version and last
39
+ # updated date.
40
+ def IRB.version
41
+ format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE)
42
+ end
8
43
 
9
44
  # initialize config
10
45
  def IRB.setup(ap_path, argv: ::ARGV)
@@ -28,6 +63,7 @@ module IRB # :nodoc:
28
63
  unless ap_path and @CONF[:AP_NAME]
29
64
  ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb")
30
65
  end
66
+ @CONF[:VERSION] = version
31
67
  @CONF[:AP_NAME] = File::basename(ap_path, ".rb")
32
68
 
33
69
  @CONF[:IRB_NAME] = "irb"
@@ -40,6 +76,7 @@ module IRB # :nodoc:
40
76
  @CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
41
77
  @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
42
78
  @CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false"
79
+ @CONF[:COMPLETOR] = :regexp
43
80
  @CONF[:INSPECT_MODE] = true
44
81
  @CONF[:USE_TRACER] = false
45
82
  @CONF[:USE_LOADER] = false
@@ -293,6 +330,10 @@ module IRB # :nodoc:
293
330
  @CONF[:USE_AUTOCOMPLETE] = true
294
331
  when "--noautocomplete"
295
332
  @CONF[:USE_AUTOCOMPLETE] = false
333
+ when "--regexp-completor"
334
+ @CONF[:COMPLETOR] = :regexp
335
+ when "--type-completor"
336
+ @CONF[:COMPLETOR] = :type
296
337
  when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/
297
338
  opt = $1 || argv.shift
298
339
  prompt_mode = opt.upcase.tr("-", "_").intern
@@ -193,6 +193,10 @@ module IRB
193
193
  }
194
194
  end
195
195
 
196
+ def completion_info
197
+ 'RegexpCompletor'
198
+ end
199
+
196
200
  # Reads the next line from this input method.
197
201
  #
198
202
  # See IO#gets for more information.
@@ -230,13 +234,13 @@ module IRB
230
234
  HISTORY = Reline::HISTORY
231
235
  include HistorySavingAbility
232
236
  # Creates a new input method object using Reline
233
- def initialize
237
+ def initialize(completor)
234
238
  IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false)
235
239
 
236
- super
240
+ super()
237
241
 
238
242
  @eof = false
239
- @completor = RegexpCompletor.new
243
+ @completor = completor
240
244
 
241
245
  Reline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
242
246
  Reline.completion_append_character = nil
@@ -270,6 +274,11 @@ module IRB
270
274
  end
271
275
  end
272
276
 
277
+ def completion_info
278
+ autocomplete_message = Reline.autocompletion ? 'Autocomplete' : 'Tab Complete'
279
+ "#{autocomplete_message}, #{@completor.inspect}"
280
+ end
281
+
273
282
  def check_termination(&block)
274
283
  @check_termination_proc = block
275
284
  end
@@ -303,15 +312,20 @@ module IRB
303
312
  return nil if result.nil? or pointer.nil? or pointer < 0
304
313
 
305
314
  name = doc_namespace.call(result[pointer])
315
+ show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
306
316
 
307
317
  options = {}
308
318
  options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
309
319
  driver = RDoc::RI::Driver.new(options)
310
320
 
311
321
  if key.match?(dialog.name)
312
- begin
313
- driver.display_names([name])
314
- rescue RDoc::RI::Driver::NotFoundError
322
+ if show_easter_egg
323
+ IRB.__send__(:easter_egg)
324
+ else
325
+ begin
326
+ driver.display_names([name])
327
+ rescue RDoc::RI::Driver::NotFoundError
328
+ end
315
329
  end
316
330
  end
317
331
 
@@ -374,8 +388,15 @@ module IRB
374
388
  formatter.width = width
375
389
  dialog.trap_key = alt_d
376
390
  mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
377
- message = "Press #{mod_key}+d to read the full document"
378
- contents = [message] + doc.accept(formatter).split("\n")
391
+ if show_easter_egg
392
+ type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
393
+ contents = IRB.send(:easter_egg_logo, type).split("\n")
394
+ message = "Press #{mod_key}+d to see more"
395
+ contents[0][0, message.size] = message
396
+ else
397
+ message = "Press #{mod_key}+d to read the full document"
398
+ contents = [message] + doc.accept(formatter).split("\n")
399
+ end
379
400
  contents = contents.take(preferred_dialog_height)
380
401
 
381
402
  y = cursor_pos_to_render.y
@@ -30,6 +30,9 @@ Usage: irb.rb [options] [programfile] [arguments]
30
30
  --nocolorize Don't use color-highlighting.
31
31
  --autocomplete Use auto-completion (default).
32
32
  --noautocomplete Don't use auto-completion.
33
+ --regexp-completor
34
+ Use regexp based completion (default).
35
+ --type-completor Use type based completion.
33
36
  --prompt prompt-mode, --prompt-mode prompt-mode
34
37
  Set prompt mode. Pre-defined prompt modes are:
35
38
  'default', 'classic', 'simple', 'inf-ruby', 'xmp', 'null'.
@@ -21,6 +21,9 @@ Usage: irb.rb [options] [programfile] [arguments]
21
21
  --nocolorize 色付けを利用しない.
22
22
  --autocomplete オートコンプリートを利用する.
23
23
  --noautocomplete オートコンプリートを利用しない.
24
+ --regexp-completor
25
+ 補完に正規表現を利用する.
26
+ --type-completor 補完に型情報を利用する.
24
27
  --prompt prompt-mode/--prompt-mode prompt-mode
25
28
  プロンプトモードを切替えます. 現在定義されているプ
26
29
  ロンプトモードは, default, simple, xmp, inf-rubyが
data/lib/irb/ruby_logo.aa CHANGED
@@ -1,3 +1,4 @@
1
+ TYPE: LARGE
1
2
 
2
3
  -+smJYYN?mm-
3
4
  HB"BBYT TQg NggT
@@ -35,3 +36,45 @@
35
36
  m7 NW H N HSVO1z=?11-
36
37
  NgTH bB kH WBHWWHBHWmQgg&gggggNNN
37
38
  NNggggggNN
39
+ TYPE: ASCII
40
+ ,,,;;;;''''';;;'';,
41
+ ,,;'' ';;,;;; ',
42
+ ,,'' ;;'';'''';;;;;;
43
+ ,;' ;; ',, ;
44
+ ,;' ,;' ';, ;
45
+ ;' ,;; ',,,;
46
+ ,' ,;;,,,,,,,,,,,;;;;
47
+ ;' ;;';;;; ,;;
48
+ ;' ,;' ;; '',, ,;;;
49
+ ;; ,;' ; '';, ,; ;'
50
+ ;; ,;;' ;; ;; ;;
51
+ ;;, ,,;;' ; ;'; ;;
52
+ ;';;,,,,;;;;;;;,,, ;; ,' ; ;;
53
+ ; ;;''' ,;'; ''';,,, ; ,;' ;;;;
54
+ ;;;;, ; '; ''';;;' ';;;
55
+ ;'; ;, ;' '; ,;' ', ;;;
56
+ ;;; ; ,; '; ,,' ',, ;;
57
+ ;;; '; ;' ';,,'' ';,;;
58
+ '; ';,; ,,;''''''''';;;;;;,,;;;
59
+ ';,,;;,,;;;;;;;;;;''''''''''''''
60
+ TYPE: UNICODE
61
+ ⣀⣤⣴⣾⣿⣿⣿⡛⠛⠛⠛⠛⣻⣿⠿⠛⠛⠶⣤⡀
62
+ ⣀⣴⠾⠛⠉⠁ ⠙⣿⣶⣤⣶⣟⣉ ⠈⠻⣦
63
+ ⣀⣴⠟⠋ ⢸⣿⠟⠻⣯⡙⠛⠛⠛⠶⠶⠶⢶⣽⣇
64
+ ⣠⡾⠋⠁ ⣾⡿ ⠈⠛⢦⣄ ⣿
65
+ ⣠⡾⠋ ⣰⣿⠃ ⠙⠷⣤⡀ ⣿
66
+ ⢀⡾⠋ ⣰⣿⡏ ⠈⠻⣦⣄⢠⣿
67
+ ⣰⠟⠁ ⣴⣿⣿⣁⣀⣠⣤⣤⣤⣤⣤⣤⣤⣴⠶⠿⣿⡏
68
+ ⣼⠏ ⢀⣾⣿⠟⣿⠿⣯⣍⠁ ⣰⣿⡇
69
+ ⢀⣼⠋ ⢀⣴⣿⠟⠁ ⢸⡇ ⠙⠻⢦⣄⡀ ⢠⡿⣿⡇
70
+ ⢀⣾⡏ ⢀⣴⣿⠟⠁ ⣿ ⠉⠻⢶⣄⡀⣰⡟ ⣿⠃
71
+ ⣾⣿⠁ ⣠⣶⡿⠋⠁ ⢹⡇ ⠈⣿⡏ ⢸⣿
72
+ ⣿⣿⡆ ⢀⣠⣴⣿⡿⠋ ⠈⣿ ⢀⡾⠋⣿ ⢸⣿
73
+ ⣿⠸⣿⣶⣤⣤⣤⣤⣶⣾⠿⠿⣿⣿⠶⣤⣤⣀⡀ ⢹⡇ ⣴⠟⠁ ⣿⡀⢸⣿
74
+ ⣿⢀⣿⣟⠛⠋⠉⠁ ⢰⡟⠹⣧ ⠈⠉⠛⠻⠶⢦⣤⣀⡀ ⠈⣿ ⣠⡾⠃ ⢸⡇⢸⡇
75
+ ⣿⣾⣿⢿⡄ ⣿⠁ ⠘⣧ ⠉⠙⠛⠷⣿⣿⡋ ⠸⣇⣸⡇
76
+ ⣿⠃⣿⠈⢿⡄ ⣸⠇ ⠘⣧ ⢀⣤⠾⠋⠈⠻⣦⡀ ⣿⣿⡇
77
+ ⣿⢸⡏ ⠈⣷⡀ ⢠⡿ ⠘⣧⡀ ⣠⡴⠟⠁ ⠈⠻⣦⣀ ⢿⣿⠁
78
+ ⢻⣾⡇ ⠘⣷ ⣼⠃ ⠘⣷⣠⣴⠟⠋ ⠙⢷⣄⢸⣿
79
+ ⠻⣧⡀ ⠘⣧⣰⡏ ⢀⣠⣤⠶⠛⠉⠛⠛⠛⠛⠛⠛⠻⢶⣶⣶⣶⣶⣶⣤⣤⣽⣿⣿
80
+ ⠈⠛⠷⢦⣤⣽⣿⣥⣤⣶⣶⡿⠿⠿⠶⠶⠶⠶⠾⠛⠛⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠁