Теперь я реализую инициализатор 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 может потребовать от меня добавления отсутствующих операторов битового сдвига.