# Set Similarity

> NumKong for Swift

Binary metrics work on packed words instead of boolean slices.
That is the right model once the workload is "semantic hash" or "binary embedding" rather than "array of booleans".
`U1x8` packs 8 bits into one byte.

```swift
import NumKong

// Each U1x8 holds 8 bits. Two elements = 16 bits total.
let a: [U1x8] = [U1x8(bitPattern: 0b10101010), U1x8(bitPattern: 0b11110000)]
let b: [U1x8] = [U1x8(bitPattern: 0b10101110), U1x8(bitPattern: 0b11000000)]

let hamming = a.hamming(b) // UInt32: count of differing bits
let jaccard = a.jaccard(b) // Float32: Jaccard distance in [0, 1]

print(hamming as Any, jaccard as Any)
```

`Hamming` returns `UInt32` — the count of differing bits across all packed words.
`Jaccard` returns `Float32` — the set-theoretic distance computed on bit populations.

### Set Distance Kernels

Set distance kernels operate on `U1x8` matrices where each row is a packed binary vector.
The same packed and symmetric shapes available for dense metrics exist here.

```swift
import NumKong

// Eight binary vectors, each 16 bits wide (2 x U1x8 per row)
let rows = 8
let columns = 2
var rawBits = [U1x8](repeating: U1x8(bitPattern: 0b10101010), count: rows * columns)

let t = try rawBits.withUnsafeMutableBufferPointer { buf -> Tensor<U1x8> in
    let data = Array(buf)
    return try Tensor<U1x8>.fromArray(data, rows: rows, columns: columns)
}

let packed = try PackedMatrix<U1x8>(packing: t.view())

// Cross-matrix Hamming distances: shape [8, 8]
let hammings = try t.hammingsPacked(packed)  // Tensor<UInt32>

// Symmetric all-pairs Jaccard distances: shape [8, 8]
let jaccards = try t.jaccardsSymmetric()     // Tensor<Float32>

assert(hammings.rows == rows && hammings.columns == rows)
assert(jaccards.rows == rows && jaccards.columns == rows)
```

Free-function forms are also available:

```swift
try hammingsPacked(view, packed, &span)
try jaccardsPacked(view, packed, &span)
try hammingsSymmetric(view, &span, rowsBegin: 0, rowsEnd: rows)
try jaccardsSymmetric(view, &span, rowsBegin: 0, rowsEnd: rows)
```

`import NumKong`

## NumKongHamming

A type that can compute SIMD-accelerated Hamming distances over bit sets.

```swift
protocol NumKongHamming
```

Conforming types: `[U1x8](https://www.unum.cloud/docs/numkong/swift/scalar-types.md#U1x8)`

## HammingOutput

The type the Hamming distance is returned in.

```swift
associatedtype HammingOutput
```

## hamming

Computes the Hamming distance between two bit sets.

```swift
static func hamming<A, B>(
    _ a: A,
    _ b: B
) -> HammingOutput? where Self == A.Element, A : Sequence, B : Sequence, A.Element == B.Element
```

- `a`: The first bit set.
- `b`: The second bit set, as long as `a`.

## NumKongJaccard

A type that can compute SIMD-accelerated Jaccard distances over bit sets.

```swift
protocol NumKongJaccard
```

Conforming types: `[U1x8](https://www.unum.cloud/docs/numkong/swift/scalar-types.md#U1x8)`

## JaccardOutput

The type the Jaccard distance is returned in.

```swift
associatedtype JaccardOutput
```

## jaccard

Computes the Jaccard distance between two bit sets.

```swift
static func jaccard<A, B>(
    _ a: A,
    _ b: B
) -> JaccardOutput? where Self == A.Element, A : Sequence, B : Sequence, A.Element == B.Element
```

- `a`: The first bit set.
- `b`: The second bit set, as long as `a`.

