apple / swift-numerics

Advanced mathematical types and functions for Swift
Apache License 2.0
1.67k stars 142 forks source link

[BigInt] Using tests from “Violet - Python VM written in Swift” #242

Open LiarPrincess opened 1 year ago

LiarPrincess commented 1 year ago

This is a parent issue for merging BigInt tests from Violet - Python VM written in Swift. In total is is about 400 tests (about 70% of Violet test cases) split into a multiple smaller pull requests.

Please note that this may seem like a lot of tests, but I still can't guarantee that everything works even after passing all of them. A few minutes ago I was debugging crash in division, just to discover that this will also fail:

// -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows
let int: Int64 = -9223372036854775808
let big = BigInt(int)
XCTAssertEqual(-big, big * -1)

Also, I have NOT looked into your implementation. I just know that you use 2 complement, but nothing more. Once we fix all of the crashes/failures I may look into your code and add some more targeted tests.

Reference implementation

BigInt module inside Violet - Python VM written in Swift passes all of the tests (look at the swift-numerics branch).

Internal Violet implementation (see documentation for more details):

Under the hood it is a union (via tagged pointer) of Int32 (called Smi, after V8) and a heap allocation (magnitude + sign representation) with ARC for garbage collection.

Since Violet contains 2 different representations of BigInt (Smi for small numbers and BigIntHeap for the rest) in a lot of tests you will see separate test_int_xxx (for small integers) and test_big_xxx (for big integers). All of the test cases finish in ~10s (2014 rMBP, lowest spec, serial execution), so they are fast enough to keep int tests even if swift-numerics implementation does not have a Smi equivalent. This also tests if all of the BigInt operations have the same semantic as Int (div/shift rounding, reminder sign etc.).

Platforms

All of the tests results come from 2014 rMBP -> mac 11.7 (Big Sur), Xcode 13.2.1, Intel.

Not tested:

I can confirm that Violet implementation passes tests on:

So, the test cases should be resistant to mac/linux/docker differences.

Missing stuff

Other engines

In some pull requests/code I will be referencing other BigInt implementations:

Helpers directory

Shared code for all of the test cases is inside Helpers directory.

Since all of the pull requests contain the same Helpers code, it may be easier for us to first review and merge tests while ignoring Helpers. Then, after all is merged, do any changes in Helpers. Well… unless there is something really broken in Helpers.

License

Unless stated otherwise I am the author of this code, so feel free to slap any license you need.

Recommended merging order

In total this is around 10k hand written + 22k generated lines of code. I split them up, so that each PR contains 2-4 files (excluding Helpers, see above).

(✅ - pass, ❌ - fail, 💀 - crash, 🐰 - to discuss)

  1. Node

    • [BigInt tests] ❌ Nodejs tests - they can be either 1st or last. Fixing them will solve most of the problems in other tests. But also, fixing problems other tests will fix Node tests. There is a certain overlap.
  2. Inits and protocols

  3. Operations

  4. Other

  5. No merge

Final

I know that with so many pull requests it may be a bit hard to see the big picture, but eventually we should arrive at the state from the image below (please ignore the Apple and Performance directories, BigIntTests.swift are the tests currently present). I think that this is quite clean (1 file for every operation), and it will be quite easy to add/modify tests.

BigInt-XCode

LiarPrincess commented 1 year ago

[3.2.2023] EDIT to main issue:

LiarPrincess commented 1 year ago

Things to do in Helpers (but I'm not going to update 15 PRs, so we will do it later):

LiarPrincess commented 1 year ago

I wrote Violet XsProMax which is a Violet implementation with following changes:

Which gives us:

struct BigInt {
  struct Header {
    var count: UInt32
    var capacity: UInt32
  }

  var flags: UInt8
  var buffer: ManagedBufferPointer<Header, UInt>
}

I updated the performance PR with the results.

mgriebling commented 1 year ago

The issue below (from @LiarPrincess) appears fixed in my latest updates (see pull request #261)

// -9223372036854775808 = Int64.min, obviously '-Int64.min' overflows
let int: Int64 = -9223372036854775808
let big = BigInt(int)
XCTAssertEqual(-big, big * -1)
mgriebling commented 1 year ago

All remaining issues (at least the published ones) appear to have been fixed with the pull request #262. There were some problems with division/modulo signs, initialization, corner-case fixes.