Skip to content

Latest commit

 

History

History
122 lines (93 loc) · 4.8 KB

File metadata and controls

122 lines (93 loc) · 4.8 KB

Eignex

KPermute

Maven Central Build codecov License

KPermute generates fast, deterministic, reversible integer permutations over arbitrary integer domains using bijective hash mixing.

Overview

Each seed defines a unique bijection over [0, size). The result behaves like a keyed shuffle: repeatable, memory-efficient, and invertible.

Typical uses are obfuscating numeric IDs (such as user or session numbers), generating reproducible pseudo-random shuffles, collision-free sampling and load balancing, and masking non-sensitive identifiers. It is not a cryptographic primitive; if your use case is cryptographic, choose a real cipher.

Installation

implementation("com.eignex:kpermute:1.2.0")

Usage

Obfuscate a numeric ID reproducibly:

val idPerm = longPermutation(seed = 1L)
val longId = 49102490812045L
val encoded = idPerm.encode(longId)
println("encoded: $encoded (always prints 3631103739497407856)")

Shuffle a large list lazily:

val largeList = object : AbstractList<Int>() {
    override val size: Int get() = Int.MAX_VALUE
    override fun get(index: Int) = index
}
val perm = intPermutation(largeList.size)
val shuffled = largeList.permuted(perm)
println("shuffled: ${shuffled.take(20)}")
val unshuffled = shuffled.unpermuted(perm)
println("unshuffled: ${unshuffled.take(20)}")

Permute a custom range, including negatives:

val rangePerm = intPermutation(-100..199)
println("encode(-50): ${rangePerm.encode(-50)}")
println("decode(...): ${rangePerm.decode(rangePerm.encode(-50))}")

Permute the full 32-bit domain:

val fullPerm = intPermutation(-1, seed = 1L)
println(fullPerm.encode(0)) // 1339315335
println(fullPerm.encode(1)) // -897806455

How It Works

KPermute builds keyed, reversible permutations over integer domains using xor-shift-multiply mixers and cycle-walking. It never stores lookup tables, and every output decodes back to its original value.

A permutation is selected by its size. A positive size means a finite domain [0, size). A size of -1 or -1L means the full 32- or 64-bit signed domain. Other negative sizes select the unsigned variants via UIntPermutation or ULongPermutation.

Domain Type Implementation Description
Tiny (≤16) Array[Int/Long]Permutation Uses shuffled array and inverse
Finite Half[Int/Long]Permutation Uses cycle-walking
Full bit-width Full[Int/Long]Permutation No cycle-walking
Unsigned variants U[Int/Long]Permutation Modulo 2^32 or 2^64

Range factories like intPermutation(range) and longPermutation(range) wrap these with a range view, so you can permute directly on intervals such as -100..199.

Each round multiplies by an odd constant, adds or xors a secret per-round key, and applies xor-shift steps (x ^= x >>> s) to diffuse bits. Every step is invertible via modular inverses and xor-shift inversion 1 3. For non-power-of-two domains KPermute uses cycle-walking 1 2: permute in the next power-of-two space and retry until the output falls in [0, size).

References

  1. P. Rogaway and T. Shrimpton, “Ciphers with Arbitrary Finite Domains,” CT-RSA 2002. PDF
  2. M. Bellare, P. Rogaway, and T. Spies, “The FFX Mode of Operation for Format-Preserving Encryption,” NIST submission, 2010. Spec
  3. B. Jenkins, “Integer Hash Functions,” 1997. Web