class ClinitElimAnalysis extends ForwardFlowAnalysis<Unit, FlowSet<SootMethod>> {

  private final CallGraph cg = Scene.v().getCallGraph();
  private final UnitGraph g;

  private static FlowSet<SootMethod> cachedFlowSet = null;

  public ClinitElimAnalysis(UnitGraph g) {
    super(g);
    this.g = g;

    doAnalysis();
  }

  @Override
  public void merge(FlowSet<SootMethod> in1, FlowSet<SootMethod> in2, FlowSet<SootMethod> out) {
    in1.intersection(in2, out);
  }

  @Override
  public void copy(FlowSet<SootMethod> src, FlowSet<SootMethod> dest) {
    src.copy(dest);
  }

  @Override
  protected void copyFreshToExisting(FlowSet<SootMethod> in, FlowSet<SootMethod> dest) {
    in.copyFreshToExisting(dest);
  }

  // out(s) = in(s) intersect { target methods of s where edge kind is clinit}
  @Override
  protected void flowThrough(FlowSet<SootMethod> inVal, Unit stmt, FlowSet<SootMethod> outVal) {
    inVal.copy(outVal);

    for (Iterator<Edge> edges = cg.edgesOutOf(stmt); edges.hasNext();) {
      Edge e = edges.next();
      if (e.isClinit()) {
        outVal.add(e.tgt());
      }
    }
  }

  @Override
  protected FlowSet<SootMethod> entryInitialFlow() {
    return new HashSparseSet<SootMethod>();
  }

  @Override
  protected FlowSet<SootMethod> newInitialFlow() {
    HashSparseSet<SootMethod> returnedFlowSet = new HashSparseSet<>();
    if (cachedFlowSet == null) {
      cachedFlowSet = calculateInitialFlow();
    }
    cachedFlowSet.copy(returnedFlowSet);
    return returnedFlowSet;
  }
  protected FlowSet<SootMethod> calculateInitialFlow() {<FILL_FUNCTION_BODY>}

}

    HashSparseSet<SootMethod> newFlowSet = new HashSparseSet<>();
    for (Iterator<Edge> mIt = cg.edgesOutOf(g.getBody().getMethod()); mIt.hasNext();) {
      Edge edge = mIt.next();
      if (edge.isClinit()) {
        newFlowSet.add(edge.tgt());
      }
    }
    return newFlowSet;
  
<methods>public void <init>(DirectedGraph<soot.Unit>) <variables>