/**
  * Constructor: build an STPG from an MDP. Data is copied directly from the MDP so take a copy
  * first if you plan to keep/modify the MDP.
  */
 public STPGAbstrSimple(MDPSimple m) {
   DistributionSet set;
   int i;
   // TODO: actions?
   initialise(m.getNumStates());
   copyFrom(m);
   for (i = 0; i < numStates; i++) {
     set = newDistributionSet(null);
     set.addAll(m.getChoices(i));
     addDistributionSet(i, set);
   }
 }
 @Override
 public Iterator<Entry<Integer, Double>> getNestedTransitionsIterator(int s, int i, int j) {
   DistributionSet ds = trans.get(s).get(i);
   Iterator<Distribution> iter = ds.iterator();
   Distribution distr = null;
   int k = 0;
   while (iter.hasNext() && k <= j) {
     distr = iter.next();
     k++;
   }
   if (k <= j) return null;
   else return distr.iterator();
 }
 @Override
 public int getNumNestedTransitions(int s, int i, int j) {
   DistributionSet ds = trans.get(s).get(i);
   Iterator<Distribution> iter = ds.iterator();
   Distribution distr = null;
   int k = 0;
   while (iter.hasNext() && k <= j) {
     distr = iter.next();
     k++;
   }
   if (k <= j) return 0;
   else return distr.size();
 }
 @Override
 public void findDeadlocks(boolean fix) throws PrismException {
   for (int i = 0; i < numStates; i++) {
     // Note that no distributions is a deadlock, not an empty distribution
     if (trans.get(i).isEmpty()) {
       addDeadlockState(i);
       if (fix) {
         DistributionSet distrs = newDistributionSet(null);
         Distribution distr = new Distribution();
         distr.add(i, 1.0);
         distrs.add(distr);
         addDistributionSet(i, distrs);
       }
     }
   }
 }
 @Override
 public void clearState(int i) {
   // Do nothing if state does not exist
   if (i >= numStates || i < 0) return;
   // Clear data structures and update stats
   List<DistributionSet> list = trans.get(i);
   numDistrSets -= list.size();
   for (DistributionSet set : list) {
     numDistrs -= set.size();
     for (Distribution distr : set) numTransitions -= distr.size();
   }
   // TODO: recompute maxNumDistrSets
   // TODO: recompute maxNumDistrs
   // Remove all distribution sets
   trans.set(i, new ArrayList<DistributionSet>(0));
 }
 /**
  * Add distribution set 'newSet' to state s (which must exist). Distribution set is only actually
  * added if it does not already exists for state s. (Assuming 'allowDupes' flag is not enabled.)
  * Returns the index of the (existing or newly added) set. Returns -1 in case of error.
  */
 public int addDistributionSet(int s, DistributionSet newSet) {
   ArrayList<DistributionSet> set;
   // Check state exists
   if (s >= numStates || s < 0) return -1;
   // Add distribution set (if new)
   set = trans.get(s);
   if (!allowDupes) {
     int i = set.indexOf(newSet);
     if (i != -1) return i;
   }
   set.add(newSet);
   // Update stats
   numDistrSets++;
   maxNumDistrSets = Math.max(maxNumDistrSets, set.size());
   numDistrs += newSet.size();
   maxNumDistrs = Math.max(maxNumDistrs, newSet.size());
   for (Distribution distr : newSet) numTransitions += distr.size();
   return set.size() - 1;
 }
  /** Simple test program */
  public static void main(String args[]) {
    STPGModelChecker mc;
    STPGAbstrSimple stpg;
    DistributionSet set;
    Distribution distr;
    // ModelCheckerResult res;
    BitSet target;

    // Simple example: Create and solve the stochastic game from:
    // Mark Kattenbelt, Marta Kwiatkowska, Gethin Norman, David Parker
    // A Game-based Abstraction-Refinement Framework for Markov Decision Processes
    // Formal Methods in System Design 36(3): 246-280, 2010

    try {
      // Build game
      stpg = new STPGAbstrSimple();
      stpg.addStates(4);
      // State 0 (s_0)
      set = stpg.newDistributionSet(null);
      distr = new Distribution();
      distr.set(1, 1.0);
      set.add(distr);
      stpg.addDistributionSet(0, set);
      // State 1 (s_1,s_2,s_3)
      set = stpg.newDistributionSet(null);
      distr = new Distribution();
      distr.set(2, 1.0);
      set.add(distr);
      distr = new Distribution();
      distr.set(1, 1.0);
      set.add(distr);
      stpg.addDistributionSet(1, set);
      set = stpg.newDistributionSet(null);
      distr = new Distribution();
      distr.set(2, 0.5);
      distr.set(3, 0.5);
      set.add(distr);
      distr = new Distribution();
      distr.set(3, 1.0);
      set.add(distr);
      stpg.addDistributionSet(1, set);
      // State 2 (s_4,s_5)
      set = stpg.newDistributionSet(null);
      distr = new Distribution();
      distr.set(2, 1.0);
      set.add(distr);
      stpg.addDistributionSet(2, set);
      // State 3 (s_6)
      set = stpg.newDistributionSet(null);
      distr = new Distribution();
      distr.set(3, 1.0);
      set.add(distr);
      stpg.addDistributionSet(3, set);
      // Print game
      System.out.println(stpg);

      // Model check
      mc = new STPGModelChecker(null);
      // mc.setVerbosity(2);
      target = new BitSet();
      target.set(3);
      stpg.exportToDotFile("stpg.dot", target);
      System.out.println("min min: " + mc.computeReachProbs(stpg, target, true, true).soln[0]);
      System.out.println("max min: " + mc.computeReachProbs(stpg, target, false, true).soln[0]);
      System.out.println("min max: " + mc.computeReachProbs(stpg, target, true, false).soln[0]);
      System.out.println("max max: " + mc.computeReachProbs(stpg, target, false, false).soln[0]);
    } catch (PrismException e) {
      System.out.println(e);
    }
  }
  @Override
  public void buildFromPrismExplicit(String filename) throws PrismException {
    BufferedReader in;
    Distribution distr;
    DistributionSet distrs;
    String s, ss[];
    int i, j, k1, k2, iLast, k1Last, k2Last, n, lineNum = 0;
    double prob;

    try {
      // Open file
      in = new BufferedReader(new FileReader(new File(filename)));
      // Parse first line to get num states
      s = in.readLine();
      lineNum = 1;
      if (s == null) {
        in.close();
        throw new PrismException("Missing first line of .tra file");
      }
      ss = s.split(" ");
      n = Integer.parseInt(ss[0]);
      // Initialise
      initialise(n);
      // Go though list of transitions in file
      iLast = -1;
      k1Last = -1;
      k2Last = -1;
      distrs = null;
      distr = null;
      s = in.readLine();
      lineNum++;
      while (s != null) {
        s = s.trim();
        if (s.length() > 0) {
          ss = s.split(" ");
          i = Integer.parseInt(ss[0]);
          k1 = Integer.parseInt(ss[1]);
          k2 = Integer.parseInt(ss[2]);
          j = Integer.parseInt(ss[3]);
          prob = Double.parseDouble(ss[4]);
          // For a new state or distribution set or distribution
          if (i != iLast || k1 != k1Last || k2 != k2Last) {
            // Add any previous distribution to the last set, create new one
            if (distrs != null) {
              distrs.add(distr);
            }
            distr = new Distribution();
            // Only for a new state or distribution set...
            if (i != iLast || k1 != k1Last) {
              // Add any previous distribution set to the last state, create new one
              if (distrs != null) {
                addDistributionSet(iLast, distrs);
              }
              distrs = newDistributionSet(null);
            }
          }
          // Add transition to the current distribution
          distr.add(j, prob);
          // Prepare for next iter
          iLast = i;
          k1Last = k1;
          k2Last = k2;
        }
        s = in.readLine();
        lineNum++;
      }
      // Add previous distribution to the last set
      distrs.add(distr);
      // Add previous distribution set to the last state
      addDistributionSet(iLast, distrs);
      // Close file
      in.close();
    } catch (IOException e) {
      System.out.println(e);
      System.exit(1);
    } catch (NumberFormatException e) {
      throw new PrismException("Problem in .tra file (line " + lineNum + ") for " + getModelType());
    }
    // Set initial state (assume 0)
    initialStates.add(0);
  }