0

I'm trying to implement something like binary masking where I turn an Int into an array of it's components (powers of 2), for example:

69 = [64, 4, 1]

I don't want to use binary masking and shifting, because I want to use Swift enums for interface elements:

enum State: Int{
    case readyButton = 1
    case workingButton = 2
    case sleepingButton = 4
   //etc
}

then 7 = [.sleepingButton, .workingButton, .readyButton] // 7 = 4+2+1

Are there some tools built in Swift to help me turn an Int into power of 2 components?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Alex Stone
  • 46,408
  • 55
  • 231
  • 407

2 Answers2

2

You may be looking for something like this. As it is written, it will crash when the values do not match up, but you can adapt it for your purposes. Though it does use shifting, it still converts to your enum nicely.

extension UInt32 {
    func state() -> [State] {
        var bitvals: [UInt32] = []
        var mask: UInt32 = 1
        while mask > 0 {
            if mask & self > 0 {
                bitvals.append(mask)
            }
            mask = mask << 1
        }
        let state = bitvals.map { State(rawValue: $0)! }
        return state
    }
}
enum State: UInt32 {
    case readyButton = 1
    case workingButton = 2
    case sleepingButton = 4
}

let val: UInt32 = 7
print(val.state())

This would print out the example you gave at the end of your question.

Adam H.
  • 681
  • 3
  • 8
1

Updated answer to convert any Int into an array of powers of 2. Uncomment fatal error if you want it to crash on negative values

enum State: Int {
    case illegal = -1
    case readyButton = 1
    case workingButton = 2
    case sleepingButton = 4
}

Here is a general solution for decomposing integers into powers of 2:

extension Int {

    func toPowersOf2() -> [Int] {
        guard self > 0 else {
//            fatalError("The number should be strictly positive")
            print("The number should be strictly positive")
            return [-1] //not really - use fatal error above to enforce crash if you don't want this behavior
        }
        var arrayOfPowers: [Int] = [] //Will hold the desired powers
        var remainder: Int = self   //We will substract found powers from the original number

        //Since Ints are coded on 64 bits (the 64th is for the sign)
        //Let's create an array of all the powers of 2 that
        //could be used to decompose an integer
        let powers = (0...62).map { NSDecimalNumber(decimal: pow(2.0, $0)).intValue }

        //Let's go from the highest to the lowest power
        for i in (0 ..< powers.count).reversed() {
            //Here we are looking for the power just smaller than the remainder
            if i < powers.count - 1, powers[i] <= remainder, powers[i + 1] > remainder {
                let p = powers[i]
                arrayOfPowers.append(p)
                remainder -= p
            }

                //if this is the biggest power and it is smaller than the number, then add it to arrayOfPowers
            else if i == powers.count - 1, powers[i] <= remainder {
                let p = powers[i]
                arrayOfPowers.append(p)
            }
        }
        return arrayOfPowers
    }

    func toStateArray() -> [State] {
        let array = self.toPowersOf2().map{ State(rawValue: $0) }.filter{ $0 != nil }
        return array as! [State]
    }
}

And you could use it like so:

(-1).toStateArray()//[illegal]
0.toStateArray()   //[illegal]
7.toPowersOf2()    //[4, 2, 1]
7.toStateArray()   //[sleepingButton, workingButton, readyButton]
Alex Stone
  • 46,408
  • 55
  • 231
  • 407
ielyamani
  • 17,807
  • 10
  • 55
  • 90