Implementing an ADT Example: Sum-Constraint ADT Part II Ohad Barzilay May 2004
Ohad Barzilay, April Cell implementation /** * A cell contains a value, and may be connected to * several constraint ports. * connections != null, "Non-null connections" */ public class Cell { protected Object _value; protected ListSet connections;
Ohad Barzilay, April Cell implementation /** * Creates a new Cell with no value. * value() == null, "Null value" */ public Cell() { connections = new ListSet(); _value = null; } /** * Creates a new Cell with a given value. * value() == new_val, "Value stored" */ public Cell(Object new_val) { this(); _value = new_val; }
Ohad Barzilay, April value() /** * Returns the value stored in this Cell. */ public Object value() { return _value; } Don’t access fields directly No postcondition
Ohad Barzilay, April add_connection /** * Adds a connection (a 'constraint - port' * pair) to the Cell. * c != null, "Non-null constraint" c.port_valid(port), "Valid port" */ public void add_connection(Constraint c, int port) { connections.extend(new Pair(c, new Integer(port))); }
Ohad Barzilay, April set_value /** * Sets the value stored in the Cell to the given one. * Notifier is the user program. * new_val != null, "Non-null new value" value() == new_val, "Value stored" */ public void set_value(Object new_val) { set_value(new_val, null, 0); }
Ohad Barzilay, April set_value /** * Sets the value stored in the Cell to the given one. * If null, notifier is the user program. * new_val != null, "Non-null new value" notifier == null || notifier.port_valid(port), * "Valid port" notifier == null || value() == null, * "Constraint cannot override existing value" * value() == new_val, "Value stored" */ public void set_value(Object new_val, Constraint notifier, int port)
Ohad Barzilay, April set_value public void set_value(Object new_val, Constraint notifier, int port) { Set agenda = null; if (_value != null) { agenda = new ListSet(); forget(agenda); } _value = new_val;...
Ohad Barzilay, April set_value... Linear iter = connections.iterator(); for (iter.start(); !iter.after(); iter.forth()) { Pair connection = (Pair)iter.item(); Constraint c = (Constraint)connection.car(); int p = ((Integer)connection.cdr()).intValue(); if (!(c == notifier && p == port)) c.new_port_value(p); } if (agenda != null) { iter = agenda.iterator(); for (iter.start(); !iter.after(); iter.forth()) ((Constraint)iter.item()).deduce(); }
Ohad Barzilay, April forget /** * Begins a value retraction chain from this Cell. * When finished, the value of this Cell, and every value * in the network depending on it will be retracted. Then * it will be deduced if possible. */ public void forget() { Set agenda = new ListSet(); forget(agenda); Linear iter = agenda.iterator(); for (iter.start(); !iter.after(); iter.forth()) ((Constraint)iter.item()).deduce(); }
Ohad Barzilay, April forget /** * A link within a retracting chain. * When finished, the value of this Cell will be retracted. * agenda != null, "Non-null agenda" value() == null */ protected void forget(Set agenda) { if (_value != null) { _value = null; Linear iter = connections.iterator(); for (iter.start(); !iter.after(); iter.forth()) { Pair connection = (Pair)iter.item(); Constraint c = (Constraint)connection.car(); int p = ((Integer)connection.cdr()).intValue(); c.forget(p, agenda); }
Ohad Barzilay, April toString public String toString() { StringBuffer result = new StringBuffer("<Cell: "); result.append(value() == null ? "---" : value().toString()); result.append(">"); return result.toString(); }
The Solution Implementing SumConstraint
Ohad Barzilay, April SumConstraint public class SumConstraint extends Constraint { public static final int ADDEND = 0; public static final int AUGEND = 1; public static final int SUM = 2; public static final int NUM_OF_PORTS = 3; protected Cell[] ports; protected boolean[] _deduced;
Ohad Barzilay, April SumConstraint public SumConstraint(Cell addend, Cell augend, Cell sum) { ports = new Cell[NUM_OF_PORTS]; ports[ADDEND] = addend; ports[AUGEND] = augend; ports[SUM] = sum; addend.add_connection(this, ADDEND); augend.add_connection(this, AUGEND); sum.add_connection(this, SUM); _deduced = new boolean[NUM_OF_PORTS]; for (int i = 0; i < NUM_OF_PORTS; i++) _deduced[i] = false; deduce(); }
Ohad Barzilay, April SumConstraint - contract /** * Creates a new SumConstraint which is connected to the * given Cells. * The Cells are fixed during the life of the constraint. * addend != null, "Non-null addend" augend != null, "Non-null augend" sum != null, "Non-null sum" ($prev(addend.value()) == null) || defined(ADDEND), * "Addend defined if cell had value" ($prev(augend.value()) == null) || defined(AUGEND), * "Augend defined if cell had value" ($prev(sum.value()) == null) || defined(SUM), * "Sum defined if cell had value" */ public SumConstraint(Cell addend, Cell augend, Cell sum)
Ohad Barzilay, April port_valid /** * Returns true if the given port is one of * ADDEND, AUGEND or SUM. */ public boolean port_valid(int port) { return 0 <= port && port < NUM_OF_PORTS; }
Ohad Barzilay, April get_port /** * Returns the value stored in the given port. * port_valid(port), "Valid port" * known(port), "Port value is known" // * possible, but better: !known(port) == ($ret == null), * "Unknown iff null" */ public Object get_port(int port) { return ports[port].value(); }
Ohad Barzilay, April defined /** * returns true iff the given port is defined. * port_valid(port), "Valid port" * $ret == (!deduced(port) && get_port(port) != null), * "Defined iff has value but not deduced" */ public boolean defined(int port) { return !_deduced[port] && ports[port].value() != null; }
Ohad Barzilay, April deduced /** * Is the port deduced? * port_valid(port), "Valid port" * $ret == (!defined(port) && get_port(port) != null), * "Deduced iff has value but not defined" !$ret || (((Integer)get_port(SUM)).intValue() == * ((Integer)get_port(ADDEND)).intValue() + *((Integer)get_port(AUGEND)).intValue()), * "Deduction obeys constraint" */ public boolean deduced(int port) { return _deduced[port]; }
Ohad Barzilay, April known /** * Returns true if this port is either defined or deduced. * port_valid(port), "Valid port" * $ret == (get_port(port) != null), * "Known iff port is non-null" */ public boolean known(int port) { return ports[port].value() != null; }
Ohad Barzilay, April contradiction /** * Returns true if all the ports are defined, and the sum of the addend * and the augend port's values isn't equal to the value of the sum port. * $ret == (defined(ADDEND) && defined(AUGEND) && defined(SUM) * && (((Integer)get_port(ADDEND)).intValue() + * ((Integer)get_port(AUGEND)).intValue() != * ((Integer)get_port(SUM)).intValue())), * "Contradiction iff constraint not obeyed" */ public boolean contradiction() { return defined(ADDEND) && defined(AUGEND) && defined(SUM) && (((Integer)get_port(ADDEND)).intValue() + ((Integer)get_port(AUGEND)).intValue() != ((Integer)get_port(SUM)).intValue()); }
Ohad Barzilay, April new_port_value /** * Inform constraint of new value in port. To be called by * connected cell after changing the cell's own value. * port_valid(port), "Valid port" * defined(port), "Port defined" !deduced(port), "Port not deduced" */ protected void new_port_value(int port) { deduced[port] = false; deduce(); }
Ohad Barzilay, April forget /** * Retracts the value from the given port recursively. To be * called by connected cell after it forgot its own value. If the * given port is not defined and/or is deduced nothing happens. * The constraint adds itself to the agenda in order to check * deductions after the process is finished. * port_valid(port), "Valid port" agenda != null, "Non-null agenda" * !defined(port), "Port undefined" */ protected void forget(int port, Set agenda) { agenda.extend(this); retract(agenda); }
Ohad Barzilay, April retract /** Retract previous deduction if no longer valid. */ protected void retract(Set agenda) { if (_deduced[ADDEND] && !(defined(AUGEND) && defined(SUM))) { _deduced[ADDEND] = false; ports[ADDEND].forget(agenda); } else if (_deduced[AUGEND] && !(defined(ADDEND) && defined(SUM))) { _deduced[AUGEND] = false; ports[AUGEND].forget(agenda); } else if (_deduced[SUM] && !(defined(ADDEND) && defined(AUGEND))) { _deduced[SUM] = false; ports[SUM].forget(agenda); }
Ohad Barzilay, April deduce /** Attempt to deduce new information for ports in this constraint. */ protected void deduce() { if (defined(ADDEND) && defined(AUGEND) && !known(SUM)) { _deduced[SUM] = true; ports[SUM].set_value( new Integer(((Integer)get_port(ADDEND)).intValue() + ((Integer)get_port(AUGEND)).intValue()), this, SUM); }...
Ohad Barzilay, April deduce... if (defined(ADDEND) && !known(AUGEND) && defined(SUM)) { _deduced[AUGEND] = true; ports[AUGEND].set_value (new Integer(((Integer)get_port(SUM)).intValue() - ((Integer)get_port(ADDEND)).intValue()), this, AUGEND); } if (!known(ADDEND) && defined(AUGEND) && defined(SUM)) { _deduced[ADDEND] = true; ports[ADDEND].set_value (new Integer(((Integer)get_port(SUM)).intValue() - ((Integer)get_port(AUGEND)).intValue()), this, ADDEND); }
Ohad Barzilay, April port_string /** port_valid(port), "Valid port" */ protected String port_string(int port) { if (defined(port)) return "defined(" + get_port(port).toString() + ")"; else if (deduced(port)) return "deduced(" + get_port(port).toString() + ")"; else return "---"; }
Ohad Barzilay, April toString public String toString() { StringBuffer result = new StringBuffer("<Sum: "); result.append(port_string(ADDEND)); result.append(" + "); result.append(port_string(AUGEND)); result.append(" = "); result.append(port_string(SUM)); if (contradiction()) result.append(" ***"); result.append(">"); return result.toString(); }
Questions ?