/**
   * Verifies that heap and global object sets are disjunct
   *
   * @param pLogger Logger to log the message
   * @param pSmg SMG to check
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyDisjunctHeapAndGlobal(LogManager pLogger, CLangSMG pSmg) {
    Map<String, SMGRegion> globals = pSmg.getGlobalObjects();
    Set<SMGObject> heap = pSmg.getHeapObjects();

    boolean toReturn = Collections.disjoint(globals.values(), heap);

    if (!toReturn) {
      pLogger.log(Level.SEVERE, "CLangSMG inconsistent, heap and global objects are not disjoint");
    }

    return toReturn;
  }
  /**
   * Verifies several NULL object-related properties
   *
   * @param pLogger Logger to log the message
   * @param pSmg SMG to check
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyNullObjectCLangProperties(LogManager pLogger, CLangSMG pSmg) {
    // Verify that there is no NULL object in global scope
    for (SMGObject obj : pSmg.getGlobalObjects().values()) {
      if (!obj.notNull()) {
        pLogger.log(
            Level.SEVERE, "CLangSMG inconsistent: null object in global object set [" + obj + "]");
        return false;
      }
    }

    // Verify there is no more than one NULL object in the heap object set
    SMGObject firstNull = null;
    for (SMGObject obj : pSmg.getHeapObjects()) {
      if (!obj.notNull()) {
        if (firstNull != null) {
          pLogger.log(
              Level.SEVERE,
              "CLangSMG inconsistent: second null object in heap object set [first="
                  + firstNull
                  + ", second="
                  + obj
                  + "]");
          return false;
        } else {
          firstNull = obj;
        }
      }
    }

    // Verify there is no NULL object in the stack object set
    for (CLangStackFrame frame : pSmg.getStackFrames()) {
      for (SMGObject obj : frame.getAllObjects()) {
        if (!obj.notNull()) {
          pLogger.log(
              Level.SEVERE, "CLangSMG inconsistent: null object in stack object set [" + obj + "]");
          return false;
        }
      }
    }

    // Verify there is at least one NULL object
    if (firstNull == null) {
      pLogger.log(Level.SEVERE, "CLangSMG inconsistent: no null object");
      return false;
    }

    return true;
  }
  /**
   * Verifies that global and stack object sets are disjunct
   *
   * @param pLogger Logger to log the message
   * @param pSmg SMG to check
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyDisjunctGlobalAndStack(LogManager pLogger, CLangSMG pSmg) {
    Deque<CLangStackFrame> stack_frames = pSmg.getStackFrames();
    Set<SMGObject> stack = new HashSet<>();

    for (CLangStackFrame frame : stack_frames) {
      stack.addAll(frame.getAllObjects());
    }
    Map<String, SMGRegion> globals = pSmg.getGlobalObjects();

    boolean toReturn = Collections.disjoint(stack, globals.values());

    if (!toReturn) {
      pLogger.log(Level.SEVERE, "CLangSMG inconsistent, global and stack objects are not disjoint");
    }

    return toReturn;
  }
  /**
   * Verify the global scope is consistent: each record points to an appropriately labeled object
   *
   * @param pLogger Logger to log the message
   * @param pSmg SMG to check
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyGlobalNamespace(LogManager pLogger, CLangSMG pSmg) {
    Map<String, SMGRegion> globals = pSmg.getGlobalObjects();

    for (String label : pSmg.getGlobalObjects().keySet()) {
      String globalLabel = globals.get(label).getLabel();
      if (!globalLabel.equals(label)) {
        pLogger.log(
            Level.SEVERE,
            "CLangSMG inconsistent: label ["
                + label
                + "] points to an object with label ["
                + pSmg.getGlobalObjects().get(label).getLabel()
                + "]");
        return false;
      }
    }

    return true;
  }
  /**
   * Verifies that heap and stack object sets are disjunct
   *
   * @param pLogger Logger to log the message
   * @param pSmg SMG to check
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyDisjunctHeapAndStack(LogManager pLogger, CLangSMG pSmg) {
    Deque<CLangStackFrame> stack_frames = pSmg.getStackFrames();
    Set<SMGObject> stack = new HashSet<>();

    for (CLangStackFrame frame : stack_frames) {
      stack.addAll(frame.getAllObjects());
    }
    Set<SMGObject> heap = pSmg.getHeapObjects();

    boolean toReturn = Collections.disjoint(stack, heap);

    if (!toReturn) {
      pLogger.log(
          Level.SEVERE,
          "CLangSMG inconsistent, heap and stack objects are not disjoint: "
              + Sets.intersection(stack, heap));
    }

    return toReturn;
  }
  /**
   * Verifies that heap, global and stack union is equal to the set of all objects
   *
   * @param pLogger Logger to log the message
   * @param pSmg SMG to check
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyStackGlobalHeapUnion(LogManager pLogger, CLangSMG pSmg) {
    HashSet<SMGObject> object_union = new HashSet<>();

    object_union.addAll(pSmg.getHeapObjects());
    object_union.addAll(pSmg.getGlobalObjects().values());

    for (CLangStackFrame frame : pSmg.getStackFrames()) {
      object_union.addAll(frame.getAllObjects());
    }

    boolean toReturn =
        object_union.containsAll(pSmg.getObjects()) && pSmg.getObjects().containsAll(object_union);

    if (!toReturn) {
      pLogger.log(
          Level.SEVERE,
          "CLangSMG inconsistent: union of stack, heap and global object is not the same set as the set of SMG objects");
    }

    return toReturn;
  }
  /**
   * Verify the stack name space: each record points to an appropriately labeled object
   *
   * @param pLogger Logger to log the message
   * @param pSmg the current smg
   * @return True if pSmg is consistent w.r.t. this criteria. False otherwise.
   */
  private static boolean verifyStackNamespaces(LogManager pLogger, CLangSMG pSmg) {
    HashSet<SMGObject> stack_objects = new HashSet<>();

    for (CLangStackFrame frame : pSmg.getStackFrames()) {
      for (SMGObject object : frame.getAllObjects()) {
        if (stack_objects.contains(object)) {
          pLogger.log(
              Level.SEVERE,
              "CLangSMG inconsistent: object [" + object + "] present multiple times in the stack");
          return false;
        }
        stack_objects.add(object);
      }
    }

    return true;
  }