private static SamplerBoolean createSamplerForProbPathPropertySimple(
      Expression expr, ModulesFile mf) throws PrismException {
    // Negation/parentheses
    if (expr instanceof ExpressionUnaryOp) {
      ExpressionUnaryOp exprUnary = (ExpressionUnaryOp) expr;
      // Parentheses
      if (exprUnary.getOperator() == ExpressionUnaryOp.PARENTH) {
        // Recurse
        return createSamplerForProbPathPropertySimple(exprUnary.getOperand(), mf);
      }
      // Negation
      else if (exprUnary.getOperator() == ExpressionUnaryOp.NOT) {
        // Recurse, then negate meaning
        SamplerBoolean sampler = createSamplerForProbPathPropertySimple(exprUnary.getOperand(), mf);
        sampler.negate();
        return sampler;
      }
    }
    // Temporal operators
    else if (expr instanceof ExpressionTemporal) {
      ExpressionTemporal exprTemp = (ExpressionTemporal) expr;
      // Next
      if (exprTemp.getOperator() == ExpressionTemporal.P_X) {
        return new SamplerNext(exprTemp);
      }
      // Until
      else if (exprTemp.getOperator() == ExpressionTemporal.P_U) {
        if (exprTemp.hasBounds()) {
          if (mf.getModelType().continuousTime()) {
            // Continuous-time bounded until
            return new SamplerBoundedUntilCont(exprTemp);
          } else {
            // Discrete-time bounded until
            return new SamplerBoundedUntilDisc(exprTemp);
          }
        } else {
          // Unbounded until
          return new SamplerUntil(exprTemp);
        }
      }
      // Anything else - convert to until and recurse
      else {
        return createSamplerForProbPathPropertySimple(exprTemp.convertToUntilForm(), mf);
      }
    }

    throw new PrismException("Can't create sampler for property \"" + expr + "\"");
  }
  private static SamplerDouble createSamplerForRewardProperty(ExpressionReward expr, ModulesFile mf)
      throws PrismException {
    // Extract reward structure index
    Object rs = expr.getRewardStructIndex();
    int rsi = -1;
    if (mf.getNumRewardStructs() == 0) throw new PrismException("Model has no rewards specified");
    if (rs == null) {
      rsi = 0;
    } else if (rs instanceof Expression) {
      rsi = ((Expression) rs).evaluateInt();
      rs = new Integer(rsi); // for better error reporting below
      rsi = (rsi < 1 || rsi > mf.getNumRewardStructs()) ? -1 : rsi - 1;
    } else if (rs instanceof String) {
      rsi = mf.getRewardStructIndex((String) rs);
    }
    if (rsi == -1) throw new PrismException("Invalid reward structure index \"" + rs + "\"");

    // Construct sampler based on type
    ExpressionTemporal exprTemp = (ExpressionTemporal) expr.getExpression();
    switch (exprTemp.getOperator()) {
      case ExpressionTemporal.R_C:
        if (mf.getModelType().continuousTime()) {
          // Continuous-time cumulative reward
          return new SamplerRewardCumulCont(exprTemp, rsi);
        } else {
          // Discrete-time cumulative reward
          return new SamplerRewardCumulDisc(exprTemp, rsi);
        }
      case ExpressionTemporal.R_I:
        if (mf.getModelType().continuousTime()) {
          // Continuous-time instantaneous reward
          return new SamplerRewardInstCont(exprTemp, rsi);
        } else {
          // Discrete-time instantaneous reward
          return new SamplerRewardInstDisc(exprTemp, rsi);
        }
      case ExpressionTemporal.P_F:
        // reachability reward
        return new SamplerRewardReach(exprTemp, rsi);
    }

    throw new PrismException("Can't create sampler for property \"" + expr + "\"");
  }