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
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
use crate::vm::*;
use fraction::{BigFraction, BigUint};
use num_bigint::BigInt;
use std::sync::Arc;

pub trait Runtime
where
    Self: Sized,
{
    fn print_panic(message: String, call_stack: CallStack);

    #[inline]
    fn call(vm: &mut VM, method: NativeMethod) -> VMResult<()> {
        match method {
            NativeMethod::Number_plus => Self::number_plus(vm),
            NativeMethod::Number_minus => Self::number_minus(vm),
            NativeMethod::Object_eq => Self::object_eq(vm),
            NativeMethod::Object_asString => Self::object_asString(vm),
        }
    }

    fn object_eq(vm: &mut VM) -> VMResult<()> {
        let receiver = unwrap!(vm, vm.pop_eval::<Self>());
        let operand = unwrap!(vm, vm.pop_eval::<Self>());

        vm.push(Object::box_bool(receiver == operand));

        VMResult::Ok(())
    }

    #[allow(non_snake_case)]
    fn object_asString(vm: &mut VM) -> VMResult<()> {
        let receiver = unwrap!(vm, vm.pop_eval::<Self>());

        vm.push(Object::box_string(format!("{}", receiver)));

        VMResult::Ok(())
    }

    fn number_plus(vm: &mut VM) -> VMResult<()> {
        let receiver = unwrap!(vm, vm.pop_eval::<Self>());
        let operand = unwrap!(vm, vm.pop_eval::<Self>());

        match (&receiver.const_value, &operand.const_value) {
            (ConstValue::Lazy(_, _, _), _) | (_, ConstValue::Lazy(_, _, _)) => {
                return vm.panic("adding lazy".into())
            }

            (ConstValue::InstanceVariables(_), _)
            | (_, ConstValue::InstanceVariables(_))
            | (ConstValue::Nothing, _)
            | (_, ConstValue::Nothing)
            | (ConstValue::String(_), _)
            | (_, ConstValue::String(_))
            | (ConstValue::Character(_), _)
            | (_, ConstValue::Character(_))
            | (ConstValue::Symbol(_), _)
            | (_, ConstValue::Symbol(_)) => return vm.panic("not a number".into()),

            (ConstValue::U8(a), ConstValue::U8(b)) => vm.push(add_u8(*a, *b)),
            (ConstValue::U8(a), ConstValue::U16(b)) => vm.push(add_u16(*a as u16, *b)),
            (ConstValue::U8(a), ConstValue::U32(b)) => vm.push(add_u32(*a as u32, *b)),
            (ConstValue::U8(a), ConstValue::U64(b)) => vm.push(add_u64(*a as u64, *b)),
            (ConstValue::U8(a), ConstValue::U128(b)) => vm.push(add_u128(*a as u128, *b)),
            (ConstValue::U8(a), ConstValue::UBig(b)) => vm.push(add_ubig(&BigUint::from(*a), b)),

            (ConstValue::U16(a), ConstValue::U8(b)) => vm.push(add_u16(*a, *b as u16)),
            (ConstValue::U16(a), ConstValue::U16(b)) => vm.push(add_u16(*a, *b)),
            (ConstValue::U16(a), ConstValue::U32(b)) => vm.push(add_u32(*a as u32, *b)),
            (ConstValue::U16(a), ConstValue::U64(b)) => vm.push(add_u64(*a as u64, *b)),
            (ConstValue::U16(a), ConstValue::U128(b)) => vm.push(add_u128(*a as u128, *b)),
            (ConstValue::U16(a), ConstValue::UBig(b)) => vm.push(add_ubig(&BigUint::from(*a), b)),

            (ConstValue::U32(a), ConstValue::U8(b)) => vm.push(add_u32(*a, *b as u32)),
            (ConstValue::U32(a), ConstValue::U16(b)) => vm.push(add_u32(*a, *b as u32)),
            (ConstValue::U32(a), ConstValue::U32(b)) => vm.push(add_u32(*a, *b)),
            (ConstValue::U32(a), ConstValue::U64(b)) => vm.push(add_u64(*a as u64, *b)),
            (ConstValue::U32(a), ConstValue::U128(b)) => vm.push(add_u128(*a as u128, *b)),
            (ConstValue::U32(a), ConstValue::UBig(b)) => vm.push(add_ubig(&BigUint::from(*a), b)),

            (ConstValue::U64(a), ConstValue::U8(b)) => vm.push(add_u64(*a, *b as u64)),
            (ConstValue::U64(a), ConstValue::U16(b)) => vm.push(add_u64(*a, *b as u64)),
            (ConstValue::U64(a), ConstValue::U32(b)) => vm.push(add_u64(*a, *b as u64)),
            (ConstValue::U64(a), ConstValue::U64(b)) => vm.push(add_u64(*a, *b)),
            (ConstValue::U64(a), ConstValue::U128(b)) => vm.push(add_u128(*a as u128, *b)),
            (ConstValue::U64(a), ConstValue::UBig(b)) => vm.push(add_ubig(&BigUint::from(*a), b)),

            (ConstValue::U128(a), ConstValue::U8(b)) => vm.push(add_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U16(b)) => vm.push(add_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U32(b)) => vm.push(add_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U64(b)) => vm.push(add_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U128(b)) => vm.push(add_u128(*a, *b)),
            (ConstValue::U128(a), ConstValue::UBig(b)) => vm.push(add_ubig(&BigUint::from(*a), b)),

            (ConstValue::UBig(a), ConstValue::U8(b)) => vm.push(add_ubig(a, &BigUint::from(*b))),
            (ConstValue::UBig(a), ConstValue::U16(b)) => vm.push(add_ubig(a, &BigUint::from(*b))),
            (ConstValue::UBig(a), ConstValue::U32(b)) => vm.push(add_ubig(a, &BigUint::from(*b))),
            (ConstValue::UBig(a), ConstValue::U64(b)) => vm.push(add_ubig(a, &BigUint::from(*b))),
            (ConstValue::UBig(a), ConstValue::U128(b)) => vm.push(add_ubig(a, &BigUint::from(*b))),
            (ConstValue::UBig(a), ConstValue::UBig(b)) => vm.push(add_ubig(a, b)),

            (ConstValue::I8(a), ConstValue::I8(b)) => vm.push(add_i8(*a, *b)),
            (ConstValue::I8(a), ConstValue::I16(b)) => vm.push(add_i16(*a as i16, *b)),
            (ConstValue::I8(a), ConstValue::I32(b)) => vm.push(add_i32(*a as i32, *b)),
            (ConstValue::I8(a), ConstValue::I64(b)) => vm.push(add_i64(*a as i64, *b)),
            (ConstValue::I8(a), ConstValue::I128(b)) => vm.push(add_i128(*a as i128, *b)),
            (ConstValue::I8(a), ConstValue::IBig(b)) => vm.push(add_ibig(&BigInt::from(*a), b)),

            (ConstValue::I16(a), ConstValue::I8(b)) => vm.push(add_i16(*a, *b as i16)),
            (ConstValue::I16(a), ConstValue::I16(b)) => vm.push(add_i16(*a, *b)),
            (ConstValue::I16(a), ConstValue::I32(b)) => vm.push(add_i32(*a as i32, *b)),
            (ConstValue::I16(a), ConstValue::I64(b)) => vm.push(add_i64(*a as i64, *b)),
            (ConstValue::I16(a), ConstValue::I128(b)) => vm.push(add_i128(*a as i128, *b)),
            (ConstValue::I16(a), ConstValue::IBig(b)) => vm.push(add_ibig(&BigInt::from(*a), b)),

            (ConstValue::I32(a), ConstValue::I8(b)) => vm.push(add_i32(*a, *b as i32)),
            (ConstValue::I32(a), ConstValue::I16(b)) => vm.push(add_i32(*a, *b as i32)),
            (ConstValue::I32(a), ConstValue::I32(b)) => vm.push(add_i32(*a, *b)),
            (ConstValue::I32(a), ConstValue::I64(b)) => vm.push(add_i64(*a as i64, *b)),
            (ConstValue::I32(a), ConstValue::I128(b)) => vm.push(add_i128(*a as i128, *b)),
            (ConstValue::I32(a), ConstValue::IBig(b)) => vm.push(add_ibig(&BigInt::from(*a), b)),

            (ConstValue::I64(a), ConstValue::I8(b)) => vm.push(add_i64(*a, *b as i64)),
            (ConstValue::I64(a), ConstValue::I16(b)) => vm.push(add_i64(*a, *b as i64)),
            (ConstValue::I64(a), ConstValue::I32(b)) => vm.push(add_i64(*a, *b as i64)),
            (ConstValue::I64(a), ConstValue::I64(b)) => vm.push(add_i64(*a, *b)),
            (ConstValue::I64(a), ConstValue::I128(b)) => vm.push(add_i128(*a as i128, *b)),
            (ConstValue::I64(a), ConstValue::IBig(b)) => vm.push(add_ibig(&BigInt::from(*a), b)),

            (ConstValue::I128(a), ConstValue::I8(b)) => vm.push(add_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I16(b)) => vm.push(add_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I32(b)) => vm.push(add_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I64(b)) => vm.push(add_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I128(b)) => vm.push(add_i128(*a, *b)),
            (ConstValue::I128(a), ConstValue::IBig(b)) => vm.push(add_ibig(&BigInt::from(*a), b)),

            (ConstValue::IBig(a), ConstValue::I8(b)) => vm.push(add_ibig(a, &BigInt::from(*b))),
            (ConstValue::IBig(a), ConstValue::I16(b)) => vm.push(add_ibig(a, &BigInt::from(*b))),
            (ConstValue::IBig(a), ConstValue::I32(b)) => vm.push(add_ibig(a, &BigInt::from(*b))),
            (ConstValue::IBig(a), ConstValue::I64(b)) => vm.push(add_ibig(a, &BigInt::from(*b))),
            (ConstValue::IBig(a), ConstValue::I128(b)) => vm.push(add_ibig(a, &BigInt::from(*b))),
            (ConstValue::IBig(a), ConstValue::IBig(b)) => vm.push(add_ibig(a, b)),

            (ConstValue::U8(b), ConstValue::I8(a)) => vm.push(add_i16(*a as i16, *b as i16)),
            (ConstValue::U16(b), ConstValue::I8(a)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::U32(b), ConstValue::I8(a)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I8(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U128(b), ConstValue::I8(a)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I8(a)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I16(a)) => vm.push(add_i16(*a as i16, *b as i16)),
            (ConstValue::U16(b), ConstValue::I16(a)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::U32(b), ConstValue::I16(a)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I16(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U128(b), ConstValue::I16(a)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I16(a)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I32(a)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::U16(b), ConstValue::I32(a)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::U32(b), ConstValue::I32(a)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I32(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U128(b), ConstValue::I32(a)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I32(a)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I64(a)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::U16(b), ConstValue::I64(a)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::U32(b), ConstValue::I64(a)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I64(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U128(b), ConstValue::I64(a)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I64(a)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I128(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U16(b), ConstValue::I128(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U32(b), ConstValue::I128(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U64(b), ConstValue::I128(a)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::U128(b), ConstValue::I128(a)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I128(a)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::IBig(a)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::U16(b), ConstValue::IBig(a)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::U32(b), ConstValue::IBig(a)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::U64(b), ConstValue::IBig(a)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::U128(b), ConstValue::IBig(a)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::UBig(b), ConstValue::IBig(a)) => vm.push(add_ibig(a, &(b.clone().into()))),

            (ConstValue::I8(a), ConstValue::U8(b)) => vm.push(add_i16(*a as i16, *b as i16)),
            (ConstValue::I8(a), ConstValue::U16(b)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::I8(a), ConstValue::U32(b)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::I8(a), ConstValue::U64(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I8(a), ConstValue::U128(b)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I8(a), ConstValue::UBig(b)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I16(a), ConstValue::U8(b)) => vm.push(add_i16(*a as i16, *b as i16)),
            (ConstValue::I16(a), ConstValue::U16(b)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::I16(a), ConstValue::U32(b)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::I16(a), ConstValue::U64(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I16(a), ConstValue::U128(b)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I16(a), ConstValue::UBig(b)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I32(a), ConstValue::U8(b)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::I32(a), ConstValue::U16(b)) => vm.push(add_i32(*a as i32, *b as i32)),
            (ConstValue::I32(a), ConstValue::U32(b)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::I32(a), ConstValue::U64(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I32(a), ConstValue::U128(b)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I32(a), ConstValue::UBig(b)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I64(a), ConstValue::U8(b)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::I64(a), ConstValue::U16(b)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::I64(a), ConstValue::U32(b)) => vm.push(add_i64(*a as i64, *b as i64)),
            (ConstValue::I64(a), ConstValue::U64(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I64(a), ConstValue::U128(b)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I64(a), ConstValue::UBig(b)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I128(a), ConstValue::U8(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I128(a), ConstValue::U16(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I128(a), ConstValue::U32(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I128(a), ConstValue::U64(b)) => vm.push(add_i128(*a as i128, *b as i128)),
            (ConstValue::I128(a), ConstValue::U128(b)) => {
                vm.push(add_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I128(a), ConstValue::UBig(b)) => {
                vm.push(add_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::IBig(a), ConstValue::U8(b)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U16(b)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U32(b)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U64(b)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U128(b)) => vm.push(add_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::UBig(b)) => vm.push(add_ibig(a, &(b.clone().into()))),

            (ConstValue::F32(a), ConstValue::U8(b)) | (ConstValue::U8(b), ConstValue::F32(a)) => {
                vm.push(add_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::I8(b)) | (ConstValue::I8(b), ConstValue::F32(a)) => {
                vm.push(add_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::U16(b)) | (ConstValue::U16(b), ConstValue::F32(a)) => {
                vm.push(add_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::I16(b)) | (ConstValue::I16(b), ConstValue::F32(a)) => {
                vm.push(add_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::U32(b)) | (ConstValue::U32(b), ConstValue::F32(a)) => {
                vm.push(add_f64(*a as f64, *b as f64))
            }
            (ConstValue::F32(a), ConstValue::I32(b)) | (ConstValue::I32(b), ConstValue::F32(a)) => {
                vm.push(add_f64(*a as f64, *b as f64))
            }
            (ConstValue::F32(a), ConstValue::U64(b)) | (ConstValue::U64(b), ConstValue::F32(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::I64(b)) | (ConstValue::I64(b), ConstValue::F32(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::U128(b))
            | (ConstValue::U128(b), ConstValue::F32(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::I128(b))
            | (ConstValue::I128(b), ConstValue::F32(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::UBig(b))
            | (ConstValue::UBig(b), ConstValue::F32(a)) => {
                vm.push(add_fbig(&(*a).into(), &(b.clone().into())))
            }
            (ConstValue::F32(a), ConstValue::IBig(b))
            | (ConstValue::IBig(b), ConstValue::F32(a)) => {
                vm.push(add_fbig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::F64(a), ConstValue::U8(b)) | (ConstValue::U8(b), ConstValue::F64(a)) => {
                vm.push(add_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::I8(b)) | (ConstValue::I8(b), ConstValue::F64(a)) => {
                vm.push(add_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::U16(b)) | (ConstValue::U16(b), ConstValue::F64(a)) => {
                vm.push(add_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::I16(b)) | (ConstValue::I16(b), ConstValue::F64(a)) => {
                vm.push(add_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::U32(b)) | (ConstValue::U32(b), ConstValue::F64(a)) => {
                vm.push(add_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::I32(b)) | (ConstValue::I32(b), ConstValue::F64(a)) => {
                vm.push(add_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::U64(b)) | (ConstValue::U64(b), ConstValue::F64(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::I64(b)) | (ConstValue::I64(b), ConstValue::F64(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::U128(b))
            | (ConstValue::U128(b), ConstValue::F64(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::I128(b))
            | (ConstValue::I128(b), ConstValue::F64(a)) => {
                vm.push(add_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::UBig(b))
            | (ConstValue::UBig(b), ConstValue::F64(a)) => {
                vm.push(add_fbig(&(*a).into(), &(b.clone().into())))
            }
            (ConstValue::F64(a), ConstValue::IBig(b))
            | (ConstValue::IBig(b), ConstValue::F64(a)) => {
                vm.push(add_fbig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::FBig(a), ConstValue::U8(b)) | (ConstValue::U8(b), ConstValue::FBig(a)) => {
                vm.push(add_fbig(a, &(*b).into()))
            }
            (ConstValue::FBig(a), ConstValue::I8(b)) | (ConstValue::I8(b), ConstValue::FBig(a)) => {
                vm.push(add_fbig(a, &(*b).into()))
            }
            (ConstValue::FBig(a), ConstValue::U16(b))
            | (ConstValue::U16(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I16(b))
            | (ConstValue::I16(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::U32(b))
            | (ConstValue::U32(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I32(b))
            | (ConstValue::I32(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::U64(b))
            | (ConstValue::U64(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I64(b))
            | (ConstValue::I64(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::U128(b))
            | (ConstValue::U128(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I128(b))
            | (ConstValue::I128(b), ConstValue::FBig(a)) => vm.push(add_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::UBig(b))
            | (ConstValue::UBig(b), ConstValue::FBig(a)) => {
                vm.push(add_fbig(a, &(b.clone().into())))
            }
            (ConstValue::FBig(a), ConstValue::IBig(b))
            | (ConstValue::IBig(b), ConstValue::FBig(a)) => {
                vm.push(add_fbig(a, &(b.clone().into())))
            }

            (ConstValue::F32(a), ConstValue::F32(b)) => vm.push(add_f32(*a, *b)),
            (ConstValue::F32(a), ConstValue::F64(b)) => vm.push(add_f64(*a as f64, *b)),
            (ConstValue::F64(a), ConstValue::F32(b)) => vm.push(add_f64(*a, *b as f64)),
            (ConstValue::F64(a), ConstValue::F64(b)) => vm.push(add_f64(*a, *b)),
            (ConstValue::F32(a), ConstValue::FBig(b)) => vm.push(add_fbig(&(*a).into(), b)),
            (ConstValue::F64(a), ConstValue::FBig(b)) => vm.push(add_fbig(&(*a).into(), b)),
            (ConstValue::FBig(b), ConstValue::F32(a)) => vm.push(add_fbig(&(*a).into(), b)),
            (ConstValue::FBig(b), ConstValue::F64(a)) => vm.push(add_fbig(&(*a).into(), b)),
            (ConstValue::FBig(a), ConstValue::FBig(b)) => vm.push(add_fbig(a, b)),
        }
        VMResult::Ok(())
    }

    fn number_minus(vm: &mut VM) -> VMResult<()> {
        let receiver = unwrap!(vm, vm.pop_eval::<Self>());
        let operand = unwrap!(vm, vm.pop_eval::<Self>());

        match (&receiver.const_value, &operand.const_value) {
            (ConstValue::Lazy(_, _, _), _) | (_, ConstValue::Lazy(_, _, _)) => {
                return vm.panic("subtracting lazy".into())
            }

            (ConstValue::InstanceVariables(_), _)
            | (_, ConstValue::InstanceVariables(_))
            | (ConstValue::Nothing, _)
            | (_, ConstValue::Nothing)
            | (ConstValue::String(_), _)
            | (_, ConstValue::String(_))
            | (ConstValue::Character(_), _)
            | (_, ConstValue::Character(_))
            | (ConstValue::Symbol(_), _)
            | (_, ConstValue::Symbol(_)) => return vm.panic("not a number".into()),

            (ConstValue::U8(a), ConstValue::U8(b)) => vm.push(subtract_u8(*a, *b)),
            (ConstValue::U8(a), ConstValue::U16(b)) => vm.push(subtract_u16(*a as u16, *b)),
            (ConstValue::U8(a), ConstValue::U32(b)) => vm.push(subtract_u32(*a as u32, *b)),
            (ConstValue::U8(a), ConstValue::U64(b)) => vm.push(subtract_u64(*a as u64, *b)),
            (ConstValue::U8(a), ConstValue::U128(b)) => vm.push(subtract_u128(*a as u128, *b)),
            (ConstValue::U8(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ubig(&BigUint::from(*a), b))
            }

            (ConstValue::U16(a), ConstValue::U8(b)) => vm.push(subtract_u16(*a, *b as u16)),
            (ConstValue::U16(a), ConstValue::U16(b)) => vm.push(subtract_u16(*a, *b)),
            (ConstValue::U16(a), ConstValue::U32(b)) => vm.push(subtract_u32(*a as u32, *b)),
            (ConstValue::U16(a), ConstValue::U64(b)) => vm.push(subtract_u64(*a as u64, *b)),
            (ConstValue::U16(a), ConstValue::U128(b)) => vm.push(subtract_u128(*a as u128, *b)),
            (ConstValue::U16(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ubig(&BigUint::from(*a), b))
            }

            (ConstValue::U32(a), ConstValue::U8(b)) => vm.push(subtract_u32(*a, *b as u32)),
            (ConstValue::U32(a), ConstValue::U16(b)) => vm.push(subtract_u32(*a, *b as u32)),
            (ConstValue::U32(a), ConstValue::U32(b)) => vm.push(subtract_u32(*a, *b)),
            (ConstValue::U32(a), ConstValue::U64(b)) => vm.push(subtract_u64(*a as u64, *b)),
            (ConstValue::U32(a), ConstValue::U128(b)) => vm.push(subtract_u128(*a as u128, *b)),
            (ConstValue::U32(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ubig(&BigUint::from(*a), b))
            }

            (ConstValue::U64(a), ConstValue::U8(b)) => vm.push(subtract_u64(*a, *b as u64)),
            (ConstValue::U64(a), ConstValue::U16(b)) => vm.push(subtract_u64(*a, *b as u64)),
            (ConstValue::U64(a), ConstValue::U32(b)) => vm.push(subtract_u64(*a, *b as u64)),
            (ConstValue::U64(a), ConstValue::U64(b)) => vm.push(subtract_u64(*a, *b)),
            (ConstValue::U64(a), ConstValue::U128(b)) => vm.push(subtract_u128(*a as u128, *b)),
            (ConstValue::U64(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ubig(&BigUint::from(*a), b))
            }

            (ConstValue::U128(a), ConstValue::U8(b)) => vm.push(subtract_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U16(b)) => vm.push(subtract_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U32(b)) => vm.push(subtract_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U64(b)) => vm.push(subtract_u128(*a, *b as u128)),
            (ConstValue::U128(a), ConstValue::U128(b)) => vm.push(subtract_u128(*a, *b)),
            (ConstValue::U128(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ubig(&BigUint::from(*a), b))
            }

            (ConstValue::UBig(a), ConstValue::U8(b)) => {
                vm.push(subtract_ubig(a, &BigUint::from(*b)))
            }
            (ConstValue::UBig(a), ConstValue::U16(b)) => {
                vm.push(subtract_ubig(a, &BigUint::from(*b)))
            }
            (ConstValue::UBig(a), ConstValue::U32(b)) => {
                vm.push(subtract_ubig(a, &BigUint::from(*b)))
            }
            (ConstValue::UBig(a), ConstValue::U64(b)) => {
                vm.push(subtract_ubig(a, &BigUint::from(*b)))
            }
            (ConstValue::UBig(a), ConstValue::U128(b)) => {
                vm.push(subtract_ubig(a, &BigUint::from(*b)))
            }
            (ConstValue::UBig(a), ConstValue::UBig(b)) => vm.push(subtract_ubig(a, b)),

            (ConstValue::I8(a), ConstValue::I8(b)) => vm.push(subtract_i8(*a, *b)),
            (ConstValue::I8(a), ConstValue::I16(b)) => vm.push(subtract_i16(*a as i16, *b)),
            (ConstValue::I8(a), ConstValue::I32(b)) => vm.push(subtract_i32(*a as i32, *b)),
            (ConstValue::I8(a), ConstValue::I64(b)) => vm.push(subtract_i64(*a as i64, *b)),
            (ConstValue::I8(a), ConstValue::I128(b)) => vm.push(subtract_i128(*a as i128, *b)),
            (ConstValue::I8(a), ConstValue::IBig(b)) => {
                vm.push(subtract_ibig(&BigInt::from(*a), b))
            }

            (ConstValue::I16(a), ConstValue::I8(b)) => vm.push(subtract_i16(*a, *b as i16)),
            (ConstValue::I16(a), ConstValue::I16(b)) => vm.push(subtract_i16(*a, *b)),
            (ConstValue::I16(a), ConstValue::I32(b)) => vm.push(subtract_i32(*a as i32, *b)),
            (ConstValue::I16(a), ConstValue::I64(b)) => vm.push(subtract_i64(*a as i64, *b)),
            (ConstValue::I16(a), ConstValue::I128(b)) => vm.push(subtract_i128(*a as i128, *b)),
            (ConstValue::I16(a), ConstValue::IBig(b)) => {
                vm.push(subtract_ibig(&BigInt::from(*a), b))
            }

            (ConstValue::I32(a), ConstValue::I8(b)) => vm.push(subtract_i32(*a, *b as i32)),
            (ConstValue::I32(a), ConstValue::I16(b)) => vm.push(subtract_i32(*a, *b as i32)),
            (ConstValue::I32(a), ConstValue::I32(b)) => vm.push(subtract_i32(*a, *b)),
            (ConstValue::I32(a), ConstValue::I64(b)) => vm.push(subtract_i64(*a as i64, *b)),
            (ConstValue::I32(a), ConstValue::I128(b)) => vm.push(subtract_i128(*a as i128, *b)),
            (ConstValue::I32(a), ConstValue::IBig(b)) => {
                vm.push(subtract_ibig(&BigInt::from(*a), b))
            }

            (ConstValue::I64(a), ConstValue::I8(b)) => vm.push(subtract_i64(*a, *b as i64)),
            (ConstValue::I64(a), ConstValue::I16(b)) => vm.push(subtract_i64(*a, *b as i64)),
            (ConstValue::I64(a), ConstValue::I32(b)) => vm.push(subtract_i64(*a, *b as i64)),
            (ConstValue::I64(a), ConstValue::I64(b)) => vm.push(subtract_i64(*a, *b)),
            (ConstValue::I64(a), ConstValue::I128(b)) => vm.push(subtract_i128(*a as i128, *b)),
            (ConstValue::I64(a), ConstValue::IBig(b)) => {
                vm.push(subtract_ibig(&BigInt::from(*a), b))
            }

            (ConstValue::I128(a), ConstValue::I8(b)) => vm.push(subtract_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I16(b)) => vm.push(subtract_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I32(b)) => vm.push(subtract_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I64(b)) => vm.push(subtract_i128(*a, *b as i128)),
            (ConstValue::I128(a), ConstValue::I128(b)) => vm.push(subtract_i128(*a, *b)),
            (ConstValue::I128(a), ConstValue::IBig(b)) => {
                vm.push(subtract_ibig(&BigInt::from(*a), b))
            }

            (ConstValue::IBig(a), ConstValue::I8(b)) => {
                vm.push(subtract_ibig(a, &BigInt::from(*b)))
            }
            (ConstValue::IBig(a), ConstValue::I16(b)) => {
                vm.push(subtract_ibig(a, &BigInt::from(*b)))
            }
            (ConstValue::IBig(a), ConstValue::I32(b)) => {
                vm.push(subtract_ibig(a, &BigInt::from(*b)))
            }
            (ConstValue::IBig(a), ConstValue::I64(b)) => {
                vm.push(subtract_ibig(a, &BigInt::from(*b)))
            }
            (ConstValue::IBig(a), ConstValue::I128(b)) => {
                vm.push(subtract_ibig(a, &BigInt::from(*b)))
            }
            (ConstValue::IBig(a), ConstValue::IBig(b)) => vm.push(subtract_ibig(a, b)),

            (ConstValue::U8(b), ConstValue::I8(a)) => vm.push(subtract_i16(*a as i16, *b as i16)),
            (ConstValue::U16(b), ConstValue::I8(a)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::U32(b), ConstValue::I8(a)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I8(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U128(b), ConstValue::I8(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I8(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I16(a)) => vm.push(subtract_i16(*a as i16, *b as i16)),
            (ConstValue::U16(b), ConstValue::I16(a)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::U32(b), ConstValue::I16(a)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I16(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U128(b), ConstValue::I16(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I16(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I32(a)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::U16(b), ConstValue::I32(a)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::U32(b), ConstValue::I32(a)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I32(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U128(b), ConstValue::I32(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I32(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I64(a)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::U16(b), ConstValue::I64(a)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::U32(b), ConstValue::I64(a)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::U64(b), ConstValue::I64(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U128(b), ConstValue::I64(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I64(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::I128(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U16(b), ConstValue::I128(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U32(b), ConstValue::I128(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U64(b), ConstValue::I128(a)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::U128(b), ConstValue::I128(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::UBig(b), ConstValue::I128(a)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::U8(b), ConstValue::IBig(a)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::U16(b), ConstValue::IBig(a)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::U32(b), ConstValue::IBig(a)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::U64(b), ConstValue::IBig(a)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::U128(b), ConstValue::IBig(a)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::UBig(b), ConstValue::IBig(a)) => {
                vm.push(subtract_ibig(a, &(b.clone().into())))
            }

            (ConstValue::I8(a), ConstValue::U8(b)) => vm.push(subtract_i16(*a as i16, *b as i16)),
            (ConstValue::I8(a), ConstValue::U16(b)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::I8(a), ConstValue::U32(b)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::I8(a), ConstValue::U64(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I8(a), ConstValue::U128(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I8(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I16(a), ConstValue::U8(b)) => vm.push(subtract_i16(*a as i16, *b as i16)),
            (ConstValue::I16(a), ConstValue::U16(b)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::I16(a), ConstValue::U32(b)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::I16(a), ConstValue::U64(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I16(a), ConstValue::U128(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I16(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I32(a), ConstValue::U8(b)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::I32(a), ConstValue::U16(b)) => vm.push(subtract_i32(*a as i32, *b as i32)),
            (ConstValue::I32(a), ConstValue::U32(b)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::I32(a), ConstValue::U64(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I32(a), ConstValue::U128(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I32(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I64(a), ConstValue::U8(b)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::I64(a), ConstValue::U16(b)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::I64(a), ConstValue::U32(b)) => vm.push(subtract_i64(*a as i64, *b as i64)),
            (ConstValue::I64(a), ConstValue::U64(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I64(a), ConstValue::U128(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I64(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::I128(a), ConstValue::U8(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I128(a), ConstValue::U16(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I128(a), ConstValue::U32(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I128(a), ConstValue::U64(b)) => {
                vm.push(subtract_i128(*a as i128, *b as i128))
            }
            (ConstValue::I128(a), ConstValue::U128(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::I128(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ibig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::IBig(a), ConstValue::U8(b)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U16(b)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U32(b)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U64(b)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::U128(b)) => vm.push(subtract_ibig(a, &(*b).into())),
            (ConstValue::IBig(a), ConstValue::UBig(b)) => {
                vm.push(subtract_ibig(a, &(b.clone().into())))
            }

            (ConstValue::F32(a), ConstValue::U8(b)) | (ConstValue::U8(b), ConstValue::F32(a)) => {
                vm.push(subtract_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::I8(b)) | (ConstValue::I8(b), ConstValue::F32(a)) => {
                vm.push(subtract_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::U16(b)) | (ConstValue::U16(b), ConstValue::F32(a)) => {
                vm.push(subtract_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::I16(b)) | (ConstValue::I16(b), ConstValue::F32(a)) => {
                vm.push(subtract_f32(*a, *b as f32))
            }
            (ConstValue::F32(a), ConstValue::U32(b)) | (ConstValue::U32(b), ConstValue::F32(a)) => {
                vm.push(subtract_f64(*a as f64, *b as f64))
            }
            (ConstValue::F32(a), ConstValue::I32(b)) | (ConstValue::I32(b), ConstValue::F32(a)) => {
                vm.push(subtract_f64(*a as f64, *b as f64))
            }
            (ConstValue::F32(a), ConstValue::U64(b)) | (ConstValue::U64(b), ConstValue::F32(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::I64(b)) | (ConstValue::I64(b), ConstValue::F32(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::U128(b))
            | (ConstValue::U128(b), ConstValue::F32(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::I128(b))
            | (ConstValue::I128(b), ConstValue::F32(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F32(a), ConstValue::UBig(b))
            | (ConstValue::UBig(b), ConstValue::F32(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(b.clone().into())))
            }
            (ConstValue::F32(a), ConstValue::IBig(b))
            | (ConstValue::IBig(b), ConstValue::F32(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::F64(a), ConstValue::U8(b)) | (ConstValue::U8(b), ConstValue::F64(a)) => {
                vm.push(subtract_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::I8(b)) | (ConstValue::I8(b), ConstValue::F64(a)) => {
                vm.push(subtract_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::U16(b)) | (ConstValue::U16(b), ConstValue::F64(a)) => {
                vm.push(subtract_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::I16(b)) | (ConstValue::I16(b), ConstValue::F64(a)) => {
                vm.push(subtract_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::U32(b)) | (ConstValue::U32(b), ConstValue::F64(a)) => {
                vm.push(subtract_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::I32(b)) | (ConstValue::I32(b), ConstValue::F64(a)) => {
                vm.push(subtract_f64(*a, *b as f64))
            }
            (ConstValue::F64(a), ConstValue::U64(b)) | (ConstValue::U64(b), ConstValue::F64(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::I64(b)) | (ConstValue::I64(b), ConstValue::F64(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::U128(b))
            | (ConstValue::U128(b), ConstValue::F64(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::I128(b))
            | (ConstValue::I128(b), ConstValue::F64(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(*b).into()))
            }
            (ConstValue::F64(a), ConstValue::UBig(b))
            | (ConstValue::UBig(b), ConstValue::F64(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(b.clone().into())))
            }
            (ConstValue::F64(a), ConstValue::IBig(b))
            | (ConstValue::IBig(b), ConstValue::F64(a)) => {
                vm.push(subtract_fbig(&(*a).into(), &(b.clone().into())))
            }

            (ConstValue::FBig(a), ConstValue::U8(b)) | (ConstValue::U8(b), ConstValue::FBig(a)) => {
                vm.push(subtract_fbig(a, &(*b).into()))
            }
            (ConstValue::FBig(a), ConstValue::I8(b)) | (ConstValue::I8(b), ConstValue::FBig(a)) => {
                vm.push(subtract_fbig(a, &(*b).into()))
            }
            (ConstValue::FBig(a), ConstValue::U16(b))
            | (ConstValue::U16(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I16(b))
            | (ConstValue::I16(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::U32(b))
            | (ConstValue::U32(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I32(b))
            | (ConstValue::I32(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::U64(b))
            | (ConstValue::U64(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I64(b))
            | (ConstValue::I64(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::U128(b))
            | (ConstValue::U128(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::I128(b))
            | (ConstValue::I128(b), ConstValue::FBig(a)) => vm.push(subtract_fbig(a, &(*b).into())),
            (ConstValue::FBig(a), ConstValue::UBig(b))
            | (ConstValue::UBig(b), ConstValue::FBig(a)) => {
                vm.push(subtract_fbig(a, &(b.clone().into())))
            }
            (ConstValue::FBig(a), ConstValue::IBig(b))
            | (ConstValue::IBig(b), ConstValue::FBig(a)) => {
                vm.push(subtract_fbig(a, &(b.clone().into())))
            }

            (ConstValue::F32(a), ConstValue::F32(b)) => vm.push(subtract_f32(*a, *b)),
            (ConstValue::F32(a), ConstValue::F64(b)) => vm.push(subtract_f64(*a as f64, *b)),
            (ConstValue::F64(a), ConstValue::F32(b)) => vm.push(subtract_f64(*a, *b as f64)),
            (ConstValue::F64(a), ConstValue::F64(b)) => vm.push(subtract_f64(*a, *b)),
            (ConstValue::F32(a), ConstValue::FBig(b)) => vm.push(subtract_fbig(&(*a).into(), b)),
            (ConstValue::F64(a), ConstValue::FBig(b)) => vm.push(subtract_fbig(&(*a).into(), b)),
            (ConstValue::FBig(b), ConstValue::F32(a)) => vm.push(subtract_fbig(&(*a).into(), b)),
            (ConstValue::FBig(b), ConstValue::F64(a)) => vm.push(subtract_fbig(&(*a).into(), b)),
            (ConstValue::FBig(a), ConstValue::FBig(b)) => vm.push(subtract_fbig(a, b)),
        }
        VMResult::Ok(())
    }
}

fn add_u8(lhs: u8, rhs: u8) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_u8(res),
        None => add_u16(lhs as u16, rhs as u16),
    }
}

fn add_u16(lhs: u16, rhs: u16) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_u16(res),
        None => add_u32(lhs as u32, rhs as u32),
    }
}

fn add_u32(lhs: u32, rhs: u32) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_u32(res),
        None => add_u64(lhs as u64, rhs as u64),
    }
}

fn add_u64(lhs: u64, rhs: u64) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_u64(res),
        None => add_u128(lhs as u128, rhs as u128),
    }
}

fn add_u128(lhs: u128, rhs: u128) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_u128(res),
        None => add_ubig(&BigUint::from(lhs), &BigUint::from(rhs)),
    }
}

fn add_ubig(lhs: &BigUint, rhs: &BigUint) -> Arc<Object> {
    Object::box_ubig(lhs + rhs)
}

fn add_i8(lhs: i8, rhs: i8) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_i8(res),
        None => add_i16(lhs as i16, rhs as i16),
    }
}

fn add_i16(lhs: i16, rhs: i16) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_i16(res),
        None => add_i32(lhs as i32, rhs as i32),
    }
}

fn add_i32(lhs: i32, rhs: i32) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_i32(res),
        None => add_i64(lhs as i64, rhs as i64),
    }
}

fn add_i64(lhs: i64, rhs: i64) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_i64(res),
        None => add_i128(lhs as i128, rhs as i128),
    }
}

fn add_i128(lhs: i128, rhs: i128) -> Arc<Object> {
    match lhs.checked_add(rhs) {
        Some(res) => Object::box_i128(res),
        None => add_ibig(&lhs.into(), &rhs.into()),
    }
}

fn add_ibig(lhs: &BigInt, rhs: &BigInt) -> Arc<Object> {
    Object::box_ibig(lhs + rhs)
}

fn add_f32(lhs: f32, rhs: f32) -> Arc<Object> {
    Object::box_f32(lhs + rhs)
}

fn add_f64(lhs: f64, rhs: f64) -> Arc<Object> {
    Object::box_f64(lhs + rhs)
}

fn add_fbig(lhs: &BigFraction, rhs: &BigFraction) -> Arc<Object> {
    Object::box_fbig(lhs + rhs)
}

fn subtract_u8(lhs: u8, rhs: u8) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_u8(res),
        None => subtract_u16(lhs as u16, rhs as u16),
    }
}

fn subtract_u16(lhs: u16, rhs: u16) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_u16(res),
        None => subtract_u32(lhs as u32, rhs as u32),
    }
}

fn subtract_u32(lhs: u32, rhs: u32) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_u32(res),
        None => subtract_u64(lhs as u64, rhs as u64),
    }
}

fn subtract_u64(lhs: u64, rhs: u64) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_u64(res),
        None => subtract_u128(lhs as u128, rhs as u128),
    }
}

fn subtract_u128(lhs: u128, rhs: u128) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_u128(res),
        None => subtract_ubig(&BigUint::from(lhs), &BigUint::from(rhs)),
    }
}

fn subtract_ubig(lhs: &BigUint, rhs: &BigUint) -> Arc<Object> {
    Object::box_ubig(lhs + rhs)
}

fn subtract_i8(lhs: i8, rhs: i8) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_i8(res),
        None => subtract_i16(lhs as i16, rhs as i16),
    }
}

fn subtract_i16(lhs: i16, rhs: i16) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_i16(res),
        None => subtract_i32(lhs as i32, rhs as i32),
    }
}

fn subtract_i32(lhs: i32, rhs: i32) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_i32(res),
        None => subtract_i64(lhs as i64, rhs as i64),
    }
}

fn subtract_i64(lhs: i64, rhs: i64) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_i64(res),
        None => subtract_i128(lhs as i128, rhs as i128),
    }
}

fn subtract_i128(lhs: i128, rhs: i128) -> Arc<Object> {
    match lhs.checked_sub(rhs) {
        Some(res) => Object::box_i128(res),
        None => subtract_ibig(&lhs.into(), &rhs.into()),
    }
}

fn subtract_ibig(lhs: &BigInt, rhs: &BigInt) -> Arc<Object> {
    Object::box_ibig(lhs - rhs)
}

fn subtract_f32(lhs: f32, rhs: f32) -> Arc<Object> {
    Object::box_f32(lhs - rhs)
}

fn subtract_f64(lhs: f64, rhs: f64) -> Arc<Object> {
    Object::box_f64(lhs - rhs)
}

fn subtract_fbig(lhs: &BigFraction, rhs: &BigFraction) -> Arc<Object> {
    Object::box_fbig(lhs - rhs)
}

impl Runtime for () {
    fn print_panic(message: String, call_stack: CallStack) {
        panic!("{}\n{:#?}", message, call_stack)
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, PartialEq)]
pub enum NativeMethod {
    Number_plus,
    Number_minus,
    Object_eq,
    Object_asString,
}

impl<'a> From<&'a str> for NativeMethod {
    fn from(name: &'a str) -> Self {
        use NativeMethod::*;
        match name {
            "Loa/Number#+" => Number_plus,
            "Loa/Number#-" => Number_minus,
            "Loa/Object#==" => Object_eq,
            "Loa/Object#asString" => Object_asString,
            n => panic!("unknown native method: {}", n),
        }
    }
}

impl std::fmt::Display for NativeMethod {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use NativeMethod::*;
        match self {
            Number_plus => write!(f, "Loa/Number#+"),
            Number_minus => write!(f, "Loa/Number#-"),
            Object_eq => write!(f, "Loa/Object#=="),
            Object_asString => write!(f, "Loa/Object#asString"),
        }
    }
}