Skip to content

Commit 96271ff

Browse files
committed
v.0.9.3
* fix partial tensor missing data when in reversed order due to wrong index computation * take into account position of value in generating partial combinatorial tensor for strictly increasing or decreasing sequences (for example usefull in generating conditional combinations, still filtering is needed) * Filter.SORTED can accept also <,>,=<,>= etc operands symbolising ordering * update Readme, tests
1 parent a5d2d04 commit 96271ff

38 files changed

+909
-761
lines changed

README.md

+17-16
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, C
55
(php/python/java/c implementations in progress)
66

77

8-
**version 0.9.2** (~ 69kB minified, ~ 22kB zipped)
8+
**version 0.9.3** (~ 74kB minified, ~ 23kB zipped)
99

1010
![abacus combinatorial numbers](/abacus.jpg)
1111

@@ -75,9 +75,9 @@ A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, C
7575
* `LatinSquare` (`test/latin_squares.js`)
7676
* `MagicSquare` (`test/magic_squares.js`)
7777
* **algebraic composition** (of **fixed** dimensions at present) and **sequences** of combinatorial objects to construct new combinatorial objects (eg `all combinations` = `all permutations` **OF** `all unique combinations`, see `test/permutations_of_combinations.js` and `test/permutations_of_permutations.js`, `k-Derangements` = `(n,k) Combinations` **combined With** `(n-k) Derangements`, see `test/k-derangements.js` or `all subsets` = `(n,0)Combinations + (n,1)Combinations + .. + (n,n-1)Combinations + (n,n)Combinations`, see `test/combination_subsets.js`)
78-
* custom (user-supplied callable) and/or built-in **filters** which can select and generate any custom and complex combinatorial object from filtering other combinatorial objects as efficiently as possible (e.g see `test/filtered.js`, `test/filtered_partitions.js`). Also **algebraic / boolean composition of filters** (i.e `.NOT()`, `.AND()`, `.OR()` and so on..)
78+
* custom (user-supplied callable) and/or built-in **filters** which can select and generate any custom and complex combinatorial object from filtering other combinatorial objects as efficiently as possible (e.g see `test/filtered.js`, `test/filtered_partitions.js`). Also **algebraic / boolean composition of filters** (i.e `.NOT()`, `.AND()`, `.OR()` and so on..). **Note** that filtering should be **used with caution and only if no other method is currently possible** to generate the desired combinatorial object as **filtering is equivalent to exhaustive search** over the space of the original combinatorial object and as such can be an inefficient way to generate a combinatorial object (e.g see `test/filtered.js`). **Note2** with filtering applied some methods like `.total()`, `.hasNext()`, `.base()`, `.dimension()` still return data of the original object **not** the filtered object since that would require to pre-generate all the data and filter them afterwards instead of doing it one-by-one on each generation and would be impractical and unachievable for very large combinatorial objects, so be careful when using, for example, `.total()` with fitering applied
7979
* **multiple (combined) iterator orderings &amp; traversals**: `lex`, `colex`, `random`, `reversed`, `reflected`, `minimal` (not implemented yet). For example: `"revlex"` (equivalent to `"lex,reversed"`), `"refcolex"` (equivalent to `"colex,reflected"`), and so on..
80-
* **arbitrary range** of combinatorial objects in a number of supported orderings (ie `lex`, `colex`, `random`,..). **Note** `rank`/`unrank` have to be implemented for this feature to work
80+
* **arbitrary range** of combinatorial objects in a number of supported orderings (ie `lex`, `colex`, `random`,..) (and with filtering applied, if set). **Note** `rank`/`unrank` methods have to be implemented for this feature to work
8181
* **efficient and unbiased generation, (un)ranking, succession &amp; random methods** for supported combinatorial objects (see below)
8282
* `big-integer arithmetic`, `PRNG`s and other `math` utilities can be **dynamicaly pluggable using external implementations**, making the lib very flexible especialy with respect to handling big-integers &amp; (pseudo-)random number generators
8383

@@ -562,20 +562,21 @@ o.dispose()
562562

563563
### Todo
564564

565-
* apply built-in language `iterator`/`iterable` patterns (e.g ES6 `iterator` protocol, Python `__iter__` interface, PHP `Iterator` interface, ..). Combinatorial objects additionaly support a `doubly-linked list`-like interface, i.e `prev`/`next` accessors [DONE]
566-
* support `biginteger` combinatorial computations e.g large factorials [DONE, the lib **does not support** biginteger arithmetic, but arithmetic routines have been made **dynamicaly pluggable** and one can use an external implementation to support combinatorics with bigintegers where needed as needed, see test examples for an example]
567-
* support **multiple combined custom iterator orderings**, i.e `lex`, `colex`, `reversed`, `reflected`, `random` seamlessly and uniformly, both forward and backward [DONE, `random` ordering may be optimised further]
568-
* support **efficient successor methods** (preferably `CAT/Loopless` methods) to generate next/prev object from current object [DONE]
569-
* support **efficient ranking / unranking algorithms** and associated methods (preferably of `O(n)` or `O(nlgn)` complexity) for supported orderings [DONE]
570-
* support multiple combinatorial orderings (ie `lex`, `colex`, `reflex`, `refcolex`, `minimal`, ..) **directly in the successor methods** instead of using post-transformations on object [DONE]
571-
* support **unique and uniform random ordering traversals** for all combinatorial objects, so that the space of a combinatorial object can be traversed in **any random ordering uniquely and unbiasedly** (useful in some applications, eg backtracking) [DONE, see reference, used as custom iterator ordering, see above, may be optimised further]
572-
* make sure the `.random` methods **uniformly and unbiasedly sample the combinatorial object space** (methods use unbiased sampling algorithms, however results in certain cases might depend on [quality of PRNGs](http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf)) [DONE]
573-
* support algebraic composition/cascading of combinatorial objects (of **fixed** dimensions at present) to construct new combinatorial objects (eg `all combinations` = `all permutations` **OF** `all unique combinations`) [DONE]
574-
* support generation of supported combinatorial objects with additional **user-defined patterns/templates of constraints** to satisfy e.g *"only combinatorial objects matching `'(n)(m)(1){2}(){3}(0)((n+1))((n+m)){4}'`"* pattern.. [DONE]
575-
* add `LatinSquare`, `MagicSquare` algorithms [DONE]
565+
* apply built-in language `iterator`/`iterable` patterns (e.g ES6 `iterator` protocol, Python `__iter__` interface, PHP `Iterator` interface, ..). Combinatorial objects additionaly support a `doubly-linked list`-like interface, i.e `prev`/`next` accessors **[DONE]**
566+
* support `biginteger` combinatorial computations e.g large factorials **[DONE]**, the lib **does not support** biginteger arithmetic, but arithmetic routines have been made **dynamicaly pluggable** and one can use an external implementation to support combinatorics with bigintegers where needed as needed, see test examples for an example
567+
* support **multiple combined custom iterator orderings**, i.e `lex`, `colex`, `reversed`, `reflected`, `random` seamlessly and uniformly, both forward and backward **[DONE, `random` ordering may be optimised further]**
568+
* support **efficient successor methods** (preferably `CAT/Loopless` methods) to generate next/prev object from current object **[DONE]**
569+
* support **efficient ranking / unranking algorithms** and associated methods (preferably of `O(n)` or `O(nlgn)` complexity) for supported orderings **[DONE]**
570+
* support multiple combinatorial orderings (ie `lex`, `colex`, `reflex`, `refcolex`, `minimal`, ..) **directly in the successor methods** instead of using post-transformations on object **[DONE]**
571+
* support **unique and uniform random ordering traversals** for all combinatorial objects, so that the space of a combinatorial object can be traversed in **any random ordering uniquely and unbiasedly** (useful in some applications, eg backtracking) **[DONE, see reference, used as custom iterator ordering, see above, may be optimised further]**
572+
* make sure the `.random` methods **uniformly and unbiasedly sample the combinatorial object space** (methods use unbiased sampling algorithms, however results in certain cases might depend on [quality of PRNGs](http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf)) **[DONE]**
573+
* support algebraic composition/cascading of combinatorial objects (of **fixed** dimensions at present) to construct new combinatorial objects (eg `all combinations` = `all permutations` **OF** `all unique combinations`) **[DONE]**
574+
* support generation of supported combinatorial objects with additional **user-defined patterns/templates of constraints** to satisfy e.g *"only combinatorial objects matching `'(n)(m)(1){2}(){3}(0)((n+1))((n+m)){4}'`"* pattern.. **[DONE]**
575+
* add `LatinSquare`, `MagicSquare` algorithms **[DONE]**
576+
* add run-time/lazy custom and/or built-in filtering support (with support for filter composition as well) to generate and select custom and complex combinatorial objects from filtering other combinatorial objects as efficiently as possible **[DONE]**
576577
* add efficient `rank`/`unrank` methods for `DerangementPermutation`, `InvolutionPermutation`, `ConnectedPermutation`, `Composition` &amp; `Partition` (TODO)
577-
* full support for `colex` ordering `Composition` &amp; `Partition` [DONE PARTIALY]
578+
* full support for `colex` ordering `Composition` &amp; `Partition` **[DONE PARTIALY]**
578579
* support `minimal`/`gray` ordering (and successor) for all supported combinatorial objects (TODO)
579580
* use numeric arrays (ie `Uint32`) to store combinatorial items and/or make faster `successor` methods and other numerical routines to `asm.js` (TODO?)
580-
* support generation (and counting) of combinatorial objects (including the basic supported ones) based on **generic user-defined symbolic constraints / symmetries / rules** to satisfy, for example `permutations` defined symbolicaly and directly by their *symmetries / constraints* instead of being hardcoded as elementary objects (TODO?)
581+
* support generation (and counting) of combinatorial objects (including the basic supported ones) based on **generic user-defined symbolic constraints / symmetries / rules** to satisfy, for example `permutations` defined symbolicaly and directly by their *symmetries / constraints* instead of being hardcoded as elementary objects (TODO?, see using `filtering` as a similar alternative to this approach)
581582
* support *graph-based* combinatorial objects like `Graph`, `Grammar`,.. (TODO?) (for regular grammars and expressions see [RegexAnalyzer](https://github.com/foo123/RegexAnalyzer) for an example)

src/js/Abacus.js

+93-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
*
33
* Abacus
44
* A combinatorics library for Node.js / Browser / XPCOM Javascript, PHP, Python, Java, C/C++
5-
* @version: 0.9.2
5+
* @version: 0.9.3
66
* https://github.com/foo123/Abacus
77
**/
88
!function( root, name, factory ){
@@ -22,7 +22,7 @@ else if ( !(name in root) ) /* Browser/WebWorker/.. */
2222
/* module factory */ function ModuleFactory__Abacus( undef ){
2323
"use strict";
2424

25-
var Abacus = {VERSION: "0.9.2"}, stdMath = Math, PROTO = 'prototype', CLASS = 'constructor'
25+
var Abacus = {VERSION: "0.9.3"}, stdMath = Math, PROTO = 'prototype', CLASS = 'constructor'
2626
,slice = Array.prototype.slice, HAS = Object[PROTO].hasOwnProperty, toString = Object[PROTO].toString
2727
,log2 = stdMath.log2 || function(x) { return stdMath.log(x) / stdMath.LN2; }
2828
,trim_re = /^\s+|\s+$/g
@@ -1543,6 +1543,7 @@ function conditional_combinatorial_tensor( v, value_conditions, extra_conditions
15431543
function gen_combinatorial_data( n, data, pos, value_conditions, options )
15441544
{
15451545
options = options || {};
1546+
pos = pos || array(data.length||0, 0, 1);
15461547
// conditions: ALGEBRAIC(STRING EXPR) AND/OR BOOLEAN(POSITIVE / NEGATIVE) => [values] per position
15471548
var min = null==options.min ? 0 : options.min,
15481549
max = null==options.max ? n-1 : options.max,
@@ -1692,9 +1693,27 @@ function gen_combinatorial_data( n, data, pos, value_conditions, options )
16921693

16931694
// check additional conditions
16941695
additional_conditions = is_callable(options.extra_conditions) ? function(v,i0,i1){
1695-
return (min<=v[i0] && v[i0]<=max) && options.extra_conditions(v,i0,i1);
1696+
var v0 = v[i0];
1697+
if (
1698+
// check in range
1699+
(min>v0 || v0>max) ||
1700+
// when strictly increasing sequence then value at pos i cannot be less than i since it has to accomodate the rest values as well before it, complementary for strictly decreasing sequence (for strictly decreasing sequence we do not know the number of elements that come after unlike for strictly increasing sequence where we can know, but as a workaround we can add last possible position in conditions with all possible values simply as a hint/clue on what is last possible position)
1701+
// (assume values in range 0..n-1 for positions 0..n-1 or reverse)
1702+
(V_INC === value_conditions && pos[i0]>v0) ||
1703+
(V_DEC === value_conditions && pos[pos.length-1]-pos[i0]>v0)
1704+
) return false
1705+
return options.extra_conditions(v,i0,i1);
16961706
} : function(v,i0,i1){
1697-
return (min<=v[i0] && v[i0]<=max);
1707+
var v0 = v[i0];
1708+
if (
1709+
// check in range
1710+
(min>v0 || v0>max) ||
1711+
// when strictly increasing sequence then value at pos i cannot be less than i since it has to accomodate the rest values as well before it, complementary for strictly decreasing sequence (for strictly decreasing sequence we do not know the number of elements that come after unlike for strictly increasing sequence where we can know, but as a workaround we can add last possible position in conditions with all possible values simply as a hint/clue on what is last possible position)
1712+
// (assume values in range 0..n-1 for positions 0..n-1 or reverse)
1713+
(V_INC === value_conditions && pos[i0]>v0) ||
1714+
(V_DEC === value_conditions && pos[pos.length-1]-pos[i0]>v0)
1715+
) return false
1716+
return true;
16981717
};
16991718

17001719
// compute valid combinatorial data satisfying conditions
@@ -2337,6 +2356,33 @@ function is_latin( square )
23372356
}
23382357
return M;
23392358
}*/
2359+
function find( a, b, nested )
2360+
{
2361+
if ( nested )
2362+
{
2363+
if ( !a || !a.length ) return -1;
2364+
var index, found, i, j, k, n = a.length, m = b.length;
2365+
for(i=0; i<n; i++)
2366+
{
2367+
k = a[i];
2368+
found = true;
2369+
for(j=0; j<m; j++)
2370+
{
2371+
if ( b[j] !== k[j] )
2372+
{
2373+
found = false;
2374+
break;
2375+
}
2376+
}
2377+
if ( found ) return i;
2378+
}
2379+
return -1;
2380+
}
2381+
else
2382+
{
2383+
return a && a.length ? a.indexOf(b) : -1;
2384+
}
2385+
}
23402386

23412387
// Abacus.Filter, Filter class used to define and combine filters to filter combinatorial object by them
23422388
Filter = Abacus.Filter = Class({
@@ -2360,8 +2406,32 @@ Filter = Abacus.Filter = Class({
23602406
});
23612407
}
23622408
,SORTED: function( dir, strict ) {
2363-
dir = -1 === dir ? -1 : 1;
23642409
if ( 2 > arguments.length || null == strict ) strict = true;
2410+
if ( is_string(dir) )
2411+
{
2412+
if ( "<" === dir )
2413+
{
2414+
dir = 1;
2415+
strict = true;
2416+
}
2417+
else if ( ">" === dir )
2418+
{
2419+
dir = -1;
2420+
strict = true;
2421+
}
2422+
else if ( "<=" === dir || "=<" === dir )
2423+
{
2424+
dir = 1;
2425+
strict = false;
2426+
}
2427+
else if ( ">=" === dir || "=>" === dir )
2428+
{
2429+
dir = -1;
2430+
strict = false;
2431+
}
2432+
}
2433+
dir = +dir;
2434+
dir = -1 === dir ? -1 : 1;
23652435
return Filter(-1 === dir ? function(item){
23662436
for(var item0=item[0],i=1,n=item.length; i<n; i++)
23672437
{
@@ -2408,9 +2478,9 @@ Filter = Abacus.Filter = Class({
24082478
}
24092479
,VAL: function( pos, val, comp ) {
24102480
comp = comp || "==";
2411-
val = +val;
2481+
//val = +val;
24122482
pos = +pos;
2413-
if ( ">=" === comp )
2483+
if ( ">=" === comp || "=>" === comp )
24142484
{
24152485
return Filter(function(item){ return 0<=pos && pos<item.length && item[pos]>=val; });
24162486
}
@@ -2422,7 +2492,7 @@ Filter = Abacus.Filter = Class({
24222492
{
24232493
return Filter(function(item){ return 0<=pos && pos<item.length && item[pos]<val; });
24242494
}
2425-
else if ( "<=" === comp )
2495+
else if ( "<=" === comp || "=<" === comp )
24262496
{
24272497
return Filter(function(item){ return 0<=pos && pos<item.length && item[pos]<=val; });
24282498
}
@@ -2438,7 +2508,7 @@ Filter = Abacus.Filter = Class({
24382508
,MAX: function( val, comp ) {
24392509
comp = comp || "==";
24402510
val = +val;
2441-
if ( ">=" === comp )
2511+
if ( ">=" === comp || "=>" === comp )
24422512
{
24432513
return Filter(function(item){ return operate(function(M,i){
24442514
if ( item[i] > M ) M = item[i];
@@ -2459,7 +2529,7 @@ Filter = Abacus.Filter = Class({
24592529
return M;
24602530
}, -Infinity, null, 0, item.length-1, 1) < val; });
24612531
}
2462-
else if ( "<=" === comp )
2532+
else if ( "<=" === comp || "=<" === comp )
24632533
{
24642534
return Filter(function(item){ return operate(function(M,i){
24652535
if ( item[i] > M ) M = item[i];
@@ -2484,7 +2554,7 @@ Filter = Abacus.Filter = Class({
24842554
,MIN: function( val, comp ) {
24852555
comp = comp || "==";
24862556
val = +val;
2487-
if ( ">=" === comp )
2557+
if ( ">=" === comp || "=>" === comp )
24882558
{
24892559
return Filter(function(item){ return operate(function(M,i){
24902560
if ( item[i] < M ) M = item[i];
@@ -2505,7 +2575,7 @@ Filter = Abacus.Filter = Class({
25052575
return M;
25062576
}, Infinity, null, 0, item.length-1, 1) < val; });
25072577
}
2508-
else if ( "<=" === comp )
2578+
else if ( "<=" === comp || "=<" === comp )
25092579
{
25102580
return Filter(function(item){ return operate(function(M,i){
25112581
if ( item[i] < M ) M = item[i];
@@ -3600,7 +3670,6 @@ CombinatorialIterator = Abacus.CombinatorialIterator = Class({
36003670
else self._next = has_next;
36013671
}
36023672
}while($.filter && current && !$.filter.apply(current, self)); // if custom filter, reject if invalid, try next
3603-
36043673
return current;
36053674
}
36063675

@@ -3830,14 +3899,20 @@ Tensor = Abacus.Tensor = Class(CombinatorialIterator, {
38303899
,succ: function( item, index, n, $, dir, TI ) {
38313900
if ( !n || (null == item) ) return null;
38323901
var type = $ && $.type ? $.type : "tensor",
3833-
order = $ && null!=$.order ? $.order : LEX;
3902+
order = $ && null!=$.order ? $.order : LEX,
3903+
Arithmetic = Abacus.Arithmetic, ind;
38343904
dir = -1 === dir ? -1 : 1;
38353905
if ( "partial" === type )
38363906
{
38373907
if ( !$.data || !$.data.length ) return null;
3838-
if ( REVERSED & order ) dir = -dir;
3839-
var i = null == index ? $.data.indexOf(item) : Abacus.Arithmetic.val(index);
3840-
return 0>dir ? (0<=i-1 ? $.data[i-1] : null) : (0<=i && i+1<$.data.length ? $.data[i+1] : null);
3908+
if ( REVERSED & order )
3909+
{
3910+
dir = -dir;
3911+
if ( null != index ) index = Arithmetic.sub(Arithmetic.N($.data.length-1),index);
3912+
}
3913+
if ( null == index ) index = find($.data, item, true);
3914+
ind = Arithmetic.val(index);
3915+
return 0>dir ? (0<=ind-1 ? $.data[ind-1] : null) : (0<=ind && ind+1<$.data.length ? $.data[ind+1] : null);
38413916
}
38423917
return !n[0] || (0 >= n[0]) ? null : next_tensor(item, n, dir, type, order, TI);
38433918
}
@@ -3876,7 +3951,7 @@ Tensor = Abacus.Tensor = Class(CombinatorialIterator, {
38763951

38773952
if ( "partial" === type )
38783953
{
3879-
index = Arithmetic.N($.data&&$.data.length ? $.data.indexOf(item) : -1);
3954+
index = Arithmetic.N(find($.data, item, true));
38803955
}
38813956
else
38823957
{

0 commit comments

Comments
 (0)