Skip to content

Instantly share code, notes, and snippets.

@atierian
Last active June 19, 2021 18:49
Show Gist options
  • Select an option

  • Save atierian/2b00a678986b7aa4e87d7cce18043987 to your computer and use it in GitHub Desktop.

Select an option

Save atierian/2b00a678986b7aa4e87d7cce18043987 to your computer and use it in GitHub Desktop.
Break down Ints and do addition at the bit level
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