Skip to content

Bitwise Calculator — Swift source

Perform AND, OR, XOR, NOT, shifts and rotates on 8/16/32/64-bit values with exact bigint math. Enter operands in binary, octal, decimal or hex and read the result in every base plus a live bit grid. Runs 100% in your browser.

This is the Swift implementation — the same logic the interactive tool runs, in a shareable, citable form.

// =============================================================================
//  bitwise.swift — CosmoDev polyglot showcase port of the `bitwise` tool
//  -----------------------------------------------------------------------------
//  Language : Swift (5.9, standard library only)
//  Source:   ported from src/lib/bitwise.ts (the canonical, live TypeScript
//             lib); mirrors src/tool-sources/bitwise/{python.py,rust.rs}
//  License  : display source — part of CosmoDev's polyglot tool pages
//             (dev.cosmolabs.org). Shown verbatim alongside the JS/TS/Go/Rust/
//             Python ports and the other language ports.
//  -----------------------------------------------------------------------------
//  Pure, deterministic bitwise calculator. Zero deps. Operands are interpreted
//  as width-bit two's-complement values: any integer is normalized to the
//  half-open range [0, 2^width) before an operation, and every result is masked
//  back into that range — so the returned integer is always the unsigned
//  bit-pattern of the width-bit result.
//
//  Swift note: the TS source uses arbitrary-precision `bigint`. Swift 5.9 has
//  no bignum or 128-bit integer, so values live in Int64/UInt64. This stays
//  exact for every supported width (8/16/32/64): parsed magnitudes fold
//  through two's-complement wrapping (invisible after width-bit
//  normalization), and shift/rotate intermediates pre-mask the operand so
//  nothing exceeds 64 bits — arithmetically identical to the reference's
//  masked bigint shifts. Only literals beyond 2^64-1 diverge from the TS lib
//  (thrown as a clean out-of-range), far outside any realistic input.
// =============================================================================

/// Numeric radix used for parsing and formatting.
enum Base {
    case bin, oct, dec, hex

    /// Positional radix (2/8/10/16).
    var radix: UInt64 {
        switch self {
        case .bin: return 2
        case .oct: return 8
        case .dec: return 10
        case .hex: return 16
        }
    }

    /// Lowercase digit alphabet valid for this base.
    var digits: String {
        switch self {
        case .bin: return "01"
        case .oct: return "01234567"
        case .dec: return "0123456789"
        case .hex: return "0123456789abcdef"
        }
    }

    /// Lowercase name, for error messages that match the TS lib.
    var lower: String {
        switch self {
        case .bin: return "bin"
        case .oct: return "oct"
        case .dec: return "dec"
        case .hex: return "hex"
        }
    }
}

/// Bit-width of the virtual register (8/16/32/64). `Int` is the natural Swift
/// choice; the library asserts the documented set.
typealias Width = Int

/// Supported bitwise operation. `.not` is unary on `a`; the rest are binary,
/// with `b` as the shift/rotate count for the shift/rotate ops.
enum Op {
    case and, or, xor, not, shl, shr, rol, ror
}

/// Parse error: an empty value, an out-of-base digit, or an out-of-range literal.
struct ParseError: Error, CustomStringConvertible {
    let message: String
    var description: String { message }
}

enum Bitwise {
    /// Parse a numeric string in `base` into a signed Int64. Strips 0x/0b/0o
    /// prefixes and an optional leading sign. Throws `ParseError` on empty
    /// input, any out-of-base digit, or a literal whose magnitude exceeds
    /// 2^64-1. The raw signed value is returned (no width normalization);
    /// callers fold it into a field via `normalize` / `apply`.
    ///
    /// Magnitudes accumulate as UInt64 (checked with Swift's reporting-
    /// overflow arithmetic) and negate by wrapping subtraction, so the full
    /// unsigned range (e.g. hex 0xFFFFFFFFFFFFFFFF) round-trips exactly — a
    /// wrap of 2^64 is invisible once the value is width-normalized.
    static func parse(_ value: String, _ base: Base) throws -> Int64 {
        // Trim ASCII whitespace from both ends (stdlib-only; Character
        // .isWhitespace covers spaces, tabs, and newlines).
        var chars = Substring(value)
        while let f = chars.first, f.isWhitespace { chars.removeFirst() }
        while let l = chars.last, l.isWhitespace { chars.removeLast() }
        let trimmed = String(chars)
        guard !trimmed.isEmpty, trimmed != "-" else {
            throw ParseError(message: "Empty \(base.lower) value")
        }

        // Peel off an optional leading '-' so negative literals parse correctly.
        let negative = trimmed.hasPrefix("-")
        let body = negative ? String(trimmed.dropFirst()) : trimmed

        // Strip each base prefix in turn (0x, then 0b, then 0o) from a lowered
        // copy — mirrors the reference's chained leading-prefix removal.
        // Matching against the lowercased copy keeps "0xFF" valid.
        var digits = body.lowercased()
        for prefix in ["0x", "0b", "0o"] where digits.hasPrefix(prefix) {
            digits.removeFirst(prefix.count)
        }
        guard !digits.isEmpty else {
            throw ParseError(message: "Empty \(base.lower) value")
        }

        // Horner's method over the digit alphabet, with a checked accumulate:
        // an over-wide literal reports a clean error instead of trapping.
        let allowed = base.digits
        let radix = base.radix
        var acc: UInt64 = 0
        for ch in digits {
            guard let idx = allowed.firstIndex(of: ch) else {
                throw ParseError(message: "Invalid digit '\(ch)' for base \(base.lower)")
            }
            let digit = UInt64(allowed.distance(from: allowed.startIndex, to: idx))
            let (mul, o1) = acc.multipliedReportingOverflow(by: radix)
            let (add, o2) = mul.addingReportingOverflow(digit)
            if o1 || o2 {
                throw ParseError(message: "Value out of range for base \(base.lower)")
            }
            acc = add
        }

        // Wrapping negate is exact mod 2^64 (two's complement); bitPattern
        // reinterprets the result as signed.
        let pattern = negative ? 0 &- acc : acc
        return Int64(bitPattern: pattern)
    }

    /// Mask for a `width`-bit field: 2^width - 1. Width 64 is spelled
    /// directly because 1 << 64 would trap in UInt64.
    static func mask(_ width: Width) -> UInt64 {
        width == 64 ? .max : ((1 as UInt64) << width) - 1
    }

    /// Normalize any signed value to its unsigned width-bit two's-complement
    /// value, i.e. into the half-open range [0, 2^width) — e.g. -1 at width 8
    /// yields 255. The reference computes ((n % m) + m) % m with m = 2^width
    /// on arbitrary-precision ints; casting to UInt64 already yields
    /// n mod 2^64 (two's-complement truncation), and ANDing with the width-bit
    /// mask reduces that to n mod 2^width — the same canonical value, without
    /// materializing 2^64 itself.
    static func normalize(_ n: Int64, _ width: Width) -> UInt64 {
        UInt64(bitPattern: n) & mask(width)
    }

    /// Render `n` in `base`, zero-padded to at least `minDigits` digits.
    /// Negatives carry a leading '-' and format their magnitude (via wrapping
    /// negation on the bit pattern — exact even at Int64.min).
    /// `minDigits` corresponds to the `width` parameter of the TS reference
    /// (a width-bit binary value needs exactly `width` digits).
    ///
    /// String(_:radix:) produces the lowercase digits of the TS
    /// `bigint.toString(radix)` output and renders "0" for zero naturally.
    static func format(_ n: Int64, _ base: Base, _ minDigits: Int) -> String {
        if n < 0 {
            // Wrapping negate yields the true magnitude as a bit pattern.
            let mag = 0 &- UInt64(bitPattern: n)
            return "-" + String(mag, radix: Int(base.radix)).padding(minDigits)
        }
        return String(UInt64(bitPattern: n), radix: Int(base.radix)).padding(minDigits)
    }

    /// Apply a width-bit operation. `a` is the (unary) operand for `.not`;
    /// `b` is the second operand for binary ops and the shift/rotate count
    /// for shl/shr/rol/ror. Both operands are normalized to width-bit two's
    /// complement first; the result is masked to `width` bits.
    ///
    /// For shl/shr we short-circuit when the shift count meets or exceeds the
    /// width: every significant bit is shifted out, so the masked result is
    /// zero (this also keeps every shift amount below 64 — Swift's `<<`
    /// traps on shift counts >= 64). For shl/rol the shifted operand is
    /// pre-masked with (mask >> count): the low `width` bits of the true
    /// bigint shift are identical, but the intermediate can no longer exceed
    /// 64 bits.
    static func apply(_ op: Op, _ a: Int64, _ b: Int64, _ width: Width) -> UInt64 {
        let m = mask(width)
        let x = normalize(a, width)
        let y = normalize(b, width)

        switch op {
        case .and:
            return x & y
        case .or:
            return x | y
        case .xor:
            return x ^ y
        case .not:
            return ~x & m
        case .shl:
            if y >= numericCast(width) {
                return 0
            }
            return (x & (m >> y)) << Int(y) // == (x << y) & m, without trapping
        case .shr:
            // x is normalized non-negative → logical (zero-filling) shift.
            return y >= numericCast(width) ? 0 : x >> Int(y)
        case .rol, .ror:
            let shift = y % numericCast(width) // rotate amount wraps within width
            if shift == 0 {
                return x
            }
            // A right-rotate by `shift` is a left-rotate by (width - shift).
            let s = op == .rol ? shift : UInt64(width) - shift
            return ((x & (m >> s)) << Int(s)) | (x >> (Int(width) - Int(s)))
        }
    }

    /// Fixed-width binary string of `width` bits (MSB first). Formats the
    /// unsigned normalized pattern directly: a width-64 pattern can exceed
    /// Int64.max and must not round-trip through the signed path.
    static func toBits(_ n: Int64, _ width: Width) -> String {
        String(normalize(n, width), radix: 2).padding(width)
    }

    /// Indices of set bits (LSB = index 0), normalized to `width`, in
    /// ascending order. Stops at the highest set bit.
    static func flags(_ n: Int64, _ width: Width) -> [Int] {
        var v = normalize(n, width)
        var out: [Int] = []
        var i = 0
        while v != 0 {
            if v & 1 != 0 {
                out.append(i)
            }
            v >>= 1
            i += 1
        }
        return out
    }
}

private extension String {
    /// Zero-pad to at least `minDigits` places (String has no padStart).
    func padding(_ minDigits: Int) -> String {
        count < minDigits ? String(repeating: "0", count: minDigits - count) + self : self
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →