Last active
June 19, 2021 18:49
-
-
Save atierian/2b00a678986b7aa4e87d7cce18043987 to your computer and use it in GitHub Desktop.
Break down Ints and do addition at the bit level
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Foundation | |
| import XCTest | |
| enum Bit: Int, CustomDebugStringConvertible { | |
| case zero = 0 | |
| case one | |
| var debugDescription: String { | |
| rawValue.description | |
| } | |
| } | |
| extension Array where Element == Bit { | |
| /// ```[.zero, .zero, .zero, .zero, .one, .zero, .one] -> [0, 0, 0, 0, 1, 0, 1]``` | |
| var asBits: [UInt8] { | |
| map { UInt8($0.rawValue) } | |
| } | |
| } | |
| extension UInt8 { | |
| /// Convert Byte to Array of Bits ```1 -> [.zero, .zero, .zero, .zero, .zero, .zero, .one]``` | |
| var bits: [Bit] { | |
| let bitCount = bitWidth | |
| var bitsArray = [Bit](repeating: .zero, count: 8) | |
| for i in 0..<8 { | |
| let bitValue: UInt8 = 1 << UInt8(bitCount - 1 - i) | |
| let check = self & bitValue | |
| if check != 0 { | |
| bitsArray[i] = .one | |
| } | |
| } | |
| return bitsArray | |
| } | |
| } | |
| infix operator <+ : AdditionPrecedence | |
| extension UInt8 { | |
| /// Add two UInt8 together using bitwise operators | |
| static func <+ (lhs: UInt8, rhs: UInt8) -> UInt8 { | |
| lhs.bitAdd(rhs) | |
| } | |
| /// Add two UInt8 together using bitwise operators | |
| func bitAdd(_ y: UInt8) -> UInt8 { | |
| let uncommon = self ^ y | |
| let common = self & y | |
| if common != 0 { | |
| return uncommon <+ common << 1 | |
| } | |
| return uncommon | |
| } | |
| } | |
| extension Int { | |
| /// Convert Integer into an array of digits ```1945 -> [1, 9, 4, 5]``` | |
| var digits: [UInt8] { | |
| var solution = [UInt8]() | |
| var workingCopy = self | |
| while workingCopy > 0 { | |
| let (quotient, remainder) = workingCopy.quotientAndRemainder(dividingBy: 10) | |
| workingCopy = quotient | |
| solution.append(UInt8(remainder)) | |
| } | |
| return solution.reversed() | |
| } | |
| } | |
| extension Array where Element == UInt8 { | |
| /// Break down 2 bytes into 2 sets bits and add them together without using bitwise operators | |
| func manualBitAdd(_ y: [UInt8]) -> UInt8 { | |
| let stage = zip(self, y).map { $0.0 + $0.1 } | |
| let common = stage.map { b -> UInt8 in | |
| b == 2 ? 1 : 0 | |
| } | |
| let uncommon = stage.map { b -> UInt8 in | |
| b == 1 ? 1 : 0 | |
| } | |
| if common.reduce(0, +) != 0 { | |
| var shiftedCommon = common | |
| for i in common.enumerated() where i.element == 1 { | |
| shiftedCommon[i.offset-1] = 1 | |
| shiftedCommon[i.offset] = 0 | |
| } | |
| return shiftedCommon.manualBitAdd(uncommon) | |
| } | |
| return uncommon.bitsToByte | |
| } | |
| /// Convert an 8-digit Array of bits into Byte ```[0, 0, 0, 0, 0, 0, 1] -> 1``` | |
| var bitsToByte: UInt8 { | |
| reduce(0) { total, current in | |
| total << 1 | current | |
| } | |
| } | |
| /// convert array of digits to single Int ```[1, 9, 4, 5] -> 1945``` | |
| var numericRepresentation: Int { | |
| var solution = 0 | |
| for (idx, element) in reversed().enumerated() { | |
| solution += Int(element) * Int(pow(10, Double(idx))) | |
| } | |
| return solution | |
| } | |
| } | |
| // MARK: Tests | |
| class BitMathTests: XCTestCase { | |
| func testManualBitAddition() { | |
| let a: UInt8 = 5 | |
| let b: UInt8 = 42 | |
| let sum = a.bits.asBits.manualBitAdd(b.bits.asBits) | |
| XCTAssertEqual(sum, 47) | |
| } | |
| func testBitwiseAddition() { | |
| let a: UInt8 = 5 | |
| let b: UInt8 = 42 | |
| XCTAssertEqual(a <+ b, 47) | |
| } | |
| } | |
| BitMathTests.defaultTestSuite.run() | |
| // MARK: Usage | |
| let b: UInt8 = 5 | |
| let c: UInt8 = 72 | |
| b <+ c // 77 | |
| b.bits.asBits.manualBitAdd(c.bits.asBits) // 77 | |
| let number = 1945 | |
| let a: UInt8 = 2 | |
| let bitMathWithBitwise = number.digits | |
| .map { ($0 <+ a) % 10 } | |
| .numericRepresentation | |
| // 3167 | |
| let manualBitMath = number.digits | |
| .map { $0.bits.asBits } | |
| .map { $0.manualBitAdd(a.bits.asBits) % 10 } | |
| .numericRepresentation | |
| // 3167 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment