/* Module "perm.wren" */
import "./check" for Check
/*
Perm is a class containing static methods to count or emit the permutations of a given array.
In the interests of speed, permutations are usually emitted in no particular order. However,
slower methods are included to emit them in lexicographic order of the array indices where needed.
So, if the array is sorted to begin with, such permutations will be in sorted order too.
*/
class Perm {
// Returns the number of permutations of an array with 'n' different elements.
static count(n) {
Check.int("n", n, 1, 18)
if (n == 1) return 1
var fact = 1
for (i in 2..n) fact = fact * i
return fact
}
// Returns the number of permutations of an array with 'n' elements which are not all different.
// The argument 'f' is a list of the frequencies of the different elements in any order.
static countDistinct(n, f) {
Check.int("n", n, 1, 18)
Check.typedList("frequency array", f, "Int", false, 1)
var sum = f.reduce { |acc, i| acc + i }
if (n != sum) {
Fiber.abort("The elements of the array must sum to 'n'.")
}
var prod = 1
for (e in f) {
if (e < 1) Fiber.abort("The elements of the frequency array must be positive integers.")
if (e > 1) prod = prod * count(e)
}
return count(n)/prod
}
/* Non-lexicographic methods */
// Returns a list of all the permutations of the array 'a' in no particular order.
static list(a) {
Check.list("array", a, 1)
var perms = []
var n = a.count
a = a.toList
var permute
permute = Fn.new { |start, end|
if (start == end) {
perms.add(a.toList)
} else {
var i = start
while (i <= end) {
a.swap(start, i)
permute.call(start+1, end)
a.swap(start, i)
i = i + 1
}
}
}
permute.call(0, n - 1)
return perms
}
// Returns a list of all distinct permutations of the array 'a' in no particular order.
// Use only if not all elements are distinct.
static listDistinct(a) {
Check.list("array", a, 1)
var perms = []
var n = a.count
a = a.toList
var shouldSwap = Fn.new { |start, curr|
var i = start
while (i < curr) {
if (a[i] == a[curr]) return false
i = i + 1
}
return true
}
var permute
permute = Fn.new { |index|
if (index >= n) perms.add(a.toList)
var i = index
while (i < n) {
if (shouldSwap.call(index, i)) {
a.swap(index, i)
permute.call(index+1)
a.swap(index, i)
}
i = i + 1
}
}
permute.call(0)
return perms
}
// Generates all the permutations of the array 'a' in no particular order
// and yields them one by one.
static generate(a) {
Check.list("array", a, 1)
var n = a.count
a = a.toList
var permute
permute = Fn.new { |start, end|
if (start == end) {
Fiber.yield(a.toList)
} else {
var i = start
while (i <= end) {
a.swap(start, i)
permute.call(start+1, end)
a.swap(start, i)
i = i + 1
}
}
}
permute.call(0, n - 1)
}
// Generates all the distinct permutations of the array 'a' in no particular order
// and yields them one by one.
// Use only if not all elements are distinct.
static generateDistinct(a) {
Check.list("array", a, 1)
var perms = []
var n = a.count
a = a.toList
var shouldSwap = Fn.new { |start, curr|
var i = start
while (i < curr) {
if (a[i] == a[curr]) return false
i = i + 1
}
return true
}
var permute
permute = Fn.new { |index|
if (index >= n) Fiber.yield(a.toList)
var i = index
while (i < n) {
if (shouldSwap.call(index, i)) {
a.swap(index, i)
permute.call(index+1)
a.swap(index, i)
}
i = i + 1
}
}
permute.call(0)
}
/* Lexicographic methods */
// Returns a list of all the permutations of the array 'a' in lexicographic order by index.
static listLex(a) {
Check.list("array", a, 1, 18)
var n = a.count - 1
var p = (0..n).toList
var perms = [p.toList]
for (c in 1...count(n+1)) {
var i = n - 1
var j = n
while (p[i] > p[i+1]) i = i - 1
while (p[j] < p[i]) j = j - 1
p.swap(i, j)
j = n
i = i + 1
while (i < j) {
p.swap(i, j)
i = i + 1
j = j - 1
}
perms.add(p.toList)
}
for (i in 0...perms.count) {
perms[i] = perms[i].map { |i| a[i] }.toList
}
return perms
}
// Generates all the permutations of the array 'a' in lexicographic order by index
// and yields them one by one.
static generateLex(a) {
Check.list("array", a, 1, 18)
var n = a.count - 1
var p = (0..n).toList
Fiber.yield(a.toList)
for (c in 1...count(n+1)) {
var i = n - 1
var j = n
while (p[i] > p[i+1]) i = i - 1
while (p[j] < p[i]) j = j - 1
p.swap(i, j)
j = n
i = i + 1
while (i < j) {
p.swap(i, j)
i = i + 1
j = j - 1
}
Fiber.yield(p.map { |i| a[i] }.toList)
}
}
}
/*
Comb is a class containing static methods to count or emit the combinations of a given array.
Combinations are emitted in lexographic order of the array indices. So, if the array is sorted
to begin with, the combinations will be in sorted order too.
*/
class Comb {
// Returns the number of combinations of an array with 'n' different elements taken 'k' at a time.
static count(n, k) {
Check.int("n", n, 1)
Check.int("k", k, 1, n.min(18))
if (n == 1) return 1
var fact = 1
for (i in n-k+1..n) fact = fact * i
if (fact > Num.maxSafeInteger) Fiber.abort("Too large to be safely calculated.")
return fact / Perm.count(k)
}
// Returns a list of all the combinations of the array 'a' taken 'k' elements at a time.
static list(a, k) {
Check.list("array", a, 1)
var n = a.count
Check.int("k", k, 1, n.min(18))
var c = List.filled(k, null)
var combs = []
var combine
combine = Fn.new { |start, end, index|
if (index == k) {
combs.add(c.toList)
return
}
var i = start
while (i <= end && end - i + 1 >= k - index) {
c[index] = a[i]
combine.call(i+1, end, index+1)
i = i + 1
}
}
combine.call(0, n-1, 0)
return combs
}
// Returns a list of all distinct combinations of the array 'a' taken 'k' elements at a time.
// Use only if not all elements are distinct and 'a' is in sorted order to start with.
static listDistinct(a, k) {
Check.list("array", a, 1)
var n = a.count
Check.int("k", k, 1, n.min(18))
var c = List.filled(k, null)
var combs = []
var combine
combine = Fn.new { |start, end, index|
if (index == k) {
combs.add(c.toList)
return
}
var i = start
while (i <= end && end - i + 1 >= k - index) {
c[index] = a[i]
combine.call(i+1, end, index+1)
while (i + 1 < n && a[i] == a[i+1]) i = i + 1
i = i + 1
}
}
combine.call(0, n-1, 0)
return combs
}
// Generates all the combinations of the array 'a' taken 'k' elements at a time and yields them one by one.
static generate(a, k) {
Check.list("array", a, 1)
var n = a.count
Check.int("k", k, 1, n.min(18))
var c = List.filled(k, null)
var combine
combine = Fn.new { |start, end, index|
if (index == k) {
Fiber.yield(c.toList)
return
}
var i = start
while (i <= end && end - i + 1 >= k - index) {
c[index] = a[i]
combine.call(i+1, end, index+1)
i = i + 1
}
}
combine.call(0, n-1, 0)
}
// Generates all distinct combinations of the array 'a' taken 'k' elements at a time and yields them one by one.
// Use only if not all elements are distinct and 'a' is in sorted order to start with.
static generateDistinct(a, k) {
Check.list("array", a, 1)
var n = a.count
Check.int("k", k, 1, n.min(18))
var c = List.filled(k, null)
var combine
combine = Fn.new { |start, end, index|
if (index == k) {
Fiber.yield(c.toList)
return
}
var i = start
while (i <= end && end - i + 1 >= k - index) {
c[index] = a[i]
combine.call(i+1, end, index+1)
while (i + 1 < n && a[i] == a[i+1]) i = i + 1
i = i + 1
}
}
combine.call(0, n-1, 0)
}
}
/*
Powerset is a class containing static methods to count or emit the elements of the powerset of a given array.
Elements are emitted in lexographic order of the array indices. So, if the array is sorted to begin with,
the powerset will be in sorted order too.
*/
class Powerset {
// Returns the number of elements of the powerset of an array with 'n' elements.
static count(n) {
Check.nonNegInt("n", n)
if (n == 0) return 1
var a = (0...n).toList
var total = 1
for (i in 1..n) {
total = total + Comb.count(n, i)
}
if (total > Num.maxSafeInteger) Fiber.abort("Too large to be safely calculated.")
return total
}
// Returns a list of all elements of the powerset of the array 'a'.
static list(a) {
Check.list("array", a)
var ps = [[]]
if (a.count == 0) return ps
for (i in 1..a.count) ps.addAll(Comb.list(a, i))
return ps
}
// Generates all elements of the powerset of the array 'a' and yields them one by one.
static generate(a) {
Check.list("array", a)
Fiber.yield([])
if (a.count == 0) return
for (i in 1..a.count) {
for (e in Comb.list(a, i)) Fiber.yield(e)
}
}
}