Can anyone quick-check this arbitrary-precision code?

I'm trying out yet another bit-array collection type.

Obviously, I'm going to add an initializer that takes a Sequence of Bool. But I'm also going to make an initializer that takes a Sequence of FixedWidthInteger & UnsignedInteger for already-packed bits.

I'm starting with an initializer that takes exactly one such word. I've tested it with UInt8 and UInt64, but I should also test it with something that is definitely larger than a UInt64. (Uint may mirror either UInt32 or UInt64.)

So, I made my own UInt72. But, due to Standard Library deficiencies, making such a type is harder than it should be.

For division, I break up the operands with their words property, perform arbitrary-length integer division on those collections, then re-partition the bits back.

But this means I have to make an arbitrary-length integer library.

Note that my type uses neither the extended integer type nor arbitrary-length arithmetic. I only need those for a single test! I don't want to triple-stack my testing regimen to get to test what I really want (the BitArray collection). So, can anyone just take a quick look at the UInt72 and arbitrary-length arithmetic code to make sure it's (generally) all right. I'll probably go back and test them later.

I made a quick-and-dirty copy at a Gist. The integer type is at "UInt72.swift", and the arbitrary-length arithmetic code at "FixedWidthInteger+Extensions.swift", lines 63–333.

Maybe those arbitrary-length arithmetic methods could be added to the Standard Library.

/cc @xwu, @nnnnnnnn, @scanon

It's obviously too much code to check simply by visual inspection, but my general feedback would be that it seems much too much for test code and you don't seem to need it.

It's unclear that you'd gain anything in a bit array type from having an initializer that takes a Sequence of bits packed in any other way than in machine words. Nor would it be necessary to have an initializer that takes one arbitrary-width value of packed bits: any such value would have the property words, and from there you've got a Sequence of bits packed in machine words again.

If you want a wide type to test, unfortunately DoubleWidth isn't guaranteed to ship (or, at least, as a public type) or I'd simply recommend that to you. You can make a much more trivial Int65 type, though, with a sign bit and a UInt64 magnitude. Then, make sure words returns the expected sequence (on a 64-bit system, a sequence of two Int values, the last one being the sign-extended sign bit; on a 32-bit system, a sequence of four Int values, the last two being the sign-extended sign bit). For required methods that you'll never use or test, you can simply bail with fatalError(), which is superior to having an untested but seemingly complete implementation.

It's unclear that you'd gain anything in a bit array type from having an initializer that takes a Sequence of bits packed in any other way than in machine words. Nor would it be necessary to have an initializer that takes one arbitrary-width value of packed bits: any such value would have the property words, and from there you've got a Sequence of bits packed in machine words again.

The single UnsignedInteger value initializer is there for me to prepare for the Sequence of such. (Don't want to handle too much at once.) I'm making initializer(s) to do it so the user doesn't have to; the user would have to supply both myValue.words but the amount of bits too (so the leftover bits in the last word aren't read). Skipping those leftover bits is important in the Sequence of UnsignedInteger initializer since they would be in the middle of the bit-stream; you can't just compactMap on $0.words.

If you want a wide type to test, unfortunately DoubleWidth isn't guaranteed to ship (or, at least, as a public type) or I'd simply recommend that to you. You can make a much more trivial Int65 type, though, with a sign bit and a UInt64 magnitude. Then, make sure words returns the expected sequence (on a 64-bit system, a sequence of two Int values, the last one being the sign-extended sign bit; on a 32-bit system, a sequence of four Int values, the last two being the sign-extended sign bit). For required methods that you'll never use or test, you can simply bail with fatalError(), which is superior to having an untested but seemingly complete implementation.

The initializer only takes unsigned types, so Int65 is out. I was originally thinking of a UInt65, but noticed the byteSwapped property requirement. I didn't want to figure out how to byte-swap a straggler bit, so I changed to a straggler byte (i.e. 64 to 72 instead of 65).