/**
   * @return Returns a string representation of the classification structure. Of the form P(a|b,c,d)
   */
  public String getClassificationStructure() {
    StringBuilder result = new StringBuilder();

    result.append("P(");
    boolean first = true;

    for (int i = 0; i < this.getEvents().size(); i++) {
      BayesianEvent event = this.events.get(i);
      EventState state = this.query.getEventState(event);
      if (state.getEventType() == EventType.Outcome) {
        if (!first) {
          result.append(",");
        }
        result.append(event.getLabel());
        first = false;
      }
    }

    result.append("|");

    first = true;
    for (int i = 0; i < this.getEvents().size(); i++) {
      BayesianEvent event = this.events.get(i);
      if (this.query.getEventState(event).getEventType() == EventType.Evidence) {
        if (!first) {
          result.append(",");
        }
        result.append(event.getLabel());
        first = false;
      }
    }

    result.append(")");
    return result.toString();
  }
  /**
   * Create, or register, the specified event with this bayesian network.
   *
   * @param event The event to add.
   */
  public void createEvent(BayesianEvent event) {
    if (eventExists(event.getLabel())) {
      throw new BayesianError("The label \"" + event.getLabel() + "\" has already been defined.");
    }

    this.eventMap.put(event.getLabel(), event);
    this.events.add(event);
  }
 /**
  * Remove the specified event.
  *
  * @param event The event to remove.
  */
 private void removeEvent(BayesianEvent event) {
   for (BayesianEvent e : event.getParents()) {
     e.getChildren().remove(event);
   }
   this.eventMap.remove(event.getLabel());
   this.events.remove(event);
 }
  /** {@inheritDoc} */
  @Override
  public final void save(final OutputStream os, final Object obj) {
    final EncogWriteHelper out = new EncogWriteHelper(os);
    final BayesianNetwork b = (BayesianNetwork) obj;
    out.addSection("BAYES-NETWORK");
    out.addSubSection("BAYES-PARAM");
    String queryType = "";
    String queryStr = b.getClassificationStructure();

    if (b.getQuery() != null) {
      queryType = b.getQuery().getClass().getSimpleName();
    }

    out.writeProperty("queryType", queryType);
    out.writeProperty("query", queryStr);
    out.writeProperty("contents", b.getContents());
    out.addSubSection("BAYES-PROPERTIES");
    out.addProperties(b.getProperties());

    out.addSubSection("BAYES-TABLE");
    for (BayesianEvent event : b.getEvents()) {
      for (TableLine line : event.getTable().getLines()) {
        if (line == null) continue;
        StringBuilder str = new StringBuilder();
        str.append("P(");

        str.append(BayesianEvent.formatEventName(event, line.getResult()));

        if (event.getParents().size() > 0) {
          str.append("|");
        }

        int index = 0;
        boolean first = true;
        for (BayesianEvent parentEvent : event.getParents()) {
          if (!first) {
            str.append(",");
          }
          first = false;
          int arg = line.getArguments()[index++];
          if (parentEvent.isBoolean()) {
            if (arg == 0) {
              str.append("+");
            } else {
              str.append("-");
            }
          }
          str.append(parentEvent.getLabel());
          if (!parentEvent.isBoolean()) {
            str.append("=");
            if (arg >= parentEvent.getChoices().size()) {
              throw new BayesianError(
                  "Argument value " + arg + " is out of range for event " + parentEvent.toString());
            }
            str.append(parentEvent.getChoice(arg));
          }
        }
        str.append(")=");
        str.append(line.getProbability());
        str.append("\n");
        out.write(str.toString());
      }
    }

    out.flush();
  }
  /**
   * Define the structure of the Bayesian network as a string.
   *
   * @param line The string to define events and relations.
   */
  public void setContents(String line) {
    List<ParsedProbability> list = ParseProbability.parseProbabilityList(this, line);
    List<String> labelList = new ArrayList<String>();

    // ensure that all events are there
    for (ParsedProbability prob : list) {
      ParsedEvent parsedEvent = prob.getChildEvent();
      String eventLabel = parsedEvent.getLabel();
      labelList.add(eventLabel);

      // create event, if not already here
      BayesianEvent e = getEvent(eventLabel);
      if (e == null) {
        List<BayesianChoice> cl = new ArrayList<BayesianChoice>();

        for (ParsedChoice c : parsedEvent.getList()) {
          cl.add(new BayesianChoice(c.getLabel(), c.getMin(), c.getMax()));
        }

        createEvent(eventLabel, cl);
      }
    }

    // now remove all events that were not covered
    for (int i = 0; i < events.size(); i++) {
      BayesianEvent event = this.events.get(i);
      if (!labelList.contains(event.getLabel())) {
        removeEvent(event);
      }
    }

    // handle dependencies
    for (ParsedProbability prob : list) {
      ParsedEvent parsedEvent = prob.getChildEvent();
      String eventLabel = parsedEvent.getLabel();

      BayesianEvent event = requireEvent(eventLabel);

      // ensure that all "givens" are present
      List<String> givenList = new ArrayList<String>();
      for (ParsedEvent given : prob.getGivenEvents()) {
        if (!event.hasGiven(given.getLabel())) {
          BayesianEvent givenEvent = requireEvent(given.getLabel());
          this.createDependency(givenEvent, event);
        }
        givenList.add(given.getLabel());
      }

      // now remove givens that were not covered
      for (int i = 0; i < event.getParents().size(); i++) {
        BayesianEvent event2 = event.getParents().get(i);
        if (!givenList.contains(event2.getLabel())) {
          removeDependency(event2, event);
        }
      }
    }

    // finalize the structure
    finalizeStructure();
    if (this.query != null) {
      this.query.finalizeStructure();
    }
  }