# AIOStreams DSU Dedup — Kotlin Port

## Why DSU beats LinkedHashMap

LinkedHashMap dedup only handles **pairwise** collisions (shared key A↔B).  
DSU handles **transitive** collisions: A shares filename with B, B shares infoHash with C → all three are correctly deduplicated.

## Key architecture

StreamDeduplicator uses 3 key types with a Union-Find core:

| Key Type | Source | Example |
|----------|--------|--------|
| FILENAME | Normalized name | `fn:movie20241080pblurayx264flux` |
| INFO_HASH | infoHash + fileIdx | `ih:abc123def456:0` |
| SMART_DETECT | geometric size + name tail | `sd:3309610060:movie2024...` |

## Pipeline

1. **Key construction** — for each stream, build 1-3 keys from its metadata
2. **Union phase** — for every key shared by multiple streams, union their DSU sets
3. **Phase 2** — apply multi-group behaviour (cached wins, delete uncached/P2P)
4. **Within-group selection** — per-type dedup mode (SINGLE_RESULT / PER_SERVICE / PER_ADDON)

## Geometric size rounding

Rounds near-equal file sizes (1.2GB vs 1.3GB) into same bucket at 10% rounding:

```kotlin
fun geometricRound(bytes: Long, roundPct: Int = 10): Long {
    if (bytes <= 0) return 0
    val logStep = Math.log(1 + roundPct / 100.0)
    val bucketIndex = Math.round(Math.log(bytes.toDouble()) / logStep)
    return Math.round(Math.exp(bucketIndex * logStep)).toLong()
}
```

## DSU implementation

```kotlin
internal class DisjointSetUnion(private val n: Int) {
    private val parent = IntArray(n) { it }
    private val rank = IntArray(n)

    fun find(x: Int): Int {
        var node = x
        while (parent[node] != node) {
            parent[node] = parent[parent[node]]
            node = parent[node]
        }
        return node
    }

    fun union(a: Int, b: Int) {
        val ra = find(a)
        val rb = find(b)
        if (ra == rb) return
        when {
            rank[ra] < rank[rb] -> parent[ra] = rb
            rank[ra] > rank[rb] -> parent[rb] = ra
            else -> { parent[rb] = ra; rank[ra]++ }
        }
    }
}
```

## Dedup modes

| Mode | Behavior |
|------|----------|
| SINGLE_RESULT | Keep the single best stream per group |
| PER_SERVICE | Keep one per debrid service per group |
| PER_ADDON | Keep one per source addon per group |
| DISABLED | Keep all |
