extension BinaryInteger {
  @inlinable
  public var isSmith: Bool {
    guard self > 3 else {
      return false
    }

    let primeFactors = primeDecomposition()

    guard primeFactors.count!= 1 else {
      return false
    }

    return primeFactors.map({ $0.sumDigits() }).reduce(0, +) == sumDigits()
  }

  @inlinable
  public func primeDecomposition() -> [Self] {
    guard self > 1 else { return [] }

    func step(_ x: Self) -> Self {
      return 1 + (x << 2) - ((x >> 1) << 1)
    }

    let maxQ = Self(Double(self).squareRoot())
    var d: Self = 1
    var q: Self = self & 1 == 0? 2: 3

    while q <= maxQ && self% q!= 0 {
      q = step(d)
      d += 1
    }

    return q <= maxQ? [q] + (self / q).primeDecomposition(): [self]
  }

  @inlinable
  public func sumDigits() -> Self {
    return String(self).lazy.map({ Self(Int(String($0))!) }).reduce(0, +)
  }
}

let smiths = (0..<10_000).filter({ $0.isSmith })

print("Num Smith numbers below 10,000: \(smiths.count)")
print("First 10 smith numbers: \(Array(smiths.prefix(10)))")
print("Last 10 smith numbers below 10,000: \(Array(smiths.suffix(10)))")