Теперь я реализую инициализатор Sequenceпроизвольных слов, который я угрожал сделать в Части 2.



Хотя BitArray внутренне использует [UInt] для хранения упакованных битов, я не хочу навязывать это в общедоступном инициализаторе. Пользователь должен иметь возможность использовать любой тип UnsignedInteger для хранения упакованных логических значений; Я сделаю любое перераспределение между их типом и UInt во время конвертации.

Преобразование Sequence слов — это то же самое, что преобразование каждого слова по отдельности, а затем объединение этих результатов вместе. Итак, давайте начнем с инициализатора для преобразования отдельных слов:

extension BitArray {
    public init<W: UnsignedInteger>(word: W, bitCount: Int, bitIterationDirection: EndianTraversal) {
        precondition(0...word.bitWidth ~= bitCount)
        let coreWords: WordArray
        switch bitIterationDirection {
        case .lo2hi:
            coreWords = WordArray(word.words)
        case .hi2lo:
            let (wbwq, wbwr) = word.bitWidth.quotientAndRemainder(dividingBy: Word.bitWidth)
            let wordWords = WordArray(word.words)
            assert(wbwq + wbwr.signum() == wordWords.count)
            let highOrderWordBitCount = wbwr != 0 ? wbwr : wbwq.signum() * Word.bitWidth
            let head = BitArray(coreWords: [wordWords.last! << (Word.bitWidth - highOrderWordBitCount)], bitCount: highOrderWordBitCount, bitIterationDirection: .hi2lo)
            var tail = BitArray(coreWords: wordWords.dropLast().reversed(), bitCount: Word.bitWidth * (wordWords.count - 1), bitIterationDirection: .hi2lo)
            tail.prependHead(head)
            coreWords = tail.bits
        }
        self.init(coreWords: coreWords, bitCount: bitCount, bitIterationDirection: bitIterationDirection)
    }
    //...
}

Суть в том, что каждый объект, соответствующий BinaryInteger, включая объекты, соответствующие UnsignedInteger, должен предоставлять метод доступа к своему двоичному значению. Этот метод доступа является свойством уровня экземпляра с именем words, которое является Sequence из UInt, но тайно должно быть вместо этого Collection. Возвращаемые слова должны быть от младшего к старшему, причем биты внутри должны иметь одинаковый приоритет.

Этот формат уже почти идеален для моих нужд. Это определенно происходит, когда инициализатору необходимо читать с младших битов вверх. При чтении в другом направлении мне нужно учитывать, что слово старшего порядка может не использовать все свои биты. Я справляюсь с этим, читая это слово высшего порядка и другие слова в отдельных проходах, а затем сшиваю результаты вместе. Собрав биты в моем формате, я передаю их своему главному инициализатору.

Инициализатор Sequence-произвольных-слов просто составлен из работы, которую я уже сделал, и из стандартной библиотеки:

{
    //...
    public init<S>(words: S, bitCount: Int, bitIterationDirection: EndianTraversal) where S: Sequence, S.Element: UnsignedInteger {
        let bitArraySlivers = words.map { BitArray(word: $0, bitCount: $0.bitWidth, bitIterationDirection: bitIterationDirection) }
        var scratch = BitArray(coreWords: [], bitCount: 0, bitIterationDirection: bitIterationDirection)
        for s in bitArraySlivers.reversed() {
            scratch.prependHead(s)
        }
        self = scratch.head(bitCount: bitCount)
    }
}

Большой разрыв между этим постом и предыдущей частью был из-за тестирования. Чтобы полностью протестировать инициализатор из одного слова, один из исходных типов должен быть больше UInt. Пришлось делать свой, и я отвлекся на полную реализацию арифметических операций. Но они мне здесь не нужны; только words должен быть полностью функциональным, а все остальное может быть заглушкой. Я нашел баг при создании указанной заглушки, так что это не было пустой тратой времени:

// An unsigned integer type bigger than any of the standard ones.
struct UInt72: FixedWidthInteger, UnsignedInteger {
    // Implementation properties
    var high: UInt8
    var low: UInt64
    // Main initializer
    init(highOrderBits hi: UInt8, lowOrderBits lo: UInt64) { (high, low) = (hi, lo) }
    // FixedWidthInteger secret initializer
    init(_truncatingBits bits: UInt) { self.init(highOrderBits: 0, lowOrderBits: UInt64(bits)) }
    // FixedWidthInteger properties
    var byteSwapped: UInt72 {
        return UInt72(highOrderBits: UInt8(truncatingIfNeeded: low), lowOrderBits: (low.byteSwapped << 8) | UInt64(high))
    }
    var leadingZeroBitCount: Int { return high != 0 ? high.leadingZeroBitCount : 8 + low.leadingZeroBitCount }
    var nonzeroBitCount: Int { return high.nonzeroBitCount + low.nonzeroBitCount }
    static var bitWidth: Int { return 72 }
    // BinaryInteger properties
    var trailingZeroBitCount: Int { return low != 0 ? low.trailingZeroBitCount : high.trailingZeroBitCount + 64 }
    var words: [UInt] { return Array(low.words) + high.words }
    // ExpressibleByIntegerLiteral and Hashable support
    init(integerLiteral value: UInt) { self.init(_truncatingBits: value) }
    var hashValue: Int { return String(describing: self).hashValue }
    // BinaryInteger floating-point initializer
    init<T>(_ source: T) where T : BinaryFloatingPoint { fatalError("\(#function) not implemented") }
    // FixedWidthInteger core math
    func addingReportingOverflow(_ rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
        fatalError("\(#function) not implemented")
    }
    func dividedReportingOverflow(by rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
        fatalError("\(#function) not implemented")
    }
    func dividingFullWidth(_ dividend: (high: UInt72, low: UInt72)) -> (quotient: UInt72, remainder: UInt72) {
        fatalError("\(#function) not implemented")
    }
    func multipliedReportingOverflow(by rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
        fatalError("\(#function) not implemented")
    }
    func multipliedFullWidth(by other: UInt72) -> (high: UInt72, low: UInt72) {
        fatalError("\(#function) not implemented")
    }
    func remainderReportingOverflow(dividingBy rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
        fatalError("\(#function) not implemented")
    }
    func subtractingReportingOverflow(_ rhs: UInt72) -> (partialValue: UInt72, overflow: Bool) {
        fatalError("\(#function) not implemented")
    }
    // BinaryInteger operators
    static func %(lhs: UInt72, rhs: UInt72) -> UInt72 {
        let results = lhs.remainderReportingOverflow(dividingBy: rhs)
        assert(!results.overflow)
        return results.partialValue
    }
    static func *(lhs: UInt72, rhs: UInt72) -> UInt72 {
        let results = lhs.multipliedReportingOverflow(by: rhs)
        assert(!results.overflow)
        return results.partialValue
    }
    static func +(lhs: UInt72, rhs: UInt72) -> UInt72 {
        let results = lhs.addingReportingOverflow(rhs)
        assert(!results.overflow)
        return results.partialValue
    }
    static func -(lhs: UInt72, rhs: UInt72) -> UInt72 {
        let results = lhs.subtractingReportingOverflow(rhs)
        assert(!results.overflow)
        return results.partialValue
    }
    static func /(lhs: UInt72, rhs: UInt72) -> UInt72 {
        let results = lhs.dividedReportingOverflow(by: rhs)
        assert(!results.overflow)
        return results.partialValue
    }
    static func %=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs % rhs }
    static func *=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs * rhs }
    static func +=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs + rhs }
    static func -=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs - rhs }
    static func /=(lhs: inout UInt72, rhs: UInt72) { lhs = lhs / rhs }
}

Будущее обновление Swift может потребовать от меня добавления отсутствующих операторов битового сдвига.