/**
     * Updates the state of the watcher, given reception of a new message.
     *
     * @param m
     *          The message that was received.
     */
    private void update(WatcherMessage m)
    {
        WatcherNode wn2;
        HashSet<WatcherNode> spawnedNodes;
        HashSet<WatcherNode> newNodes = new HashSet<WatcherNode>();
        int size = 0, atoms = 0;

        foreach (WatcherNode wn in m_nodes)
        {
            wn2 = new WatcherNode();
            wn2.setGamma(wn.getDelta());
            spawnedNodes = wn2.spawn(m);

            foreach (WatcherNode wn3 in spawnedNodes)
            {
                newNodes.Add(wn3);
            }
        }

        m_nodes = newNodes;

        // Updates statistics about maximum number of nodes
        size = m_nodes.Count;

        if (size > m_maxNodes)
        {
            m_maxNodes = size;
        }

        // Updates statistics about total number of formulae
        size = 0;
        atoms = 0;

        foreach (WatcherNode wn in m_nodes)
        {
            size += wn.getSize();
            atoms += wn.getAtoms();
        }

        if (size > m_maxFormulae)
        {
            m_maxFormulae = size;
        }

        if (atoms > m_maxAtoms)
        {
            m_maxAtoms = atoms;
        }
    }
    /**
     * Determines the value of an LTL-FO+ formula using a 3-valued logic. In
     * addition to TRUE and FALSE, we add a third value "?". The following rules
     * apply:
     * <ul>
     * <li>&not; ? = ?</li>
     * <li>TRUE &and; ? = ?</li>
     * <li>FALSE &and; ? = FALSE</li>
     * <li>TRUE &or; ? = TRUE</li>
     * <li>FALSE &or; ? = ?</li>
     * <li>X &phi; = G &phi; = ? no matter &phi;</li>
     * <li>F &phi; = &phi; &or; ?</li>
     * <li>&phi; U &psi; = &psi; &or; ?</li>
     * <li>&exist;<sub>p</sub> x: &phi; = &phi;(x<sub>1</sub>) &or; ... &or;
     * &phi;(x<sub>n</sub>) for x<sub>i</sub> in Dom<sub>s</sub>(p)</li>
     * <li>&forall;<sub>p</sub> x: &phi; = &phi;(x<sub>1</sub>) &and; ... &and;
     * &phi;(x<sub>n</sub>) for x<sub>i</sub> in Dom<sub>s</sub>(p)</li>
     *
     * @param o
     * @param m
     *          TODO
     * @return
     */
    protected static Outcome isSatisfiedInCurrentState(Operator o, WatcherMessage m)
    {
        // This method uses getClass to branch on the type of operator
        // used; TODO: redesign with method overriding
        Outcome oc1, oc2;
        Operator o1, o2, o3;

        if (o.GetType() == typeof(OperatorNot))
        {
            o1 = ((OperatorNot)o).getOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            return threeValuedNot(oc1);
        }

        else if (o.GetType() == typeof(OperatorX) ||
            o.GetType() == typeof(OperatorG))
        {
            // TODO
            // assert(false);
            return Outcome.INCONCLUSIVE;
        }

        else if (o.GetType() == typeof(OperatorEquals))
        {
          o1 = ((OperatorEquals)o).getLeftOperand();
          o2 = ((OperatorEquals)o).getRightOperand();
          if (o1.GetType () == typeof(ConstantPath))
          {
              // Left member is a path; we compare that path
              // to the right member
              HashSet<Atom> dom = m.getDomain((ConstantPath) o1);

              foreach (Atom a in dom)
              {
                  // We return true if at least one value at the end
                  // of the path equals o2
                  if (a.Equals(o2))
                      return Outcome.TRUE;
              }
              return Outcome.FALSE;
          }
          else
          {
              // We compare directly the two members
              if (o1.Equals(o2))
                  return Outcome.TRUE;
          }
          return Outcome.FALSE;
        }

        else if (o.GetType() == typeof(OperatorAnd))
        {
            o1 = ((OperatorAnd)o).getLeftOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            o2 = ((OperatorAnd)o).getRightOperand();
            oc2 = isSatisfiedInCurrentState(o2, m);

            return threeValuedAnd(oc1, oc2);
        }

        else if (o.GetType() == typeof(OperatorOr))
        {
            o1 = ((OperatorOr)o).getLeftOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            o2 = ((OperatorOr)o).getRightOperand();
            oc2 = isSatisfiedInCurrentState(o2, m);

            return threeValuedOr(oc1, oc2);
        }

        else if (o.GetType() == typeof(OperatorF))
        {
            o1 = ((OperatorF)o).getOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            return threeValuedOr(oc1, Outcome.INCONCLUSIVE);
        }

        else if (o.GetType() == typeof(OperatorImplies))
        {
            o1 = ((OperatorImplies)o).getLeftOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            o2 = ((OperatorImplies)o).getRightOperand();
            oc2 = isSatisfiedInCurrentState(o2, m);

            return threeValuedOr(threeValuedNot(oc1), oc2);
        }

        else if (o.GetType() == typeof(OperatorU))
        {
            o1 = ((OperatorU)o).getLeftOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            o2 = ((OperatorU)o).getRightOperand();
            oc2 = isSatisfiedInCurrentState(o2, m);

            return threeValuedOr(oc1, threeValuedAnd(oc1, Outcome.INCONCLUSIVE));
        }

        else if (o.GetType() == typeof(OperatorV))
        {
            o1 = ((OperatorV)o).getLeftOperand();
            oc1 = isSatisfiedInCurrentState(o1, m);

            o2 = ((OperatorV)o).getRightOperand();
            oc2 = isSatisfiedInCurrentState(o2, m);

            return threeValuedAnd(oc1, threeValuedOr(oc2, Outcome.INCONCLUSIVE));
        }

        else if (o.GetType() == typeof(FOExists))
        {
            // Iterate over domain
          		// TODO: supposes that the string qualifier is an atom
            Atom p = new Atom(((FOExists)o).getQualifier());
            Atom x = ((FOExists)o).getQuantifiedVariable();
            HashSet<Atom> s = m.getDomain(p);

            oc1 = Outcome.FALSE;

            foreach (Atom a in s)
            {
                o2 = ((FOExists)o).getOperand();
                o3 = o2.evaluate(x, a);
                oc2 = isSatisfiedInCurrentState(o3, m);
                oc1 = threeValuedOr(oc1, oc2);
            }

            return oc1;
        }

        else if (o.GetType() == typeof(FOForAll))
        {
            // Iterate over domain
          		// TODO: supposes that the string qualifier is an atom
            Atom p = new Atom(((FOForAll)o).getQualifier());
            Atom x = ((FOForAll)o).getQuantifiedVariable();
            HashSet<Atom> s = m.getDomain(p);

            oc1 = Outcome.TRUE;

            foreach (Atom a in s)
            {
                o2 = ((FOForAll)o).getOperand();
                o3 = o2.evaluate(x, a);
                oc2 = isSatisfiedInCurrentState(o3, m);
                oc1 = threeValuedAnd(oc1, oc2);
            }

            return oc1;
        }

        else if (o.GetType() == typeof(Constant))
        {
            // TODO: true and false are checked by comparing their
            // string representations; there should be a more graceful
            // way to check for true and false
            if (((Constant)o).getSymbol() == Operator.m_trueAtom.getSymbol())
            {
                return Outcome.TRUE;
            }

            else if (((Constant)o).getSymbol() == Operator.m_falseAtom.getSymbol())
            {
                return Outcome.FALSE;
            }

            return Outcome.INCONCLUSIVE;
        }

        return Outcome.INCONCLUSIVE;
    }
    /**
     * Updates the state of the watcher, given reception of a new message in a
     * stringified, XML-ised form.
     *
     * @param s
     *          A String containing the XML snippet corresponding to the message.
     *          For the format of XML supported by the method, see {@link
     *          WatcherMessage(String)}.
     */
    public override void update(string s)
    {
        WatcherMessage m = new WatcherMessage(s);

        // Prepends the default message name to the queried paths
        //m.setPrependPath("/" + m_messageName + "/");
        update(m);
    }
    /**
     * Decomposes a watcher's node, given a received message.
     *
     * @param m
     *          The message on which to make the decomposition
     * @return A Set of WatcherNodes consisting of the resulting decomposition
     *         for that node. If the left-hand side of the node is empty,
     *         returns an empty set.
     */
    public HashSet<WatcherNode> spawn(WatcherMessage m)
    {
        // This method uses getClass to branch on the type of operator
        // used; TODO: redesign with method overriding
        HashSet<WatcherNode> spawnedSet, outSet = new HashSet<WatcherNode>();
        WatcherNode wn;
        Operator o2, o3;

        // Picks a formula and removes it from Gamma
        foreach (Operator o in m_gamma)
        {
            m_gamma.Remove(o);

            // Optimization from original algorithm: if formula can readily
          		// be decided in current state, bypass decomposition
            Outcome oc = isSatisfiedInCurrentState(o, m);

            if (oc == Outcome.TRUE)
            {
                // Formula is true: stop decomposition of this formula and
                // continue spawning
                return spawn(m);
            }

            else if (oc == Outcome.FALSE)
            {
                // Formula is false: stop branch and return empty set
                return outSet;
            }

            // Operator OR
            else if (o.GetType() == typeof(OperatorOr))
            {
                // Do for left operand
                wn = new WatcherNode(this);
                o2 = ((OperatorOr)o).getLeftOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right operand
                wn = new WatcherNode(this);
                o2 = ((OperatorOr)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator IMPLIES
            else if (o.GetType() == typeof(OperatorImplies))
            {
                // Do for right operand
                wn = new WatcherNode(this);
                o2 = ((OperatorImplies)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right operand
                wn = new WatcherNode(this);
                o2 = ((OperatorImplies)o).getLeftOperand().getNegated();
                wn.addToGamma(o2.getNegatedNormalForm());
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator AND
            else if (o.GetType() == typeof(OperatorAnd))
            {
                // Do for left operand
                wn = new WatcherNode(this);
                o2 = ((OperatorAnd)o).getLeftOperand();
                wn.addToGamma(o2);

                // Do for left operand
                o2 = ((OperatorAnd)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator X
            else if (o.GetType() == typeof(OperatorX))
            {
                wn = new WatcherNode(this);
                o2 = ((OperatorX)o).getOperand();
                wn.addToDelta(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator G
            else if (o.GetType() == typeof(OperatorG))
            {
                // Do for left operand
                wn = new WatcherNode(this);
                o2 = ((OperatorG)o).getOperand();
                wn.addToGamma(o2);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator F
            else if (o.GetType() == typeof(OperatorF))
            {
                // Do for left node
                wn = new WatcherNode(this);
                o2 = ((OperatorF)o).getOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right node
                wn = new WatcherNode(this);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator U
            else if (o.GetType() == typeof(OperatorU))
            {
                // Do for left node
                wn = new WatcherNode(this);
                o2 = ((OperatorU)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right node
                wn = new WatcherNode(this);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator V
            else if (o.GetType() == typeof(OperatorV))
            {
                // Do for left node
                wn = new WatcherNode(this);
                o2 = ((OperatorV)o).getLeftOperand();
                wn.addToGamma(o2);
                o2 = ((OperatorV)o).getRightOperand();
                wn.addToGamma(o2);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                // Do for right node
                wn = new WatcherNode(this);
                o2 = ((OperatorV)o).getRightOperand();
                wn.addToGamma(o2);
                wn.addToDelta(o);
                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator [p=x]
            else if (o.GetType() == typeof(FOForAll))
            {
                // Iterate over domain
                wn = new WatcherNode(this);

                // TODO: supposes that the string qualifier is an atom
                Atom p = new Atom(((FOForAll)o).getQualifier());
                Atom x = ((FOForAll)o).getQuantifiedVariable();
                HashSet<Atom> s = m.getDomain(p);

                foreach (Atom v in s)
                {
                    o2 = ((FOForAll)o).getOperand();
                    o3 = o2.evaluate(x, v);
                    wn.addToGamma(o3);
                }

                spawnedSet = wn.spawn(m);

                foreach (WatcherNode wn2 in spawnedSet)
                {
                    outSet.Add(wn2);
                }

                return outSet;
            }

            // Operator <p=x>
            else if (o.GetType() == typeof(FOExists))
            {
                // Iterate over domain
                // TODO: supposes that the string qualifier is an atom
                Atom p = new Atom(((FOExists)o).getQualifier());
                Atom x = ((FOExists)o).getQuantifiedVariable();
                HashSet<Atom> s = m.getDomain(p);

                foreach (Atom v in s)
                {
                    wn = new WatcherNode(this);
                    o2 = ((FOExists)o).getOperand();
                    o3 = o2.evaluate(x, v);
                    wn.addToGamma(o3);
                    spawnedSet = wn.spawn(m);

                    foreach (WatcherNode wn2 in spawnedSet)
                    {
                        outSet.Add(wn2);
                    }
                }

                // TODO (Dominic): Validate this ?
                return outSet;
            }

            // Operator =
            else if (o.GetType() == typeof(OperatorEquals))
            {
                // This should never happen! When down to the evaluation
                // of an equality, all variables should be evaluated!
                //assert(false);
                return outSet;
            }

            // Operator NOT
            else if (o.GetType() == typeof(OperatorNot))
            {
                // Do for operand
                wn = new WatcherNode(this);
                o2 = ((OperatorNot)o).getOperand();

                if (o2.GetType() != typeof(Constant))
                {
                    // This should not happen! Negations should be pushed
          			// to atoms
                    //assert(false);
                    return outSet;
                }

                // TODO: true and false are checked by comparing their
                // string representations; there should be a more graceful
                // way
                // to check for true and false
                if (((Constant)o).getSymbol() == Operator.m_trueAtom.getSymbol())
                {
                    // Constant TRUE, i.e. evaluates to FALSE: this branch
          			// does not return anything
          			// i.e. do nothing
                }

                else if (((Constant)o).getSymbol() == Operator.m_falseAtom.getSymbol())
                {
                    // Constant FALSE, i.e. evaluates to TRUE: just pass on
          			// the recursive evaluation
                    spawnedSet = spawn(m);

                    foreach (WatcherNode wn2 in spawnedSet)
                    {
                        outSet.Add(wn2);
                    }

                    return outSet;
                }

                else
                {
                    // This should never happen! All atoms should evaluate
          			// down
          			// to either true or false.
                    System.Diagnostics.Debug.Assert (false, "Unrecognized operator in watcher node");
                    return outSet;
                }
            }

            // Constants (true or false)
            else if (o.GetType() == typeof(Constant))
            {
                // TODO: true and false are checked by comparing their
                // string representations; there should be a more graceful
                // way
                // to check for true and false
                if (((Constant)o).getSymbol() == Operator.m_trueAtom.getSymbol())
                {
                    // Constant TRUE: just pass on the recursive evaluation
                    spawnedSet = spawn(m);

                    foreach (WatcherNode wn2 in spawnedSet)
                    {
                        outSet.Add(wn2);
                    }
                }

                else if (((Constant)o).getSymbol() == Operator.m_falseAtom.getSymbol())
                {
                    // This branch is stopped: this branch does not return
          			// anything
          			// i.e. do nothing
                    return outSet;
                }

                else
                {
                    // This should never happen! All atoms should evaluate
          			// down to either true or false.
                    //assert(false);
                    return outSet;
                }
            }
        }

        if (m_gamma.Count == 0)
        {
            // Gamma is empty: return a set with myself
            outSet.Add(this);
        }

        return outSet;
    }