a14ea82002-04-05Leif Stensson // An abstract data type for binary relations.
1cad622003-08-24Martin Nilsson #pike __REAL_VERSION__
a14ea82002-04-05Leif Stensson private mapping val = ([]); private mixed id; private int items = 0; private int need_recount = 0; constant is_binary_relation = 1;
3524712015-05-26Martin Nilsson //! Return true/false: does the relation "@[left] R @[right]" exist?
a14ea82002-04-05Leif Stensson mixed contains(mixed left, mixed right) { return val[left] && val[left][right]; } //! Does the same as the @[contains] function: returns true if the //! relation "@[left] R @[right]" exists, and otherwise false. mixed `()(mixed left, mixed right) { return contains(left, right); } //! Adds "@[left] R @[right]" as a member of the relation. Returns //! the same relation.
563bd72004-01-11Martin Nilsson this_program add(mixed left, mixed right)
a14ea82002-04-05Leif Stensson { if (!val[left]) val[left] = (<>); if (!val[left][right]) ++items, val[left][right] = 1;
563bd72004-01-11Martin Nilsson  return this;
a14ea82002-04-05Leif Stensson } //! Removes "@[left] R @[right]" as a member of the relation. Returns //! the same relation.
563bd72004-01-11Martin Nilsson this_program remove(mixed left, mixed right)
a14ea82002-04-05Leif Stensson { if (val[left] && val[left][right]) --items, val[left][right] = 0;
179cc72005-01-06Martin Nilsson  if (!sizeof(val[left])) m_delete(val, left);
563bd72004-01-11Martin Nilsson  return this;
a14ea82002-04-05Leif Stensson } //! Maps every entry in the relation. The function f gets two //! arguments: the left and the right relation value. Returns //! an array with the return values of f for each and every //! mapped entry. //! //! Note: since the entries in the relation are not ordered, //! the returned array will have its elements in no particular //! order. If you need to know which relation entry produced which //! result in the array, you have to make that information part
3524712015-05-26Martin Nilsson //! of the value that @[f] returns.
a14ea82002-04-05Leif Stensson array map(function f) { array a = ({}); foreach(indices(val), mixed left) foreach(indices(val[left]), mixed right) a += ({ f(left, right) }); return a; } //! Filters the entries in the relation, and returns a relation with //! all those entries for which the filtering function @[f] returned //! true. The function @[f] gets two arguments: the left and the right //! value for every entry in the relation. object filter(function f) { ADT.Relation.Binary res = ADT.Relation.Binary(id); foreach(indices(val), mixed left) foreach(indices(val[left]), mixed right) if (f(left, right)) res->add(left, right); return res; } //! Filters the entries in the relation destructively, removing all //! entries for which the filtering function @[f] returns false. //! The function @[f] gets two arguments: the left and the right value //! for each entry in the relation.
563bd72004-01-11Martin Nilsson this_program filter_destructively(function f)
a14ea82002-04-05Leif Stensson { foreach(indices(val), mixed left) { foreach(indices(val[left]), mixed right) if (!f(left, right)) remove(left, right); if (sizeof(val[left]) == 0) val[left] = 0; }
563bd72004-01-11Martin Nilsson  return this;
a14ea82002-04-05Leif Stensson } //! Returns the number of relation entries in the relation. (Or with //! other words: the number of relations in the relation set.) mixed _sizeof() { if (need_recount) { int i = 0; need_recount = 0; foreach(indices(val), mixed left) i += sizeof(val[left]); items = i; } return items; }
563bd72004-01-11Martin Nilsson int(0..1) `==(mixed rel)
a14ea82002-04-05Leif Stensson { if (!objectp(rel) || !rel->is_binary_relation) return 0; // different because of having different types
563bd72004-01-11Martin Nilsson  return this <= rel && rel <= this;
a14ea82002-04-05Leif Stensson }
563bd72004-01-11Martin Nilsson int(0..1) `>=(object rel)
a14ea82002-04-05Leif Stensson {
563bd72004-01-11Martin Nilsson  return rel <= this;
a14ea82002-04-05Leif Stensson }
563bd72004-01-11Martin Nilsson int(0..1) `!=(mixed rel)
a14ea82002-04-05Leif Stensson {
563bd72004-01-11Martin Nilsson  return !(this == rel);
a14ea82002-04-05Leif Stensson } //! The expression `rel1 & rel2' returns a new relation which has //! those and only those relation entries that are present in both //! rel1 and rel2. mixed `&(mixed rel) { return filter(lambda (mixed left, mixed right) { return rel->contains(left, right);}); }
92e7da2003-03-31Martin Nilsson //! @decl mixed `+(mixed rel) //! @decl mixed `|(mixed rel)
3e0ae02003-01-19Martin Nilsson //! The expression `rel1 | rel2' and `rel1 + rel2' returns a new //! relation which has all the relation entries present in rel1, //! or rel2, or both.
6c33b82003-04-02Martin Nilsson 
a14ea82002-04-05Leif Stensson mixed `|(mixed rel) { ADT.Relation.Binary res = ADT.Relation.Binary(id, rel); foreach(indices(val), mixed left) foreach(indices(val[left]), mixed right) res->add(left, right); return res; }
6c33b82003-04-02Martin Nilsson 
a14ea82002-04-05Leif Stensson mixed `+ = `|; //! The expression `rel1 - rel2' returns a new relation which has //! those and only those relation entries that are present in rel1 //! and not present in rel2. mixed `-(mixed rel) { return filter(lambda (mixed left, mixed right) { return !rel->contains(left, right);}); }
3f8a6e2002-09-12Leif Stensson //! Makes the relation symmetric, i.e. makes sure that if xRy is part //! of the relation set, then yRx should also be a part of the relation //! set.
563bd72004-01-11Martin Nilsson this_program make_symmetric()
3f8a6e2002-09-12Leif Stensson { foreach(indices(val), mixed left) foreach(indices(val[left]), mixed right) add(right, left);
563bd72004-01-11Martin Nilsson  return this;
3f8a6e2002-09-12Leif Stensson } //! Assuming the relation's domain and range sets are equal, and that //! the relation xRy means "there is a path from node x to node y", //! @[find_shortest_path] attempts to find a path with a minimum number //! of steps from one given node to another. The path is returned as an //! array of nodes (including the starting and ending node), or 0 if no //! path was found. If several equally short paths exist, one of them //! will be chosen pseudorandomly. //! //! Trying to find a path from a node to itself will always succeed, //! returning an array of one element: the node itself. (Or in other //! words, a path with no steps, only a starting/ending point). //! //! The argument @[avoiding] is either 0 (or omitted), or a multiset of //! nodes that must not be part of the path. array find_shortest_path(mixed from, mixed to, void|multiset avoiding) { if (from == to) return ({ from }); if (!val[from]) return 0; if (avoiding && avoiding[to]) return 0; if (contains(from, to)) return ({ from, to }); // NOTE: This is a simple, more or less depth-first search. Worst- // case time complexity could probably be improved, e.g. by a // breadth-first search (such as Dijkstra's path-finding algorithm). // But those algorithms typically have worse space complexity, so // this will do for now. array subpath, found = 0; avoiding = avoiding ? avoiding + (< from >) : (< from >); foreach(indices(val[from]), mixed right) if (!avoiding[right]) if (subpath = find_shortest_path(right, to, avoiding)) if (!found || sizeof(subpath)+1 < sizeof(found)) { found = ({ from, @subpath }); if (sizeof(subpath) == 1) { // We can't find a shorter path than this, so there's no // point in looking for more alternatives. break; } } return found; }
a14ea82002-04-05Leif Stensson string _sprintf(int mode) {
179cc72005-01-06Martin Nilsson  return mode=='O' && sprintf("%O(%O %O)", this_program, id, _sizeof());
a14ea82002-04-05Leif Stensson } //! Return the ID value which was given as first argument to create(). mixed get_id() { return id; } void create(void|mixed _id, void|mapping|object _initial) { id = _id; if (objectp(_initial) && _initial->is_binary_relation)
007b782004-09-25Reinhard Pfau  _initial->map(lambda (mixed left, mixed right) { add(left,right); });
a14ea82002-04-05Leif Stensson  else if (mappingp(_initial)) foreach(indices(_initial), mixed key) add(key, _initial[key]); else if (_initial) error("Bad initializer for ADT.Relation.Binary.\n"); }
c5b10d2002-06-14Martin Nilsson  //! An iterator which makes all the left/right entities in the relation //! available as index/value pairs.
9eaf1d2008-06-28Martin Nilsson protected class _get_iterator {
c5b10d2002-06-14Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected int(0..) ipos; protected int(0..) vpos; protected int(0..1) finished = 1;
c5b10d2002-06-14Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected array lefts; protected array rights;
c5b10d2002-06-14Martin Nilsson  void create() { first(); } mixed index() {
65f8672004-05-01Martin Stjernholm  return finished ? UNDEFINED : lefts[ipos];
c5b10d2002-06-14Martin Nilsson  } mixed value() {
65f8672004-05-01Martin Stjernholm  return finished ? UNDEFINED : rights[vpos];
c5b10d2002-06-14Martin Nilsson  } int(0..1) `!() { return finished; } int(0..1) next() { if(finished || (ipos==sizeof(lefts)-1 && vpos==sizeof(rights)-1)) { finished = 1; return 0; } vpos++; if(vpos>sizeof(rights)-1 && !finished) { ipos++; rights = indices(val[lefts[ipos]]); vpos = 0; } return 1; } this_program `+=(int steps) {
65f8672004-05-01Martin Stjernholm  if (steps < 0) error ("Cannot step backwards.\n");
c5b10d2002-06-14Martin Nilsson  while(steps--) next();
563bd72004-01-11Martin Nilsson  return this;
c5b10d2002-06-14Martin Nilsson  } int(0..1) first() { lefts = indices(val); if(sizeof(lefts)) { rights = indices(val[lefts[0]]); finished = 0; } else finished = 1; return !finished; } }
007b782004-09-25Reinhard Pfau 
7091062014-08-18Martin Nilsson mixed cast(string to) { if( to=="mapping" )
007b782004-09-25Reinhard Pfau  return copy_value(val);
7091062014-08-18Martin Nilsson  return UNDEFINED;
007b782004-09-25Reinhard Pfau }