mrubyedge 2.1.2

mruby/edge is yet another mruby that is specialized for running on WASM
Documentation
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
##
# Integer ISO Test

assert('Integer', '15.2.8') do
  assert_equal Class, Integer.class
end

assert('Integer#+', '15.2.8.3.1') do
  a = 1+1
  b = 1+1.0 if Object.const_defined?(:Float)

  assert_equal 2, a
  assert_equal 2.0, b if Object.const_defined?(:Float)

  assert_raise(TypeError){ 0+nil }
  assert_raise(TypeError){ 1+nil }
end

assert('Integer#-', '15.2.8.3.2') do
  a = 2-1
  b = 2-1.0 if Object.const_defined?(:Float)

  assert_equal 1, a
  assert_equal 1.0, b if Object.const_defined?(:Float)
end

assert('Integer#*', '15.2.8.3.3') do
  a = 1*1
  assert_equal 1, a
  if Object.const_defined?(:Float)
    b = 1*1.0
    assert_equal 1.0, b
  end
  assert_raise(TypeError){ 0*nil }
  assert_raise(TypeError){ 1*nil }
end

assert('Integer#/', '15.2.8.3.4') do
  a = 2/1
  assert_equal 2, a
  a = 5/2
  assert_equal 2, a
  b = -1/2
  assert_equal(-1, b)
  b = 1/-2
  assert_equal(-1, b)
  skip unless Object.const_defined?(:Float)
  b = 2/1.0
  assert_equal 2.0, b
end

if Object.const_defined?(:Float)
  assert('Integer#quo') do
    a = 6.quo(5)
    assert_equal 1.2, a
  end
end

assert('Integer#%', '15.2.8.3.5') do
  a = 1%1
  b = 2%4
  c = 2%5
  d = 2%-5
  e = -2%5
  f = -2%-5
  g = 2%-2
  h = -2%2
  i = -2%-2

  assert_equal 0, a
  assert_equal 2, b
  assert_equal 2, c
  assert_equal(-3, d)
  assert_equal 3, e
  assert_equal(-2, f)
  assert_equal 0, g
  assert_equal 0, h
  assert_equal 0, i
  assert_raise(ZeroDivisionError) { 0%0 }
  skip unless Object.const_defined?(:Float)
  j = 1%1.0
  assert_equal 0.0, j
  assert_equal 0.0, 0%2.5
  assert_kind_of Float, 0%2.5
  assert_raise(ZeroDivisionError) { 0%0.0 }
end

assert('Integer#<=>', '15.2.9.3.6') do
  a = 1<=>0
  b = 1<=>1
  c = 1<=>2

  assert_equal  1, a
  assert_equal  0, b
  assert_equal(-1, c)
end

assert('Integer#==', '15.2.8.3.7') do
  a = 1==0
  b = 1==1

  assert_false a
  assert_true b
end

assert('Integer comparison with a Float that stands for another number') do
  # An mrb_float keeps fewer significant bits than an mrb_int, so an integer
  # past the significand rounds onto a neighbouring Float when the two are
  # compared as Floats, and answers equal to a Float it is not equal to.
  #
  # The pair is derived rather than written out. 2 ** p, for the p significant
  # bits this build's Float holds, is the first Float that cannot be told from
  # the integer above it, so the pair stays right where mrb_float is narrower
  # than the usual 53 bits. A literal that wide cannot be built where mrb_int
  # is narrow either, and failing to build one drops every test in this file.
  #
  # Where mrb_int cannot hold the integer, mruby-bigint answers the assertions
  # below through mrb_bint_cmp() instead, which is exact for its own reasons;
  # without that gem the build has no such integer at all and this skips.
  skip unless Object.const_defined?(:Float)
  f = 2.0
  f *= 2 while f + 1.0 != f
  begin
    n = f.to_i + 1
  rescue RangeError
    skip 'no mrb_int here is wider than the significand of an mrb_float'
  end

  assert_false(n == f)
  assert_false(n.__send__(:==, f))    # the method, where the line above is an opcode
  assert_equal(1, n <=> f)
  assert_true(n > f)
  assert_true(n.__send__(:>, f))
  assert_true(n >= f)
  assert_false(n < f)
  assert_false(n <= f)
  assert_false([n] == [f])        # Array#== reads the same comparison
end

assert('Integer comparison with a NaN') do
  # The mixed pair is compared exactly rather than as two Floats, and that
  # comparison reports a NaN apart; the answer it stands for is the one a pair
  # of Floats gets, no order at all. See the Float file for what that means.
  skip unless Object.const_defined?(:Float)
  nan = Float::NAN

  assert_nil(1 <=> nan)
  assert_false(1.__send__(:<, nan))
  assert_false(1.__send__(:<=, nan))
  assert_false(1.__send__(:>, nan))
  assert_false(1.__send__(:>=, nan))
  assert_false(1 < nan)           # the opcode, which already answered this
end

assert('Integer wider than an mrb_int compared with a NaN') do
  # A big integer is compared by a path of its own, which reported the NaN as a
  # pair it could not compare at all: `<=>` was already nil, but the four
  # operators raised where the ones above answered false.
  skip unless Object.const_defined?(:Float)
  begin
    big = 1 << 70
  rescue RangeError
    skip 'no integer here is wider than an mrb_int'
  end
  nan = Float::NAN

  assert_nil(big <=> nan)
  assert_nil(nan <=> big)
  assert_false(big.__send__(:<, nan))
  assert_false(big.__send__(:>=, nan))
  assert_false(nan.__send__(:<, big))
  assert_false(nan.__send__(:>=, big))
end

assert('Integer comparison with a Float at the ends of the mrb_int range') do
  # The far ends are where the neighbouring Floats stand furthest apart, and
  # where the bounds the comparison tests against are themselves built. Both
  # ends come from arithmetic for the reason the pair above does.
  #
  # A narrower mrb_int leaves the shift outside the range these name, and the
  # two cases part the same way as above: with mruby-bigint the assertions
  # still hold, answered by mrb_bint_cmp() rather than by the bounds, and
  # without it the shift raises and this skips.
  skip unless Object.const_defined?(:Float)
  begin
    half = 1 << 62
  rescue RangeError
    skip 'an mrb_int here is narrower than 64 bits'
  end
  imin = -half - half             # the least mrb_int, which is exact as a Float
  imax = half - 1 + half          # the greatest, which is not
  fmin = -(2.0 ** 63)
  fmax = 2.0 ** 63
  inf = 1.0 / 0.0

  assert_true(imin == fmin)
  assert_equal(0, imin <=> fmin)
  assert_equal(1, (imin + 3) <=> fmin)
  assert_true((imin + 3) > fmin)
  assert_false(imax == fmax)
  assert_equal(-1, imax <=> fmax)
  assert_true(imax < fmax)
  assert_true(imax < inf)
  assert_true(imin > -inf)
end

assert('Integer comparison of two values too wide to store inline') do
  # Word boxing keeps an Integer in the value itself only while it fits a
  # tagged machine word and allocates an object for a wider one, which is a
  # difference in where the number is kept and not in the number: a pair of
  # them stands in the same order as any other pair. The comparisons are asked
  # for by name so that each is the method's own, the operators being opcodes
  # that read a pair of Integers themselves.
  #
  # 2**30 is past what a 32-bit word carries inline and 2**62 past a 64-bit
  # one, so one pair or the other is outside it wherever this runs. Where an
  # mrb_int is too narrow for the wider pair, mruby-bigint answers the
  # comparison and a build without it raises at the shift, which is what the
  # rescue leaves that pair out for; the shift count is a variable for the
  # reason the big integer tests give, a constant one out of range failing the
  # build rather than raising.
  pairs = [[1 << 30, (1 << 30) + 1]]
  begin
    k = 62
    pairs << [1 << k, (1 << k) + 1]
  rescue RangeError
  end

  pairs.each do |small, large|
    assert_equal(-1, small.__send__(:<=>, large))
    assert_equal(1, large.__send__(:<=>, small))
    assert_equal(0, large.__send__(:<=>, large))
    assert_true(small.__send__(:<, large))
    assert_true(small.__send__(:<=, large))
    assert_false(small.__send__(:>, large))
    assert_false(small.__send__(:>=, large))
    assert_true(large.__send__(:>, small))
  end
end

assert('Integer#~', '15.2.8.3.8') do
  # Complement
  assert_equal(-1, ~0)
  assert_equal(-3, ~2)
end

assert('Integer#&', '15.2.8.3.9') do
  # Bitwise AND
  #   0101 (5)
  # & 0011 (3)
  # = 0001 (1)
  assert_equal 1, 5 & 3
end

assert('Integer#|', '15.2.8.3.10') do
  # Bitwise OR
  #   0101 (5)
  # | 0011 (3)
  # = 0111 (7)
  assert_equal 7, 5 | 3
end

assert('Integer#^', '15.2.8.3.11') do
  # Bitwise XOR
  #   0101 (5)
  # ^ 0011 (3)
  # = 0110 (6)
  assert_equal 6, 5 ^ 3
end

assert('Integer bitwise ops reject non-Integer operands') do
  # A non-Integer operand has no bit pattern to combine, so &, |, ^ raise
  # TypeError instead of silently reading garbage (Float used to return a
  # bogus value via an unchecked union access).
  if Object.const_defined?(:Float)
    # Without float support, a float literal compiles to the integer 0, so
    # these expressions are ordinary integer bitwise ops rather than errors.
    assert_raise(TypeError) { 5 & 5.0 }
    assert_raise(TypeError) { 5 | 5.0 }
    assert_raise(TypeError) { 5 ^ 5.0 }
  end
  assert_raise(TypeError) { 5 | "3" }
  assert_raise(TypeError) { 5 & nil }
  assert_raise(TypeError) { 5 ^ :sym }
end

assert('Integer#<<', '15.2.8.3.12') do
  # Left Shift by one
  #   00010111 (23)
  # = 00101110 (46)
  assert_equal 46, 23 << 1

  # Left Shift by a negative is Right Shift
  assert_equal 23, 46 << -1

  skip unless Object.const_defined?(:Float)
end

assert('Integer#>>', '15.2.8.3.13') do
  # Right Shift by one
  #   00101110 (46)
  # = 00010111 (23)
  assert_equal 23, 46 >> 1

  # Right Shift by a negative is Left Shift
  assert_equal 46, 23 >> -1

  # Don't raise on large Right Shift
  assert_equal 0, 23 >> 128
end

assert('Integer#ceil', '15.2.8.3.14') do
  assert_equal 10, 10.ceil
  assert_equal 10, 10.ceil(2)
  assert_equal 1300, 1234.ceil(-2)
  assert_equal(-1200, -1234.ceil(-2))
  assert_equal 0, (-99).ceil(-2)
end

assert('Integer#ceil keeps a value that is already a multiple') do
  assert_equal 0, 0.ceil(-1)
  assert_equal 10, 10.ceil(-1)
  assert_equal 100, 100.ceil(-2)
  assert_equal 1200, 1200.ceil(-2)
  assert_equal(-100, -100.ceil(-2))
  assert_equal(-1200, -1200.ceil(-2))
end

assert('Integer#downto', '15.2.8.3.15') do
  a = 0
  3.downto(1) do |i|
    a += i
  end
  assert_equal 6, a
end

assert('Integer#eql?', '15.2.8.3.16') do
  a = 1.eql?(1)
  b = 1.eql?(2)
  c = 1.eql?(nil)

  assert_true a
  assert_false b
  assert_false c

  # `eql?` compares class as well as value, unlike `==`.
  if Object.const_defined?(:Float)
    assert_false 1.eql?(1.0)
    assert_true 1 == 1.0
  end
end

assert('Integer#floor', '15.2.8.3.17') do
  a = 1.floor

  assert_equal 1, a
  assert_equal 1, 1.floor(2)
  assert_equal 1200, 1234.floor(-2)
  assert_equal(-1300, -1234.floor(-2))
  assert_equal 0, 99.floor(-2)
end

assert('Integer#floor keeps a value that is already a multiple') do
  assert_equal 0, 0.floor(-1)
  assert_equal(-10, -10.floor(-1))
  assert_equal(-100, -100.floor(-2))
  assert_equal(-1200, -1200.floor(-2))
  assert_equal 100, 100.floor(-2)
  assert_equal 1200, 1200.floor(-2)
end

assert('Integer#next', '15.2.8.3.19') do
  assert_equal 2, 1.next
end

assert('Integer#round', '15.2.8.3.20') do
  assert_equal 1, 1.round
  assert_equal 1, 1.round(2)
  assert_equal 12300, 12345.round(-2)
  assert_equal 12350, 12345.round(-1)
  assert_equal 12000, 12345.round(-3)
  assert_equal(-12300, -12345.round(-2))
  assert_equal 0, 4.round(-1)
  assert_equal 100, 99.round(-2)
  assert_equal 0, 0.round(-1)
end

assert('Integer#round keeps a value that is already a multiple') do
  assert_equal 10, 10.round(-1)
  assert_equal 100, 100.round(-2)
  assert_equal 1200, 1200.round(-2)
  assert_equal(-100, -100.round(-2))
  assert_equal(-1200, -1200.round(-2))
end

assert('Integer#round breaks a tie away from zero') do
  assert_equal 10, 5.round(-1)
  assert_equal 20, 15.round(-1)
  assert_equal 30, 25.round(-1)
  assert_equal 200, 150.round(-2)
  assert_equal 300, 250.round(-2)
  assert_equal(-10, -5.round(-1))
  assert_equal(-20, -15.round(-1))
  assert_equal(-30, -25.round(-1))
  assert_equal(-200, -150.round(-2))
end

assert('Integer#succ', '15.2.8.3.21') do
  assert_equal 2, 1.succ
end

assert('Integer#times', '15.2.8.3.22') do
  a = 0
  3.times do
    a += 1
  end
  assert_equal 3, a
end

assert('Integer#to_f', '15.2.8.3.23') do
  skip unless Object.const_defined?(:Float)
  assert_equal 1.0, 1.to_f
end

assert('Integer#to_i', '15.2.8.3.24') do
  assert_equal 1, 1.to_i
end

assert('Integer#to_s', '15.2.8.3.25') do
  assert_equal "1", 1.to_s
  assert_equal "-1", -1.to_s
  assert_equal "1010", 10.to_s(2)
  assert_equal "a", 10.to_s(36)
  assert_equal "-a", -10.to_s(36)
  assert_equal "30071", 12345.to_s(8)
  assert_raise(ArgumentError) { 10.to_s(-1) }
  assert_raise(ArgumentError) { 10.to_s(0) }
  assert_raise(ArgumentError) { 10.to_s(1) }
  assert_raise(ArgumentError) { 10.to_s(37) }
end

assert('Integer#to_s(2) with the smallest Integer') do
  # The magnitude of the smallest Integer spends every bit an mrb_int has, so
  # base 2 is the one conversion that writes MRB_INT_BIT digits and still
  # takes a sign in front of them. Both widths are asked, so that this
  # build's is among them; the other one names a value that fits, or one out
  # of range without mruby-bigint, which is skipped.
  [31, 63].each do |e|
    begin
      min = -(1 << (e - 1)) * 2
    rescue RangeError
      next
    end
    assert_equal "-1" + "0" * e, min.to_s(2)
  end
end

assert('Integer#truncate', '15.2.8.3.26') do
  assert_equal 1, 1.truncate
  assert_equal 1, 1.truncate(2)
  assert_equal 1200, 1234.truncate(-2)
  assert_equal(-1200, -1234.truncate(-2))
  assert_equal 1200, 1200.truncate(-2)
  assert_equal 0, 99.truncate(-2)
end

assert('Integer rounding to a power of ten wider than an mrb_int') do
  # 10 ** 19 is wider than an mrb_int, so the four methods answer 0 rather than
  # reaching for a power they cannot hold.
  (19..25).each do |n|
    assert_equal 0, 12345.round(-n)
    assert_equal 0, (-12345).round(-n)
    assert_equal 0, 12345.truncate(-n)
    assert_equal 0, 12345.floor(-n)
    assert_equal 0, (-12345).ceil(-n)
  end
end

assert('Integer#upto', '15.2.8.3.27') do
  a = 0
  1.upto(3) do |i|
    a += i
  end
  assert_equal 6, a
end

assert('Integer#divmod', '15.2.8.3.30') do
  assert_equal [ 0,  0],   0.divmod(1)
  assert_equal [ 0,  1],   1.divmod(3)
  assert_equal [ 3,  0],   3.divmod(1)
  assert_equal [ 2,  6],  20.divmod(7)
  assert_equal [-1,  2],  -3.divmod(5)
  assert_equal [-2, -1],  25.divmod(-13)
  assert_equal [ 1, -6], -13.divmod(-7)
end

assert('Integer.__ensure converts its argument without dispatching') do
  # The mrblib idiom used to be `obj.__to_int`, a dispatch the argument could
  # redefine, so an object defining `__to_int` was accepted everywhere an
  # object defining `to_int` is rejected.
  evil = Class.new { def __to_int; 2; end }.new
  assert_raise(TypeError) { Integer.__ensure(evil) }
  assert_raise(TypeError) { Integer.__ensure(Class.new { def to_int; 2; end }.new) }

  assert_equal 2, Integer.__ensure(2)
  assert_equal 1, Integer.__ensure(1.9) if Object.const_defined?(:Float)
  assert_raise(TypeError) { Integer.__ensure("2") }
  assert_raise(TypeError) { Integer.__ensure(nil) }
end

assert('Integer arithmetic redefined on Integer itself reaches the redefinition') do
  # `OP_ADD`, `OP_SUB`, `OP_MUL` and `OP_DIV` answer `a + b` from C for an
  # Integer receiver, `OP_ADDI` and `OP_SUBI` answer `a + 1` and `OP_ADDILV`
  # and `OP_SUBILV` answer `a += 1` on a local the same way. They may only do
  # so while `Integer#+` and its kin are still the builtins they reimplement,
  # which `mrb->bop_redefined` records the moment one is replaced, so a
  # redefinition installed on `Integer` itself is honored as in CRuby. The
  # results are read before the operators are put back because mrblib itself
  # counts with them.
  Integer.class_eval do
    alias_method :__add_before_test, :+
    alias_method :__sub_before_test, :-
    alias_method :__mul_before_test, :*
    alias_method :__div_before_test, :/
    def +(other) [:add, self, other] end
    def -(other) [:sub, self, other] end
    def *(other) [:mul, self, other] end
    def /(other) [:div, self, other] end
  end
  begin
    a = 7
    b = 2
    sum = a + b
    diff = a - b
    prod = a * b
    quot = a / b
    imm_sum = a + 1
    imm_diff = a - 1
    lv = a
    lv += 1
    lv_sum = lv
    lv = a
    lv -= 1
    lv_diff = lv
    mixed = a + 2.5 if Object.const_defined?(:Float)
  ensure
    Integer.class_eval do
      alias_method :+, :__add_before_test
      alias_method :-, :__sub_before_test
      alias_method :*, :__mul_before_test
      alias_method :/, :__div_before_test
      if respond_to?(:remove_method, true)
        remove_method :__add_before_test, :__sub_before_test,
                      :__mul_before_test, :__div_before_test
      end
    end
  end
  assert_equal [:add, 7, 2], sum
  assert_equal [:sub, 7, 2], diff
  assert_equal [:mul, 7, 2], prod
  assert_equal [:div, 7, 2], quot
  assert_equal [:add, 7, 1], imm_sum
  assert_equal [:sub, 7, 1], imm_diff
  assert_equal [:add, 7, 1], lv_sum
  assert_equal [:sub, 7, 1], lv_diff
  assert_equal [:add, 7, 2.5], mixed if Object.const_defined?(:Float)
  a = 7
  b = 2
  assert_equal 9, a + b
  assert_equal 8, a + 1
  a += 1
  assert_equal 8, a
end

assert('Integer comparison redefined on Integer itself reaches the redefinition') do
  # `OP_EQ`, `OP_LT`, `OP_LE`, `OP_GT` and `OP_GE` answer `a < b` from C for
  # an Integer receiver on the same terms as the arithmetic opcodes above.
  # `a == a` is answered without any comparison whenever the two hold the
  # same value, which a redefined `Integer#==` must still be asked about.
  Integer.class_eval do
    alias_method :__eq_before_test, :==
    alias_method :__lt_before_test, :<
    alias_method :__le_before_test, :<=
    alias_method :__gt_before_test, :>
    alias_method :__ge_before_test, :>=
    def ==(other) [:eq, self, other] end
    def <(other) [:lt, self, other] end
    def <=(other) [:le, self, other] end
    def >(other) [:gt, self, other] end
    def >=(other) [:ge, self, other] end
  end
  begin
    a = 7
    b = 2
    eq = a == b
    same = a == a
    lt = a < b
    le = a <= b
    gt = a > b
    ge = a >= b
    mixed = a < 2.5 if Object.const_defined?(:Float)
    # `Array#index` compares through `mrb_equal()`, which answers an Integer
    # pair from the values only while the builtin stands
    index = [a].index(b)
  ensure
    Integer.class_eval do
      alias_method :==, :__eq_before_test
      alias_method :<, :__lt_before_test
      alias_method :<=, :__le_before_test
      alias_method :>, :__gt_before_test
      alias_method :>=, :__ge_before_test
      if respond_to?(:remove_method, true)
        remove_method :__eq_before_test, :__lt_before_test, :__le_before_test,
                      :__gt_before_test, :__ge_before_test
      end
    end
  end
  assert_equal [:eq, 7, 2], eq
  assert_equal [:eq, 7, 7], same
  assert_equal [:lt, 7, 2], lt
  assert_equal [:le, 7, 2], le
  assert_equal [:gt, 7, 2], gt
  assert_equal [:ge, 7, 2], ge
  assert_equal 0, index
  assert_equal [:lt, 7, 2.5], mixed if Object.const_defined?(:Float)
  a = 7
  b = 2
  assert_false a == b
  assert_true a == a
  assert_false a < b
  assert_true a >= b
end

assert('Integer operators on two literals reach a redefinition') do
  # The compiler used to compute `1 + 2`, `1 << 2` and their kin on two
  # Integer literals while compiling, as well as `~1` and `i = 7; i += 1`, so
  # no opcode and no send were left for a redefined operator to reach; CRuby
  # computes them when they run. A literal keeps absorbing its sign, `-1`
  # being a literal in CRuby as well.
  Integer.class_eval do
    alias_method :__add_before_test, :+
    alias_method :__sub_before_test, :-
    alias_method :__mul_before_test, :*
    alias_method :__div_before_test, :/
    alias_method :__lshift_before_test, :<<
    alias_method :__rshift_before_test, :>>
    alias_method :__mod_before_test, :%
    alias_method :__and_before_test, :&
    alias_method :__or_before_test, :|
    alias_method :__xor_before_test, :^
    alias_method :__inv_before_test, :~
    def +(other) [:add, self, other] end
    def -(other) [:sub, self, other] end
    def *(other) [:mul, self, other] end
    def /(other) [:div, self, other] end
    def <<(other) [:lshift, self, other] end
    def >>(other) [:rshift, self, other] end
    def %(other) [:mod, self, other] end
    def &(other) [:and, self, other] end
    def |(other) [:or, self, other] end
    def ^(other) [:xor, self, other] end
    def ~() [:inv, self] end
  end
  begin
    got = [1 + 2, 5 - 3, 5 * 3, 6 / 3, 1 << 2, 8 >> 1, 7 % 3, 6 & 3, 6 | 3, 6 ^ 3, ~1]
    i = 7
    i += 1
    incremented = i
    negative = -1
  ensure
    Integer.class_eval do
      alias_method :+, :__add_before_test
      alias_method :-, :__sub_before_test
      alias_method :*, :__mul_before_test
      alias_method :/, :__div_before_test
      alias_method :<<, :__lshift_before_test
      alias_method :>>, :__rshift_before_test
      alias_method :%, :__mod_before_test
      alias_method :&, :__and_before_test
      alias_method :|, :__or_before_test
      alias_method :^, :__xor_before_test
      alias_method :~, :__inv_before_test
      if respond_to?(:remove_method, true)
        remove_method :__add_before_test, :__sub_before_test, :__mul_before_test,
                      :__div_before_test, :__lshift_before_test, :__rshift_before_test,
                      :__mod_before_test, :__and_before_test, :__or_before_test,
                      :__xor_before_test, :__inv_before_test
      end
    end
  end
  assert_equal [[:add, 1, 2], [:sub, 5, 3], [:mul, 5, 3], [:div, 6, 3],
                [:lshift, 1, 2], [:rshift, 8, 1], [:mod, 7, 3],
                [:and, 6, 3], [:or, 6, 3], [:xor, 6, 3], [:inv, 1]], got
  assert_equal [:add, 7, 1], incremented
  assert_equal(-1, negative)
  assert_equal 3, 1 + 2
  assert_equal 4, 1 << 2
  assert_equal(-2, ~1)
  i = 7
  i += 1
  assert_equal 8, i
end