class LocalPacker extends BodyTransformer {
  private static final Logger logger = LoggerFactory.getLogger(LocalPacker.class);

  public LocalPacker(Singletons.Global g) {
  }

  public static LocalPacker v() {
    return G.v().soot_toolkits_scalar_LocalPacker();
  }

  @Override
  protected void internalTransform(Body body, String phaseName, Map<String, String> options) {<FILL_FUNCTION_BODY>}
}

    if (Options.v().verbose()) {
      logger.debug("[" + body.getMethod().getName() + "] Packing locals...");
    }

    final Chain<Local> bodyLocalsRef = body.getLocals();
    final int origLocalCount = bodyLocalsRef.size();
    if (origLocalCount < 1) {
      return;
    }

    // A group represents a bunch of locals which may potentially interfere with each other.
    // Separate groups can not possibly interfere with each other (coloring say ints and doubles).
    Map<Local, Type> localToGroup = new DeterministicHashMap<Local, Type>(origLocalCount * 2 + 1, 0.7f);
    Map<Type, Integer> groupToColorCount = new HashMap<Type, Integer>(origLocalCount * 2 + 1, 0.7f);
    Map<Local, Integer> localToColor = new HashMap<Local, Integer>(origLocalCount * 2 + 1, 0.7f);

    // Assign each local to a group, and set that group's color count to 0.
    for (Local l : bodyLocalsRef) {
      Type g = l.getType();
      localToGroup.put(l, g);
      groupToColorCount.putIfAbsent(g, 0);
    }

    // Assign colors to the parameter locals.
    for (Unit s : body.getUnits()) {
      if (s instanceof IdentityUnit) {
        Value leftOp = ((IdentityUnit) s).getLeftOp();
        if (leftOp instanceof Local) {
          Local l = (Local) leftOp;

          Type group = localToGroup.get(l);
          Integer count = groupToColorCount.get(group);
          localToColor.put(l, count);
          groupToColorCount.put(group, count + 1);
        }
      }
    }

    // Call the graph colorer.
    if (PhaseOptions.getBoolean(options, "unsplit-original-locals")) {
      FastColorer.unsplitAssignColorsToLocals(body, localToGroup, localToColor, groupToColorCount);
    } else {
      FastColorer.assignColorsToLocals(body, localToGroup, localToColor, groupToColorCount);
    }

    // Map each local to a new local.
    Map<Local, Local> localToNewLocal = new HashMap<Local, Local>(origLocalCount * 2 + 1, 0.7f);
    {
      Map<GroupIntPair, Local> groupIntToLocal = new HashMap<GroupIntPair, Local>(origLocalCount * 2 + 1, 0.7f);
      List<Local> originalLocals = new ArrayList<Local>(bodyLocalsRef);
      bodyLocalsRef.clear();

      final Set<String> usedLocalNames = new HashSet<>();
      for (Local original : originalLocals) {
        Type group = localToGroup.get(original);
        GroupIntPair pair = new GroupIntPair(group, localToColor.get(original));

        Local newLocal = groupIntToLocal.get(pair);
        if (newLocal == null) {
          newLocal = (Local) original.clone();
          newLocal.setType(group);

          // Added 'usedLocalNames' for distinct naming.
          // Icky fix. But I guess it works. -PL
          // It is no substitute for really understanding the
          // problem, though. I'll leave that to someone
          // who really understands the local naming stuff.
          // Does such a person exist?
          //
          // I'll just leave this comment as folklore for future
          // generations. The problem with it is that you can end up
          // with different locals that share the same name which can
          // lead to all sorts of funny results. (SA, 2017-03-02)
          //
          // If we have a split local, let's find a better name for it
          String name = newLocal.getName();
          if (name != null) {
            int signIndex = name.indexOf('#');
            if (signIndex >= 0) {
              String newName = name.substring(0, signIndex);
              if (usedLocalNames.add(newName)) {
                newLocal.setName(newName);
              } else {
                // just leave it alone for now
              }
            } else {
              usedLocalNames.add(name);

            }
          }

          groupIntToLocal.put(pair, newLocal);
          bodyLocalsRef.add(newLocal);
        }

        localToNewLocal.put(original, newLocal);
      }
    }

    // Go through all valueBoxes of this method and perform changes
    for (Unit s : body.getUnits()) {
      for (ValueBox box : s.getUseBoxes()) {
        Value val = box.getValue();
        if (val instanceof Local) {
          box.setValue(localToNewLocal.get((Local) val));
        }
      }
      for (ValueBox box : s.getDefBoxes()) {
        Value val = box.getValue();
        if (val instanceof Local) {
          box.setValue(localToNewLocal.get((Local) val));
        }
      }
    }
  
<methods>public non-sealed void <init>() ,public final void transform(soot.Body, java.lang.String, Map<java.lang.String,java.lang.String>) ,public final void transform(soot.Body, java.lang.String) ,public final void transform(soot.Body) <variables>private static final Map<java.lang.String,java.lang.String> enabledOnlyMap