Category talk:Wren-perm

From Rosetta Code
Revision as of 09:45, 6 April 2022 by PureFox (talk | contribs) (Added source code for new 'Wren-perm' module.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Source code

<lang ecmascript>/* Module "perm.wren" */

import "./check" for Check

/*

   Perm is a class containing static methods to count or emit the permutations of a given array.
   Permutations are emitted in lexographic order of the array indices. So, if the array is sorted
   to begin with, the 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
   }
   // Returns a list of all the permutations of the array 'a'.
   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'.
   // Use only if not all elements are distinct.
   static listDistinct(a) {
       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' 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' and yields them one by one.
   // Use only if not all elements are distinct.
   static generateDistinct(a) {
       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)
   }

}

/*

   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)
       }
   }

}</lang>