/** Model check a P operator. */
  private Result checkExpressionProb(ExpressionProb expr) throws PrismException {
    boolean min;
    ExpressionTemporal exprTemp;
    Expression exprTarget;
    BitSet targetLocs;
    int timeBound;
    boolean timeBoundStrict;
    double prob;

    // Check whether Pmin=? or Pmax=? (only two cases allowed)
    if (expr.getProb() != null) {
      throw new PrismException(
          "PTA model checking currently only supports Pmin=? and Pmax=? properties (try the digital clocks engine instead)");
    }
    min = expr.getRelOp().equals("min=");

    // Check this is a F path property (only case allowed at the moment)
    if (!(expr.getExpression() instanceof ExpressionTemporal))
      throw new PrismException(
          "PTA model checking currently only supports the F path operator (try the digital clocks engine instead)");
    exprTemp = (ExpressionTemporal) expr.getExpression();
    if (exprTemp.getOperator() != ExpressionTemporal.P_F || !exprTemp.isSimplePathFormula())
      throw new PrismException(
          "PTA model checking currently only supports the F path operator (try the digital clocks engine instead)");

    // Determine locations satisfying target
    exprTarget = exprTemp.getOperand2();
    targetLocs = checkLocationExpression(exprTarget);
    mainLog.println(
        "Target (" + exprTarget + ") satisfied by " + targetLocs.cardinality() + " locations.");
    // mainLog.println(targetLocs);

    // If there is a time bound, add a clock and encode this into target
    if (exprTemp.hasBounds()) {
      mainLog.println("Modifying PTA to encode time bound from property...");
      // Get time bound info (is always of form <=T or <T)
      timeBound = exprTemp.getUpperBound().evaluateInt(constantValues);
      timeBoundStrict = exprTemp.upperBoundIsStrict();
      // Modify PTA to include time bound; get new target
      targetLocs = buildTimeBoundIntoPta(pta, targetLocs, timeBound, timeBoundStrict);
      mainLog.println("New PTA: " + pta.infoString());
    }

    // Compute probability of reaching the set of target locations
    prob = computeProbabilisticReachability(targetLocs, min);

    // Return result
    return new Result(new Double(prob));
  }
  /**
   * Determine which locations in the PTA satisfy a (Boolean) expression. Note: This is rather
   * inefficiently at the moment. TODO: potentially use explicit.StateMC on dummy model eventually
   */
  private BitSet checkLocationExpression(Expression expr) throws PrismException {
    int i, n;
    BitSet res;

    // Labels - expand and recurse
    // (note: currently not used - these are expanded earlier)
    if (expr instanceof ExpressionLabel) {
      ExpressionLabel exprLabel = (ExpressionLabel) expr;
      if (exprLabel.getName().equals("deadlock"))
        throw new PrismException("The \"deadlock\" label is not yet supported for PTAs");
      if (exprLabel.getName().equals("init"))
        throw new PrismException("The \"init\" label is not yet supported for PTAs");
      i = labelList.getLabelIndex(exprLabel.getName());
      if (i == -1)
        throw new PrismException("Unknown label \"" + exprLabel.getName() + "\" in property");
      // Check recursively
      return checkLocationExpression(labelList.getLabel(i));

    }
    // Other expressions...
    else {
      List<Object> states;
      // Object[] state;
      states = pta.getLocationNameList();
      n = states.size();
      res = new BitSet(n);
      for (i = 0; i < n; i++) {
        // state = (Object[])states.get(i);
        State state = (State) states.get(i);
        if (expr.evaluateBoolean(state, nonClockVarMap)) {
          res.set(i);
        }
      }
    }

    return res;
  }
  /** Model check a property. */
  public Result check(Expression expr) throws PrismException {
    Modules2PTA m2pta;
    Result res;
    String resultString;
    long timer;

    // Starting model checking
    timer = System.currentTimeMillis();

    // Check for system...endsystem - not supported yet
    if (modulesFile.getSystemDefn() != null) {
      throw new PrismException(
          "The system...endsystem construct is not supported yet (try the digital clocks engine instead)");
    }

    // Translate ModulesFile object into a PTA object
    mainLog.println("\nBuilding PTA...");
    m2pta = new Modules2PTA(prism, modulesFile);
    pta = m2pta.translate();
    mainLog.println("\nPTA: " + pta.infoString());

    // Check for references to clocks - not allowed (yet)
    // (do this before modifications below for better error reporting)
    expr.accept(
        new ASTTraverseModify() {
          public Object visit(ExpressionVar e) throws PrismLangException {
            if (e.getType() instanceof TypeClock) {
              throw new PrismLangException(
                  "Properties cannot contain references to clocks (try the digital clocks engine instead)",
                  e);
            } else {
              return e;
            }
          }
        });

    // Take a copy of property, since will modify
    expr = expr.deepCopy();
    // Remove property refs ands labels from property
    expr = (Expression) expr.expandPropRefsAndLabels(propertiesFile, labelList);
    // Evaluate constants in property (easier to do now)
    expr = (Expression) expr.replaceConstants(constantValues);
    // Also simplify expression to optimise model checking
    expr = (Expression) expr.simplify();

    // Do model checking
    res = checkExpression(expr);

    // Model checking complete
    timer = System.currentTimeMillis() - timer;
    mainLog.println("\nModel checking completed in " + (timer / 1000.0) + " secs.");

    // Print result to log
    resultString = "Result";
    if (!("Result".equals(expr.getResultName())))
      resultString += " (" + expr.getResultName().toLowerCase() + ")";
    resultString += ": " + res;
    mainLog.print("\n" + resultString + "\n");

    // Return result
    return res;
  }
  /** Build a time bounded reachability query into a PTA; return the new target location set. */
  private BitSet buildTimeBoundIntoPta(
      PTA pta, BitSet targetLocs, int timeBound, boolean timeBoundStrict) {
    String timerClock = null;
    int timerClockIndex, numLocs, newTargetLoc;
    String newTargetLocString;
    List<Transition> trNewList;
    Transition trNew;
    BitSet targetLocsNew;
    boolean toTarget;
    int i;

    // Add a timer clock
    timerClock = "time";
    while (pta.getClockIndex(timerClock) != -1) timerClock += "_";
    timerClockIndex = pta.addClock(timerClock);
    // Add a new target location
    numLocs = pta.getNumLocations();
    newTargetLocString = "target";
    while (pta.getLocationIndex(newTargetLocString) != -1) newTargetLocString += "_";
    newTargetLoc = pta.addLocation(newTargetLocString);
    // Go through old (on-target) locations
    for (i = 0; i < numLocs; i++) {
      trNewList = new ArrayList<Transition>();
      for (Transition tr : pta.getTransitions(i)) {
        // See if the transition can go to a target location
        toTarget = false;
        for (Edge e : tr.getEdges()) {
          if (targetLocs.get(e.getDestination())) {
            toTarget = true;
            break;
          }
        }
        // Copy transition, modify edges going to target and add guard
        if (toTarget) {
          trNew = new Transition(tr);
          for (Edge e : trNew.getEdges()) {
            if (targetLocs.get(e.getDestination())) {
              e.setDestination(newTargetLoc);
            }
          }
          if (timeBoundStrict)
            trNew.addGuardConstraint(Constraint.buildLt(timerClockIndex, timeBound));
          else trNew.addGuardConstraint(Constraint.buildLeq(timerClockIndex, timeBound));
          trNewList.add(trNew);
          // Modify guard of copied transition
          if (timeBoundStrict)
            tr.addGuardConstraint(Constraint.buildGeq(timerClockIndex, timeBound));
          else tr.addGuardConstraint(Constraint.buildGt(timerClockIndex, timeBound));
        }
      }
      // Add new transitions to PTA
      for (Transition tr : trNewList) {
        pta.addTransition(tr);
      }
    }
    // Re-generate set of target locations
    targetLocsNew = new BitSet(pta.getNumLocations());
    targetLocsNew.set(newTargetLoc);

    return targetLocsNew;
  }