Initial push.
diff --git a/src/main/java/com/android/tools/r8/BSPatch.java b/src/main/java/com/android/tools/r8/BSPatch.java
new file mode 100644
index 0000000..eda5f23
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BSPatch.java
@@ -0,0 +1,335 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.DexFileReader;
+import com.android.tools.r8.dex.Segment;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import javax.imageio.ImageIO;
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+
+public class BSPatch {
+
+  private final static char[] BSDIFF_MAGIC = "BSDIFF40".toCharArray();
+  private final static int BSDIFF_HEADER_LENGTH = 32;
+
+  private final ByteBuffer patchInput;
+  private final ByteBuffer oldInput;
+  private final Path output;
+  private final Path dexPath;
+
+  private InputStream controlStream;
+  private InputStream diffStream;
+  private InputStream extraStream;
+
+  private long controlBytesRead;
+  private long diffBytesRead;
+  private long extraBytesRead;
+  private int controlBlockLen;
+  private int diffBlockLen;
+  private int extraBlockLen;
+
+  public static void main(String[] args) {
+    boolean imageMode = args.length > 1 && args[0].equals("-i");
+    int argOffset = imageMode ? 1 : 0;
+    if (args.length < argOffset + 3) {
+      System.out.println("Usage: [-i] <patch file> <original dex> <output file> [target dex]");
+      System.exit(1);
+    }
+    try {
+      new BSPatch(Paths.get(args[argOffset]), Paths.get(args[argOffset + 1]),
+          Paths.get(args[argOffset + 2]),
+          args.length != argOffset + 4 ? null : Paths.get(args[argOffset + 3]))
+          .apply(imageMode);
+    } catch (IOException e) {
+      System.err.println("File I/O error: " + e.toString());
+    } catch (CompressorException e) {
+      System.err.println("BZIP error: " + e.toString());
+    }
+  }
+
+  private BSPatch(Path patchInput, Path oldInput, Path output, Path dexPath) throws IOException {
+    this.patchInput = ByteBuffer.wrap(Files.readAllBytes(patchInput));
+    this.oldInput = ByteBuffer.wrap(Files.readAllBytes(oldInput));
+    this.output = output;
+    this.dexPath = dexPath;
+  }
+
+  public void apply(boolean imageMode) throws CompressorException, IOException {
+    PatchExecutor executor = imageMode ? new ImageExecutor(dexPath) : new FileExecutor();
+    checkHeader();
+    setupSegmentsAndOutput(executor);
+    processControl(executor);
+    executor.writeResult();
+    printStats();
+  }
+
+  private int percentOf(long a, long b) {
+    return (int) ((((double) a) / ((double) b)) * 100);
+  }
+
+  private void printStats() {
+    System.out.println("Size of control block (compressed bytes): " + controlBlockLen);
+    System.out.println("Size of control block (read bytes): " + controlBytesRead);
+    System.out
+        .println("Compression of control block: " + percentOf(controlBlockLen, controlBytesRead));
+    System.out.println("Size of diff data block (compressed bytes): " + diffBlockLen);
+    System.out.println("Size of diff data block (read bytes): " + diffBytesRead);
+    System.out
+        .println("Compression of diff data block: " + percentOf(diffBlockLen, diffBytesRead));
+    System.out.println("Size of extra data block (compressed bytes): " + extraBlockLen);
+    System.out.println("Size of extra data block (read bytes): " + extraBytesRead);
+    System.out
+        .println("Compression of extra data block: " + percentOf(extraBlockLen, extraBytesRead));
+  }
+
+  private void processControl(PatchExecutor executor) throws IOException {
+    int blockSize;
+    while ((blockSize = readNextControlEntry()) != Integer.MIN_VALUE) {
+      int extraSize = readNextControlEntry();
+      int advanceOld = readNextControlEntry();
+      executor.copyDiff(blockSize);
+      executor.copyOld(blockSize);
+      executor.submitBlock(blockSize);
+      executor.copyExtra(extraSize);
+      executor.skipOld(advanceOld);
+    }
+  }
+
+  private void checkHeader() {
+    for (int i = 0; i < BSDIFF_MAGIC.length; i++) {
+      if (patchInput.get() != BSDIFF_MAGIC[i]) {
+        throw new RuntimeException("Illegal patch, wrong magic!");
+      }
+    }
+  }
+
+  private void setupSegmentsAndOutput(PatchExecutor executor)
+      throws CompressorException, IOException {
+    controlBlockLen = readOffset();
+    diffBlockLen = readOffset();
+    int newFileSize = readOffset();
+
+    extraBlockLen =
+        patchInput.array().length - (BSDIFF_HEADER_LENGTH + controlBlockLen + diffBlockLen);
+
+    executor.createOutput(newFileSize);
+    controlStream = new BZip2CompressorInputStream(
+        new ByteArrayInputStream(patchInput.array(), BSDIFF_HEADER_LENGTH, controlBlockLen));
+    diffStream = new BZip2CompressorInputStream(
+        new ByteArrayInputStream(patchInput.array(), BSDIFF_HEADER_LENGTH + controlBlockLen,
+            diffBlockLen));
+    extraStream = new BZip2CompressorInputStream(
+        new ByteArrayInputStream(patchInput.array(),
+            BSDIFF_HEADER_LENGTH + controlBlockLen + diffBlockLen,
+            extraBlockLen));
+  }
+
+  private int readOffset() {
+    byte[] buffer = new byte[8];
+    patchInput.get(buffer);
+    return decodeOffset(buffer);
+  }
+
+  private int readNextControlEntry() throws IOException {
+    byte[] buffer = new byte[8];
+    int read = controlStream.read(buffer);
+    if (read == -1) {
+      return Integer.MIN_VALUE;
+    }
+    controlBytesRead += read;
+    assert read == buffer.length;
+    return decodeOffset(buffer);
+  }
+
+  private static int decodeOffset(byte[] buffer) {
+    long offset = buffer[7] & 0x7F;
+    for (int i = 6; i >= 0; i--) {
+      offset = (offset << 8) | (((int) buffer[i]) & 0xff);
+    }
+    if ((buffer[7] & 0x80) != 0) {
+      offset = -offset;
+    }
+    assert offset < Integer.MAX_VALUE && offset > Integer.MIN_VALUE;
+    return (int) offset;
+  }
+
+  private abstract class PatchExecutor {
+
+    public abstract void createOutput(int newFileSize);
+
+    public abstract void copyDiff(int blockSize) throws IOException;
+
+    public abstract void copyOld(int blockSize) throws IOException;
+
+    public abstract void submitBlock(int blockSize);
+
+    public abstract void copyExtra(int extraSize) throws IOException;
+
+    public abstract void skipOld(int advanceOld) throws IOException;
+
+    public abstract void writeResult() throws IOException;
+  }
+
+  private class FileExecutor extends PatchExecutor {
+
+    private ByteBuffer resultBuffer;
+    private byte[] mergeBuffer = null;
+
+    @Override
+    public void createOutput(int newFileSize) {
+      resultBuffer = ByteBuffer.allocate(newFileSize);
+    }
+
+    @Override
+    public void copyDiff(int blockSize) throws IOException {
+      assert mergeBuffer == null;
+      mergeBuffer = new byte[blockSize];
+      int read = diffStream.read(mergeBuffer);
+      diffBytesRead += read;
+      assert read == blockSize;
+    }
+
+    @Override
+    public void copyOld(int blockSize) throws IOException {
+      assert mergeBuffer != null;
+      assert mergeBuffer.length == blockSize;
+      byte[] data = new byte[blockSize];
+      oldInput.get(data);
+      for (int i = 0; i < mergeBuffer.length; i++) {
+        mergeBuffer[i] = (byte) ((((int) mergeBuffer[i]) & 0xff) + (((int) data[i]) & 0xff));
+      }
+    }
+
+    @Override
+    public void submitBlock(int blockSize) {
+      assert mergeBuffer != null;
+      assert mergeBuffer.length == blockSize;
+      resultBuffer.put(mergeBuffer);
+      mergeBuffer = null;
+    }
+
+    @Override
+    public void copyExtra(int extraSize) throws IOException {
+      byte[] data = new byte[extraSize];
+      int read = extraStream.read(data);
+      assert read == extraSize;
+      extraBytesRead += read;
+      resultBuffer.put(data);
+    }
+
+    @Override
+    public void skipOld(int delta) throws IOException {
+      oldInput.position(oldInput.position() + delta);
+    }
+
+    @Override
+    public void writeResult() throws IOException {
+      OutputStream outputStream = Files.newOutputStream(output);
+      outputStream.write(resultBuffer.array());
+      outputStream.close();
+    }
+  }
+
+  private class ImageExecutor extends PatchExecutor {
+
+    private final Path dexPath;
+
+    BufferedImage image;
+    int position = 0;
+    int width;
+    int height;
+
+    private ImageExecutor(Path dexPath) {
+      this.dexPath = dexPath;
+    }
+
+    @Override
+    public void createOutput(int newFileSize) {
+      int root = (int) Math.sqrt(newFileSize);
+      width = newFileSize / root;
+      height = newFileSize / width + 1;
+      image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
+    }
+
+    @Override
+    public void copyDiff(int blockSize) throws IOException {
+      byte[] buffer = new byte[blockSize];
+      int read = diffStream.read(buffer);
+      assert read == blockSize;
+      diffBytesRead += read;
+      for (int i = 0; i < buffer.length; i++) {
+        if (buffer[i] != 0) {
+          int y = (position + i) / width;
+          int x = (position + i) % width;
+          int rgb = image.getRGB(x, y);
+          rgb = rgb | 0xFF0000;
+          image.setRGB(x, y, rgb);
+        }
+      }
+    }
+
+    @Override
+    public void copyOld(int blockSize) throws IOException {
+      for (int i = 0; i < blockSize; i++) {
+        int y = (position + i) / width;
+        int x = (position + i) % width;
+        int rgb = image.getRGB(x, y);
+        if ((rgb & 0xFF0000) == 0) {
+          rgb = rgb | 0xFF00;
+        }
+        image.setRGB(x, y, rgb);
+      }
+    }
+
+    @Override
+    public void submitBlock(int blockSize) {
+      position += blockSize;
+    }
+
+    @Override
+    public void copyExtra(int extraSize) throws IOException {
+      long skipped = extraStream.skip(extraSize);
+      assert skipped == extraSize;
+      extraBytesRead += skipped;
+      for (int i = 0; i < extraSize; i++) {
+        int y = (position + i) / width;
+        int x = (position + i) % width;
+        int rgb = image.getRGB(x, y);
+        rgb = rgb | 0xFF;
+        image.setRGB(x, y, rgb);
+      }
+      position += extraSize;
+    }
+
+    @Override
+    public void skipOld(int advanceOld) throws IOException {
+    }
+
+    @Override
+    public void writeResult() throws IOException {
+      if (dexPath != null) {
+        Segment[] segments = DexFileReader.parseMapFrom(dexPath);
+        for (Segment segment : segments) {
+          int y = segment.offset / width;
+          for (int x = 0; x < width; x++) {
+            int val = (x / 10) % 2 == 0 ? 0 : 0xffffff;
+            image.setRGB(x, y, val);
+          }
+          System.out.println(segment);
+        }
+      }
+      ImageIO.write(image, "png", output.toFile());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
new file mode 100644
index 0000000..bf58365
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -0,0 +1,232 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+
+abstract class BaseCommand {
+
+  private final boolean printHelp;
+  private final boolean printVersion;
+
+  private final AndroidApp app;
+  private final Path outputPath;
+  private final CompilationMode mode;
+  private final int minApiLevel;
+
+  BaseCommand(boolean printHelp, boolean printVersion) {
+    this.printHelp = printHelp;
+    this.printVersion = printVersion;
+    // All other fields are initialized with stub/invalid values.
+    this.app = null;
+    this.outputPath = null;
+    this.mode = null;
+    this.minApiLevel = 0;
+  }
+
+  BaseCommand(AndroidApp app, Path outputPath, CompilationMode mode, int minApiLevel) {
+    assert app != null;
+    assert mode != null;
+    assert minApiLevel > 0;
+    this.app = app;
+    this.outputPath = outputPath;
+    this.mode = mode;
+    this.minApiLevel = minApiLevel;
+    // Print options are not set.
+    printHelp = false;
+    printVersion = false;
+  }
+
+  public boolean isPrintHelp() {
+    return printHelp;
+  }
+
+  public boolean isPrintVersion() {
+    return printVersion;
+  }
+
+  // Internal access to the input resources.
+  AndroidApp getInputApp() {
+    return app;
+  }
+
+  // Internal access to the internal options.
+  abstract InternalOptions getInternalOptions();
+
+  public Path getOutputPath() {
+    return outputPath;
+  }
+
+  public CompilationMode getMode() {
+    return mode;
+  }
+
+  public int getMinApiLevel() {
+    return minApiLevel;
+  }
+
+  abstract static class Builder<C extends BaseCommand, B extends Builder<C, B>> {
+
+    private boolean printHelp = false;
+    private boolean printVersion = false;
+    private final AndroidApp.Builder app;
+    private Path outputPath = null;
+    private CompilationMode mode;
+    private int minApiLevel = Constants.DEFAULT_ANDROID_API;
+
+    protected Builder(CompilationMode mode) {
+      this(AndroidApp.builder(), mode);
+    }
+
+    // Internal constructor for testing.
+    Builder(AndroidApp app, CompilationMode mode) {
+      this(AndroidApp.builder(app), mode);
+    }
+
+    private Builder(AndroidApp.Builder builder, CompilationMode mode) {
+      assert mode != null;
+      this.app = builder;
+      this.mode = mode;
+    }
+
+    abstract B self();
+
+    public abstract C build() throws CompilationException, IOException;
+
+    // Internal accessor for the application resources.
+    AndroidApp.Builder getAppBuilder() {
+      return app;
+    }
+
+    /** Add program file resources. */
+    public B addProgramFiles(Path... files) throws IOException {
+      app.addProgramFiles(files);
+      return self();
+    }
+
+    /** Add program file resources. */
+    public B addProgramFiles(Collection<Path> files) throws IOException {
+      app.addProgramFiles(files);
+      return self();
+    }
+
+    /** Add classpath file resources. */
+    public B addClasspathFiles(Path... files) throws IOException {
+      app.addClasspathFiles(files);
+      return self();
+    }
+
+    /** Add classpath file resources. */
+    public B addClasspathFiles(Collection<Path> files) throws IOException {
+      app.addClasspathFiles(files);
+      return self();
+    }
+
+    /** Add library file resources. */
+    public B addLibraryFiles(Path... files) throws IOException {
+      app.addLibraryFiles(files);
+      return self();
+    }
+
+    /** Add library file resources. */
+    public B addLibraryFiles(List<Path> files) throws IOException {
+      app.addLibraryFiles(files);
+      return self();
+    }
+
+    /** Add Java-bytecode program-data. */
+    public B addClassProgramData(byte[]... data) {
+      app.addClassProgramData(data);
+      return self();
+    }
+
+    /** Add Java-bytecode program-data. */
+    public B addClassProgramData(Collection<byte[]> data) {
+      app.addClassProgramData(data);
+      return self();
+    }
+
+    /** Add dex program-data. */
+    public B addDexProgramData(byte[]... data) {
+      app.addDexProgramData(data);
+      return self();
+    }
+
+    /** Add dex program-data. */
+    public B addDexProgramData(Collection<byte[]> data) {
+      app.addDexProgramData(data);
+      return self();
+    }
+
+    /** Get current compilation mode. */
+    public CompilationMode getMode() {
+      return mode;
+    }
+
+    /** Set compilation mode. */
+    public B setMode(CompilationMode mode) {
+      assert mode != null;
+      this.mode = mode;
+      return self();
+    }
+
+    /** Get the output path. Null if not set. */
+    public Path getOutputPath() {
+      return outputPath;
+    }
+
+    /** Set an output path. Must be an existing directory or a non-existent zip file. */
+    public B setOutputPath(Path outputPath) throws CompilationException {
+      this.outputPath = FileUtils.validateOutputFile(outputPath);
+      return self();
+    }
+
+    /** Get the minimum API level (aka SDK version). */
+    public int getMinApiLevel() {
+      return minApiLevel;
+    }
+
+    /** Set the minimum required API level (aka SDK version). */
+    public B setMinApiLevel(int minApiLevel) {
+      assert minApiLevel > 0;
+      this.minApiLevel = minApiLevel;
+      return self();
+    }
+
+    /** Set the main-dex list file. */
+    public B setMainDexListFile(Path file) {
+      app.setMainDexListFile(file);
+      return self();
+    }
+
+    /** True if the print-help flag is enabled. */
+    public boolean isPrintHelp() {
+      return printHelp;
+    }
+
+    /** Set the value of the print-help flag. */
+    public B setPrintHelp(boolean printHelp) {
+      this.printHelp = printHelp;
+      return self();
+    }
+
+    /** True if the print-version flag is enabled. */
+    public boolean isPrintVersion() {
+      return printVersion;
+    }
+
+    /** Set the value of the print-version flag. */
+    public B setPrintVersion(boolean printVersion) {
+      this.printVersion = printVersion;
+      return self();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/BaseOutput.java b/src/main/java/com/android/tools/r8/BaseOutput.java
new file mode 100644
index 0000000..0f082c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BaseOutput.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+
+abstract class BaseOutput {
+
+  private final AndroidApp app;
+
+  BaseOutput(AndroidApp app) {
+    this.app = app;
+  }
+
+  // Internal access to the underlying app.
+  AndroidApp getAndroidApp() {
+    return app;
+  }
+
+  /**
+   * Get the list of compiled DEX resources.
+   *
+   * <p>The order of the list corresponds to the usual naming convention:
+   *
+   * <pre>
+   *   resources.get(0)     ~=~ classes.dex  (the main dex file)
+   *   resources.get(N - 1) ~=~ classesN.dex (where N > 0).
+   * </pre>
+   *
+   * @return list of compiled DEX resources.
+   */
+  public ImmutableList<Resource> getDexResources() {
+    return ImmutableList.copyOf(app.getDexProgramResources());
+  }
+
+  /**
+   * Write the output resources to a zip-archive or directory.
+   *
+   * @param output Path to existing directory or non-existing zip-archive.
+   */
+  abstract public void write(Path output) throws IOException;
+}
diff --git a/src/main/java/com/android/tools/r8/CompilationException.java b/src/main/java/com/android/tools/r8/CompilationException.java
new file mode 100644
index 0000000..4fd3654
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CompilationException.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+/**
+ * Exception to signal an compilation error.
+ *
+ * This is always an expected error and considered a user input issue.
+ * A user-understandable message must be provided.
+ */
+public class CompilationException extends Exception {
+  private static final long serialVersionUID = 1L;
+
+  /**
+   * Construct the exception with a {@link String} message.
+   * @param message the message
+   */
+  public CompilationException(String message) {
+    super(message);
+  }
+
+  /**
+   * Construct the exception with a {@link String} message and a {@link Throwable} cause.
+   * @param message the message
+   * @param cause the cause
+   */
+  public CompilationException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  /**
+   * Construct the exception with a {@link Throwable} cause.
+   * @param cause the cause
+   */
+  public CompilationException(Throwable cause) {
+    super(cause.getMessage(), cause);
+  }
+}
+
diff --git a/src/main/java/com/android/tools/r8/CompilationMode.java b/src/main/java/com/android/tools/r8/CompilationMode.java
new file mode 100644
index 0000000..f7aa4f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/CompilationMode.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+/** Compilation mode. */
+public enum CompilationMode {
+  /** Preserves debugging information during compilation, eg, line-numbers and locals. */
+  DEBUG,
+  /** Strips debugging information that cannot affect stack traces. */
+  RELEASE
+}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
new file mode 100644
index 0000000..4ff573d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -0,0 +1,197 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static com.android.tools.r8.D8Command.USAGE_MESSAGE;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * The D8 dex compiler.
+ *
+ * <p>D8 performs modular compilation to DEX bytecode. It supports compilation of Java bytecode and
+ * Android DEX bytecode to DEX bytecode including merging a mix of these input formats.
+ *
+ * <p>The D8 dexer API is intentionally limited and should "do the right thing" given a command. If
+ * this API does not suffice please contact the D8/R8 team.
+ *
+ * <p>The compiler is invoked by calling {@link #run(D8Command) D8.run} with an appropriate {@link
+ * D8Command}. For example:
+ *
+ * <pre>
+ *   D8.run(D8Command.builder()
+ *       .addProgramFiles(inputPathA, inputPathB)
+ *       .setOutputPath(outputPath)
+ *       .build());
+ * </pre>
+ *
+ * The above reads the input files denoted by {@code inputPathA} and {@code inputPathB}, compiles
+ * them to DEX bytecode (compiling from Java bytecode for such inputs and merging for DEX inputs),
+ * and then writes the result to the directory or zip archive specified by {@code outputPath}.
+ */
+public final class D8 {
+
+  private static final int STATUS_ERROR = 1;
+
+  private D8() {}
+
+  /**
+   * Main API entry for the D8 dexer.
+   *
+   * @param command D8 command.
+   * @return the compilation result.
+   */
+  public static D8Output run(D8Command command) throws IOException {
+    D8Output output = new D8Output(
+        runForTesting(command.getInputApp(), command.getInternalOptions()));
+    if (command.getOutputPath() != null) {
+      output.write(command.getOutputPath());
+    }
+    return output;
+  }
+
+  /**
+   * Main API entry for the D8 dexer.
+   *
+   * <p>The D8 dexer API is intentionally limited and should "do the right thing" given a set of
+   * inputs. If the API does not suffice please contact the R8 team.
+   *
+   * @param command D8 command.
+   * @param executor executor service from which to get threads for multi-threaded processing.
+   * @return the compilation result.
+   */
+  public static D8Output run(D8Command command, ExecutorService executor) throws IOException {
+    D8Output output = new D8Output(
+        runForTesting(command.getInputApp(), command.getInternalOptions(), executor));
+    if (command.getOutputPath() != null) {
+      output.write(command.getOutputPath());
+    }
+    return output;
+  }
+
+  private static void run(String[] args) throws IOException, CompilationException {
+    D8Command.Builder builder = D8Command.parse(args);
+    if (builder.getOutputPath() == null) {
+      builder.setOutputPath(Paths.get("."));
+    }
+    D8Command command = builder.build();
+    if (command.isPrintHelp()) {
+      System.out.println(USAGE_MESSAGE);
+      return;
+    }
+    if (command.isPrintVersion()) {
+      System.out.println("D8 v0.0.1");
+      return;
+    }
+    run(command);
+  }
+
+  /** Command-line entry to D8. */
+  public static void main(String[] args) throws IOException {
+    if (args.length == 0) {
+      System.err.println(USAGE_MESSAGE);
+      System.exit(STATUS_ERROR);
+    }
+    try {
+      run(args);
+    } catch (NoSuchFileException e) {
+      System.err.println("File not found: " + e.getFile());
+      System.exit(STATUS_ERROR);
+    } catch (FileAlreadyExistsException e) {
+      System.err.println("File already exists: " + e.getFile());
+      System.exit(STATUS_ERROR);
+    } catch (IOException e) {
+      System.err.println("Failed to read or write application files: " + e.getMessage());
+      System.exit(STATUS_ERROR);
+    } catch (RuntimeException e) {
+      System.err.println("Compilation failed with an internal error.");
+      Throwable cause = e.getCause() == null ? e : e.getCause();
+      cause.printStackTrace();
+      System.exit(STATUS_ERROR);
+    } catch (CompilationException e) {
+      System.err.println("Compilation failed: " + e.getMessage());
+      System.err.println(USAGE_MESSAGE);
+      System.exit(STATUS_ERROR);
+    }
+  }
+
+  static AndroidApp runForTesting(AndroidApp inputApp, InternalOptions options) throws IOException {
+    ExecutorService executor = ThreadUtils.getExecutorService(options);
+    try {
+      return runForTesting(inputApp, options, executor);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  static AndroidApp runForTesting(
+      AndroidApp inputApp, InternalOptions options, ExecutorService executor) throws IOException {
+    try {
+      assert !inputApp.hasPackageDistribution();
+
+      // Disable global optimizations.
+      options.skipMinification = true;
+      options.allowAccessModification = false;
+      options.inlineAccessors = false;
+      options.outline.enabled = false;
+
+      Timing timing = new Timing("DX timer");
+      DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
+      AppInfo appInfo = new AppInfo(app);
+      app = optimize(app, appInfo, options);
+
+      // If a method filter is present don't produce output since the application is likely partial.
+      if (!options.methodsFilter.isEmpty()) {
+        System.out.println("Finished compilation with method filter: ");
+        options.methodsFilter.forEach((m) -> System.out.println("  - " + m));
+        return null;
+      }
+
+      AndroidApp output =
+          new ApplicationWriter(app, appInfo, options, NamingLens.getIdentityLens(), null)
+              .write(null, executor);
+
+      options.printWarnings();
+      return output;
+    } catch (ExecutionException e) {
+      throw new RuntimeException(e.getMessage(), e.getCause());
+    }
+  }
+
+  private static DexApplication optimize(
+      DexApplication application, AppInfo appInfo, InternalOptions options)
+      throws IOException, ExecutionException {
+    final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
+
+    IRConverter converter = new IRConverter(application, appInfo, options, printer);
+    application = converter.convertToDex();
+
+    if (options.printCfg) {
+      if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
+        System.out.print(printer.toString());
+      } else {
+        java.io.FileWriter writer = new java.io.FileWriter(options.printCfgFile);
+        writer.write(printer.toString());
+        writer.close();
+      }
+    }
+    return application;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
new file mode 100644
index 0000000..5b12cf1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Immutable command structure for an invocation of the {@code D8} compiler.
+ *
+ * <p>To build a D8 command use the {@code D8Command.Builder} class. For example:
+ *
+ * <pre>
+ *   D8Command command = D8Command.builder()
+ *     .addProgramFiles(path1, path2)
+ *     .setMode(CompilationMode.RELEASE)
+ *     .build();
+ * </pre>
+ */
+public class D8Command extends BaseCommand {
+
+  /** Builder for constructing a D8Command. */
+  public static class Builder extends BaseCommand.Builder<D8Command, Builder> {
+
+    private Builder() {
+      super(CompilationMode.DEBUG);
+    }
+
+    private Builder(AndroidApp app) {
+      super(app, CompilationMode.DEBUG);
+    }
+
+    @Override
+    Builder self() {
+      return this;
+    }
+
+    /** Build the final D8Command. */
+    @Override
+    public D8Command build() throws CompilationException {
+      if (isPrintHelp() || isPrintVersion()) {
+        return new D8Command(isPrintHelp(), isPrintVersion());
+      }
+      return new D8Command(getAppBuilder().build(), getOutputPath(), getMode(), getMinApiLevel());
+    }
+  }
+
+  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+      "Usage: d8 [options] <input-files>",
+      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+      " and options are:",
+      "  --debug             # compile with debugging information (default enabled).",
+      "  --release           # compile without debugging information.",
+      "  --output <file>     # output result in <outfile>.",
+      "                      # <file> must be an existing directory or non-existent zip file.",
+      "  --lib <file>        # Add <file> as a library resource.",
+      "  --classpath <file>  # Add <file> as a classpath resource.",
+      "  --min-sdk-version   # minimum Android API level compatibility",
+      "  --version           # print the version of d8.",
+      "  --help              # print this message."));
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  // Internal builder to start from an existing AndroidApp.
+  static Builder builder(AndroidApp app) {
+    return new Builder(app);
+  }
+
+  public static Builder parse(String[] args) throws CompilationException, IOException {
+    CompilationMode modeSet = null;
+    Path outputPath = null;
+    Builder builder = builder();
+    for (int i = 0; i < args.length; i++) {
+      String arg = args[i];
+      if (arg.equals("--help")) {
+        builder.setPrintHelp(true);
+      } else if (arg.equals("--version")) {
+        builder.setPrintVersion(true);
+      } else if (arg.equals("--debug")) {
+        if (modeSet == CompilationMode.RELEASE) {
+          throw new CompilationException("Cannot compile in both --debug and --release mode.");
+        }
+        builder.setMode(CompilationMode.DEBUG);
+        modeSet = CompilationMode.DEBUG;
+      } else if (arg.equals("--release")) {
+        if (modeSet == CompilationMode.DEBUG) {
+          throw new CompilationException("Cannot compile in both --debug and --release mode.");
+        }
+        builder.setMode(CompilationMode.RELEASE);
+        modeSet = CompilationMode.RELEASE;
+      } else if (arg.equals("--output")) {
+        String output = args[++i];
+        if (outputPath != null) {
+          throw new CompilationException(
+              "Cannot output both to '" + outputPath.toString() + "' and '" + output + "'");
+        }
+        outputPath = Paths.get(output);
+      } else if (arg.equals("--lib")) {
+        builder.addLibraryFiles(Paths.get(args[++i]));
+      } else if (arg.equals("--classpath")) {
+        builder.addClasspathFiles(Paths.get(args[++i]));
+      } else if (arg.equals("--min-sdk-version")) {
+        builder.setMinApiLevel(Integer.valueOf(args[++i]));
+      } else {
+        if (arg.startsWith("--")) {
+          throw new CompilationException("Unknown option: " + arg);
+        }
+        builder.addProgramFiles(Paths.get(arg));
+      }
+    }
+    return builder.setOutputPath(outputPath);
+  }
+
+  private D8Command(AndroidApp inputApp, Path outputPath, CompilationMode mode, int minApiLevel) {
+    super(inputApp, outputPath, mode, minApiLevel);
+  }
+
+  private D8Command(boolean printHelp, boolean printVersion) {
+    super(printHelp, printVersion);
+  }
+
+  @Override
+  InternalOptions getInternalOptions() {
+    InternalOptions internal = new InternalOptions(new DexItemFactory());
+    internal.debug = getMode() == CompilationMode.DEBUG;
+    internal.minApiLevel = getMinApiLevel();
+    internal.fillDexFiles = true;
+    // Assert and fixup defaults.
+    assert !internal.skipMinification;
+    internal.skipMinification = true;
+    assert internal.useTreeShaking;
+    internal.useTreeShaking = false;
+    assert internal.interfaceMethodDesugaring == OffOrAuto.Off;
+    assert internal.allowAccessModification;
+    internal.allowAccessModification = false;
+    assert internal.inlineAccessors;
+    internal.inlineAccessors = false;
+    assert internal.outline.enabled;
+    internal.outline.enabled = false;
+    internal.lazyClasspathLoading = true;
+    internal.lazyLibraryLoading = true;
+    return internal;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/D8Output.java b/src/main/java/com/android/tools/r8/D8Output.java
new file mode 100644
index 0000000..84f8803
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/D8Output.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Path;
+
+/** Represents the output of a D8 compilation. */
+public class D8Output extends BaseOutput {
+
+  D8Output(AndroidApp app) {
+    super(app);
+  }
+
+  @Override
+  public void write(Path output) throws IOException {
+    getAndroidApp().write(output);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java
new file mode 100644
index 0000000..71daa5c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+public class Disassemble {
+  public static void main(String[] args)
+      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+    List<Path> files = Arrays.stream(args).map(s -> Paths.get(s)).collect(Collectors.toList());
+    R8Command command = R8Command.builder().addProgramFiles(files).build();
+    R8.disassemble(command);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/IncrementalDexingBenchmark.java
new file mode 100644
index 0000000..7b9d8aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/IncrementalDexingBenchmark.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.utils.ThreadUtils;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutorService;
+
+public class IncrementalDexingBenchmark {
+  private static final int ITERATIONS = 1000;
+
+  public static void compile(ExecutorService executor) throws IOException, CompilationException {
+    D8Output output =
+        D8.run(
+            D8Command.builder()
+                .addProgramFiles(Paths.get("build/test/examples/arithmetic.jar"))
+                .setMode(CompilationMode.DEBUG)
+                .build(),
+            executor);
+    if (output.getDexResources().size() != 1) {
+      throw new RuntimeException("WAT");
+    }
+  }
+
+  public static void main(String[] args) throws IOException, CompilationException {
+    int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
+    ExecutorService executor = ThreadUtils.getExecutorService(threads);
+    try {
+      long start = System.nanoTime();
+      for (int i = 0; i < ITERATIONS; i++) {
+        compile(executor);
+      }
+      double elapsedMs = (System.nanoTime() - start) / 1000000.0;
+      System.out.println("Runtime: " + elapsedMs + " ms");
+    } finally {
+      executor.shutdown();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/PrintClassList.java b/src/main/java/com/android/tools/r8/PrintClassList.java
new file mode 100644
index 0000000..6c49769
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/PrintClassList.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidApp.Builder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class PrintClassList {
+
+  public static void main(String[] args) throws IOException, ExecutionException {
+    List<String> dexFiles = Arrays.asList(args);
+    Builder builder = AndroidApp.builder();
+    if (args[0].endsWith("map")) {
+      builder.setProguardMapFile(Paths.get(args[0]));
+      dexFiles = dexFiles.subList(1, dexFiles.size());
+    }
+    builder.addProgramFiles(ListUtils.map(dexFiles, Paths::get));
+
+    ExecutorService executorService = Executors.newCachedThreadPool();
+    DexApplication application =
+        new ApplicationReader(builder.build(), new InternalOptions(), new Timing("PrintClassList"))
+            .read(executorService);
+    ClassNameMapper map = application.getProguardMap();
+    for (DexProgramClass clazz : application.classes()) {
+      System.out.print(maybeDeobfuscateType(map, clazz.type));
+      System.out.println();
+      printMethods(clazz.directMethods(), map);
+      printMethods(clazz.virtualMethods(), map);
+      printFields(clazz.staticFields(), map);
+      printFields(clazz.instanceFields(), map);
+    }
+    executorService.shutdown();
+  }
+
+  private static void printMethods(DexEncodedMethod[] methods, ClassNameMapper map) {
+    for (DexEncodedMethod encodedMethod : methods) {
+      DexMethod method = encodedMethod.method;
+
+      if (map != null) {
+        System.out.println(map.originalNameOf(method));
+      } else {
+        // Detour via Signature to get the same formatting.
+        MethodSignature signature = MethodSignature.fromDexMethod(method);
+        System.out.println(method.holder.toSourceString() + " " + signature);
+      }
+    }
+  }
+
+  private static void printFields(DexEncodedField[] fields, ClassNameMapper map) {
+    for (DexEncodedField encodedField : fields) {
+      DexField field = encodedField.field;
+      if (map != null) {
+        System.out.println(map.originalNameOf(field));
+      } else {
+        // Detour via Signature to get the same formatting.
+        FieldSignature signature = new FieldSignature(field.name.toSourceString(),
+            field.type.toSourceString());
+        System.out.println(field.clazz.toSourceString() + " " + signature);
+      }
+    }
+  }
+
+  private static String maybeDeobfuscateType(ClassNameMapper map, DexType type) {
+    return map == null ? type.toSourceString() : map.originalNameOf(type);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
new file mode 100644
index 0000000..44a687c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -0,0 +1,416 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static com.android.tools.r8.R8Command.USAGE_MESSAGE;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.ClassAndMemberPublicizer;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.Minifier;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.optimize.BridgeMethodAnalysis;
+import com.android.tools.r8.optimize.DebugStripper;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.optimize.VisibilityBridgeRemover;
+import com.android.tools.r8.shaking.AbstractMethodRemover;
+import com.android.tools.r8.shaking.AnnotationRemover;
+import com.android.tools.r8.shaking.DiscardedChecker;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.shaking.ProguardTypeMatcher;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.shaking.ReasonPrinter;
+import com.android.tools.r8.shaking.RootSetBuilder;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.shaking.TreePruner;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.AttributeRemovalOptions;
+import com.android.tools.r8.utils.PackageDistribution;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class R8 {
+
+  private final Timing timing = new Timing("R8");
+  private final InternalOptions options;
+
+  // TODO(zerny): Refactor tests to go through testing methods and make this private.
+  public R8(InternalOptions options) {
+    this.options = options;
+    options.itemFactory.resetSortedIndices();
+  }
+
+  public static AndroidApp writeApplication(
+      ExecutorService executorService,
+      DexApplication application,
+      AppInfo appInfo,
+      NamingLens namingLens,
+      byte[] proguardSeedsData,
+      PackageDistribution packageDistribution,
+      InternalOptions options)
+      throws ExecutionException {
+    try {
+      return new ApplicationWriter(application, appInfo, options, namingLens, proguardSeedsData)
+          .write(packageDistribution, executorService);
+    } catch (IOException e) {
+      throw new RuntimeException("Cannot write dex application", e);
+    }
+  }
+
+  public DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo)
+      throws IOException, ProguardRuleParserException, ExecutionException {
+    return optimize(application, appInfo, GraphLense.getIdentityLense(),
+        Executors.newSingleThreadExecutor());
+  }
+
+  public DexApplication optimize(DexApplication application, AppInfoWithSubtyping appInfo,
+      GraphLense graphLense, ExecutorService executorService)
+      throws IOException, ProguardRuleParserException, ExecutionException {
+    final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
+
+    timing.begin("Create IR");
+    try {
+      IRConverter converter = new IRConverter(
+          timing, application, appInfo, options, printer, graphLense);
+      application = converter.optimize(executorService);
+    } finally {
+      timing.end();
+    }
+
+    if (!options.skipDebugInfoOpt && (application.getProguardMap() != null)) {
+      try {
+        timing.begin("DebugStripper");
+        DebugStripper stripper = new DebugStripper(application.getProguardMap(), options);
+        application.classes().forEach(stripper::processClass);
+      } finally {
+        timing.end();
+      }
+    }
+
+    if (options.printCfg) {
+      if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
+        System.out.print(printer.toString());
+      } else {
+        java.io.FileWriter writer = new java.io.FileWriter(options.printCfgFile);
+        writer.write(printer.toString());
+        writer.close();
+      }
+    }
+    return application;
+  }
+
+  private Set<DexType> filterMissingClasses(Set<DexType> missingClasses,
+      Set<ProguardTypeMatcher> dontWarnPatterns) {
+    Set<DexType> result = new HashSet<>(missingClasses);
+    for (ProguardTypeMatcher matcher : dontWarnPatterns) {
+      if (matcher instanceof MatchSpecificType) {
+        result.remove(((MatchSpecificType) matcher).type);
+      } else {
+        result.removeIf(matcher::matches);
+      }
+    }
+    return result;
+  }
+
+  public static void disassemble(R8Command command) throws IOException, ExecutionException {
+    Path output = command.getOutputPath();
+    AndroidApp app = command.getInputApp();
+    InternalOptions options = command.getInternalOptions();
+    ExecutorService executor = ThreadUtils.getExecutorService(options);
+    Timing timing = new Timing("disassemble");
+    try {
+      DexApplication application = new ApplicationReader(app, options, timing).read(executor);
+      if (options.useSmaliSyntax) {
+        if (output != null) {
+          Files.createDirectories(output);
+          try (PrintStream ps = new PrintStream(
+              Files.newOutputStream(output.resolve("classes.smali")))) {
+            application.smali(options, ps);
+          }
+        } else {
+          application.smali(options, System.out);
+        }
+      } else {
+        application.disassemble(output, options);
+      }
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  static AndroidApp runForTesting(AndroidApp app, InternalOptions options)
+      throws ProguardRuleParserException, ExecutionException, IOException {
+    ExecutorService executor = ThreadUtils.getExecutorService(options);
+    try {
+      return runForTesting(app, options, executor);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  static AndroidApp runForTesting(AndroidApp app, InternalOptions options, ExecutorService executor)
+      throws ProguardRuleParserException, ExecutionException, IOException {
+    return new R8(options).run(app, executor);
+  }
+
+  private AndroidApp run(AndroidApp inputApp, ExecutorService executorService)
+      throws IOException, ExecutionException, ProguardRuleParserException {
+    if (options.quiet) {
+      System.setOut(new PrintStream(ByteStreams.nullOutputStream()));
+    }
+    try {
+      DexApplication application =
+          new ApplicationReader(inputApp, options, timing).read(executorService);
+
+      AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+      RootSet rootSet;
+      byte[] proguardSeedsData = null;
+      timing.begin("Strip unused code");
+      try {
+        Set<DexType> missingClasses = appInfo.getMissingClasses();
+        missingClasses = filterMissingClasses(missingClasses, options.dontWarnPatterns);
+        if (!missingClasses.isEmpty()) {
+          System.err.println();
+          System.err.println("WARNING, some classes are missing:");
+          missingClasses.forEach(clazz -> {
+            System.err.println(" - " + clazz.toSourceString());
+          });
+          if (!options.ignoreMissingClasses) {
+            throw new CompilationError(
+                "Shrinking can't be performed because some library classes are missing.");
+          }
+        }
+        rootSet = new RootSetBuilder(application, options.keepRules).run(executorService);
+        Enqueuer enqueuer = new Enqueuer(rootSet, appInfo);
+        appInfo = enqueuer.run(timing);
+        if (options.printSeeds) {
+          ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+          PrintStream out = new PrintStream(bytes);
+          RootSetBuilder.writeSeeds(appInfo.withLiveness().pinnedItems, out);
+          out.flush();
+          proguardSeedsData = bytes.toByteArray();
+        }
+        if (options.useTreeShaking) {
+          application = new TreePruner(application, appInfo.withLiveness(), options).run();
+          // Recompute the subtyping information.
+          appInfo = appInfo.withLiveness().prunedCopyFrom(application);
+          new AbstractMethodRemover(appInfo).run();
+          new AnnotationRemover(appInfo.withLiveness(), options).run();
+        } else if (!options.skipMinification) {
+          // TODO(38188583): Ensure signatures are removed when minifying.
+          new AnnotationRemover(appInfo.withLiveness(), true,
+              AttributeRemovalOptions.filterOnlySignatures());
+        }
+      } finally {
+        timing.end();
+      }
+
+      if (options.allowAccessModification) {
+        ClassAndMemberPublicizer.run(application);
+        // We can now remove visibility bridges. Note that we do not need to update the
+        // invoke-targets here, as the existing invokes will simply dispatch to the now
+        // visible super-method. MemberRebinding, if run, will then dispatch it correctly.
+        application = new VisibilityBridgeRemover(appInfo, application).run();
+      }
+
+      GraphLense graphLense = GraphLense.getIdentityLense();
+
+      if (appInfo.withLiveness() != null) {
+        // No-op until class merger is added.
+        appInfo = appInfo.withLiveness().rewrittenWithLense(graphLense);
+        graphLense = new MemberRebindingAnalysis(appInfo.withLiveness(), graphLense).run();
+      }
+
+      graphLense = new BridgeMethodAnalysis(graphLense, appInfo.withSubtyping()).run();
+
+      application = optimize(application, appInfo, graphLense, executorService);
+      appInfo = new AppInfoWithSubtyping(application);
+
+      if (options.useTreeShaking || !options.skipMinification) {
+        timing.begin("Post optimization code stripping");
+        try {
+          Enqueuer enqueuer = new Enqueuer(rootSet, appInfo);
+          appInfo = enqueuer.run(timing);
+          if (options.useTreeShaking) {
+            application = new TreePruner(application, appInfo.withLiveness(), options).run();
+            appInfo = appInfo.withLiveness().prunedCopyFrom(application);
+            // Print reasons on the application after pruning, so that we reflect the actual result.
+            ReasonPrinter reasonPrinter = enqueuer.getReasonPrinter(rootSet.reasonAsked);
+            reasonPrinter.run(application);
+          }
+        } finally {
+          timing.end();
+        }
+      }
+
+      if (!rootSet.checkDiscarded.isEmpty()) {
+        new DiscardedChecker(rootSet, application).run();
+      }
+
+      timing.begin("Minification");
+      // If we did not have keep rules, everything will be marked as keep, so no minification
+      // will happen. Just avoid the overhead.
+      NamingLens namingLens =
+          options.skipMinification
+              ? NamingLens.getIdentityLens()
+              : new Minifier(appInfo.withLiveness(), rootSet, options).run(timing);
+      timing.end();
+
+      // If a method filter is present don't produce output since the application is likely partial.
+      if (!options.methodsFilter.isEmpty()) {
+        System.out.println("Finished compilation with method filter: ");
+        options.methodsFilter.forEach((m) -> System.out.println("  - " + m));
+        return null;
+      }
+
+      PackageDistribution packageDistribution = null;
+      if (inputApp.hasPackageDistribution()) {
+        try (Closer closer = Closer.create()) {
+          packageDistribution = PackageDistribution.load(inputApp.getPackageDistribution(closer));
+        }
+      }
+
+      // Generate the resulting application resources.
+      AndroidApp androidApp =
+          writeApplication(
+              executorService,
+              application,
+              appInfo,
+              namingLens,
+              proguardSeedsData,
+              packageDistribution,
+              options);
+
+      if (options.printMapping && androidApp.hasProguardMap() && !options.skipMinification) {
+        Path mapOutput = options.printMappingFile;
+        try (Closer closer = Closer.create()) {
+          OutputStream mapOut;
+          if (mapOutput == null) {
+            mapOut = System.out;
+          } else {
+            mapOut = new FileOutputStream(mapOutput.toFile());
+            closer.register(mapOut);
+          }
+          androidApp.writeProguardMap(closer, mapOut);
+        }
+      }
+      options.printWarnings();
+      return androidApp;
+    } finally {
+      // Dump timings.
+      if (options.printTimes) {
+        timing.report();
+      }
+    }
+  }
+
+  /**
+   * Main API entry for the R8 compiler.
+   *
+   * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+   * API does not suffice please contact the R8 team.
+   *
+   * @param command R8 command.
+   * @return the compilation result.
+   */
+  public static AndroidApp run(R8Command command)
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    AndroidApp outputApp = runForTesting(command.getInputApp(), command.getInternalOptions());
+    if (command.getOutputPath() != null) {
+      outputApp.write(command.getOutputPath());
+    }
+    return outputApp;
+  }
+
+  /**
+   * Main API entry for the R8 compiler.
+   *
+   * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this
+   * API does not suffice please contact the R8 team.
+   *
+   * @param command R8 command.
+   * @param executor executor service from which to get threads for multi-threaded processing.
+   * @return the compilation result.
+   */
+  public static AndroidApp run(R8Command command, ExecutorService executor)
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    AndroidApp outputApp =
+        runForTesting(command.getInputApp(), command.getInternalOptions(), executor);
+    if (command.getOutputPath() != null) {
+      outputApp.write(command.getOutputPath());
+    }
+    return outputApp;
+  }
+
+  private static void run(String[] args)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    R8Command.Builder builder = R8Command.parse(args);
+    if (builder.getOutputPath() == null) {
+      builder.setOutputPath(Paths.get("."));
+    }
+    R8Command command = builder.build();
+    if (command.isPrintHelp()) {
+      System.out.println(USAGE_MESSAGE);
+      return;
+    }
+    if (command.isPrintVersion()) {
+      System.out.println("R8 v0.0.1");
+      return;
+    }
+    run(command);
+  }
+
+  public static void main(String[] args) {
+    try {
+      run(args);
+    } catch (NoSuchFileException e) {
+      System.err.println("File not found: " + e.getFile());
+      System.exit(1);
+    } catch (FileAlreadyExistsException e) {
+      System.err.println("File already exists: " + e.getFile());
+    } catch (IOException e) {
+      System.err.println("Failed to read or write Android app: " + e.getMessage());
+      System.exit(1);
+    } catch (ProguardRuleParserException e) {
+      System.err.println("Failed parsing proguard keep rules: " + e.getMessage());
+      System.exit(1);
+    } catch (RuntimeException | ExecutionException e) {
+      System.err.println("Compilation failed with an internal error.");
+      Throwable cause = e.getCause() == null ? e : e.getCause();
+      cause.printStackTrace();
+      System.exit(1);
+    } catch (CompilationException e) {
+      System.err.println("Compilation failed: " + e.getMessage());
+      System.err.println(USAGE_MESSAGE);
+      System.exit(1);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
new file mode 100644
index 0000000..7343fba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -0,0 +1,313 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+public class R8Command extends BaseCommand {
+
+  public static class Builder extends BaseCommand.Builder<R8Command, Builder> {
+
+    private final List<Path> proguardConfigFiles = new ArrayList<>();
+    private Optional<Boolean> treeShaking = Optional.empty();
+    private Optional<Boolean> minification = Optional.empty();
+    private boolean ignoreMissingClasses = false;
+
+    private Builder() {
+      super(CompilationMode.RELEASE);
+    }
+
+    private Builder(AndroidApp app) {
+      super(app, CompilationMode.RELEASE);
+    }
+
+    @Override
+    Builder self() {
+      return this;
+    }
+
+    /** Enable/disable tree shaking. This overrides any settings in proguard configuration files. */
+    public Builder setTreeShaking(boolean useTreeShaking) {
+      treeShaking = Optional.of(useTreeShaking);
+      return this;
+    }
+
+    /** Enable/disable minification. This overrides any settings in proguard configuration files. */
+    public Builder setMinification(boolean useMinification) {
+      minification = Optional.of(useMinification);
+      return this;
+    }
+
+    /** Add proguard configuration file resources. */
+    public Builder addProguardConfigurationFiles(Path... paths) {
+      Collections.addAll(proguardConfigFiles, paths);
+      return this;
+    }
+
+    /** Add proguard configuration file resources. */
+    public Builder addProguardConfigurationFiles(List<Path> paths) {
+      proguardConfigFiles.addAll(paths);
+      return this;
+    }
+
+    /** Set a proguard mapping file resource. */
+    public Builder setProguardMapFile(Path path) {
+      getAppBuilder().setProguardMapFile(path);
+      return this;
+    }
+
+    /** Set a package distribution file resource. */
+    public Builder setPackageDistributionFile(Path path) {
+      getAppBuilder().setPackageDistributionFile(path);
+      return this;
+    }
+
+    /**
+     * Deprecated flag to avoid failing if classes are missing during compilation.
+     *
+     * <p>TODO: Make compilation safely assume this flag to be true and remove the flag.
+     */
+    Builder setIgnoreMissingClasses(boolean ignoreMissingClasses) {
+      this.ignoreMissingClasses = ignoreMissingClasses;
+      return this;
+    }
+
+    @Override
+    public R8Command build() throws CompilationException, IOException {
+      // If printing versions ignore everything else.
+      if (isPrintHelp() || isPrintVersion()) {
+        return new R8Command(isPrintHelp(), isPrintVersion());
+      }
+
+      DexItemFactory factory = new DexItemFactory();
+      ProguardConfiguration configuration;
+      if (proguardConfigFiles.isEmpty()) {
+        configuration = ProguardConfiguration.defaultConfiguration(factory);
+      } else {
+        ProguardConfigurationParser parser = new ProguardConfigurationParser(factory);
+        try {
+          parser.parse(proguardConfigFiles);
+        } catch (ProguardRuleParserException e) {
+          throw new CompilationException(e.getMessage(), e.getCause());
+        }
+        configuration = parser.getConfig();
+        addProgramFiles(configuration.getInjars());
+        addLibraryFiles(configuration.getLibraryjars());
+      }
+
+      boolean useTreeShaking = treeShaking.orElse(configuration.isShrinking());
+      boolean useMinification = minification.orElse(configuration.isObfuscating());
+
+      return new R8Command(
+          getAppBuilder().build(),
+          getOutputPath(),
+          configuration,
+          getMode(),
+          getMinApiLevel(),
+          useTreeShaking,
+          useMinification,
+          ignoreMissingClasses);
+    }
+  }
+
+  // Internal state to verify parsing properties not enforced by the builder.
+  private static class ParseState {
+    CompilationMode mode = null;
+  }
+
+  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+      "Usage: r8 [options] <input-files>",
+      " where <input-files> are any combination of dex, class, zip, jar, or apk files",
+      " and options are:",
+      "  --debug             # Compile with debugging information (default enabled).",
+      "  --release           # Compile without debugging information.",
+      "  --output <file>     # Output result in <file>.",
+      "                      # <file> must be an existing directory or non-existent zip file.",
+      "  --lib <file>        # Add <file> as a library resource.",
+      "  --min-sdk-version   # Minimum Android API level compatibility.",
+      "  --pg-conf <file>    # Proguard configuration <file> (implies tree shaking/minification).",
+      "  --pg-map <file>     # Proguard map <file>.",
+      "  --no-tree-shaking   # Force disable tree shaking of unreachable classes.",
+      "  --no-minification   # Force disable minification of names.",
+      "  --version           # Print the version of r8.",
+      "  --help              # Print this message."));
+
+  private final ProguardConfiguration proguardConfiguration;
+  private final boolean useTreeShaking;
+  private final boolean useMinification;
+  private final boolean ignoreMissingClasses;
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  // Internal builder to start from an existing AndroidApp.
+  static Builder builder(AndroidApp app) {
+    return new Builder(app);
+  }
+
+  public static Builder parse(String[] args) throws CompilationException, IOException {
+    Builder builder = builder();
+    parse(args, builder, new ParseState());
+    return builder;
+  }
+
+  private static ParseState parse(String[] args, Builder builder, ParseState state)
+      throws CompilationException, IOException {
+    for (int i = 0; i < args.length; i++) {
+      String arg = args[i];
+      if (arg.equals("--help")) {
+        builder.setPrintHelp(true);
+      } else if (arg.equals("--version")) {
+        builder.setPrintVersion(true);
+      } else if (arg.equals("--debug")) {
+        if (state.mode == CompilationMode.RELEASE) {
+          throw new CompilationException("Cannot compile in both --debug and --release mode.");
+        }
+        state.mode = CompilationMode.DEBUG;
+        builder.setMode(state.mode);
+      } else if (arg.equals("--release")) {
+        if (state.mode == CompilationMode.DEBUG) {
+          throw new CompilationException("Cannot compile in both --debug and --release mode.");
+        }
+        state.mode = CompilationMode.RELEASE;
+        builder.setMode(state.mode);
+      } else if (arg.equals("--output")) {
+        String outputPath = args[++i];
+        if (builder.getOutputPath() != null) {
+          throw new CompilationException(
+              "Cannot output both to '"
+                  + builder.getOutputPath().toString()
+                  + "' and '"
+                  + outputPath
+                  + "'");
+        }
+        builder.setOutputPath(Paths.get(outputPath));
+      } else if (arg.equals("--lib")) {
+        builder.addLibraryFiles(Paths.get(args[++i]));
+      } else if (arg.equals("--min-sdk-version")) {
+        builder.setMinApiLevel(Integer.valueOf(args[++i]));
+      } else if (arg.equals("--no-tree-shaking")) {
+        builder.setTreeShaking(false);
+      } else if (arg.equals("--no-minification")) {
+        builder.setMinification(false);
+      } else if (arg.equals("--pg-conf")) {
+        builder.addProguardConfigurationFiles(Paths.get(args[++i]));
+      } else if (arg.equals("--pg-map")) {
+        builder.setProguardMapFile(Paths.get(args[++i]));
+      } else if (arg.equals("--ignore-missing-classes")) {
+        builder.setIgnoreMissingClasses(true);
+      } else if (arg.startsWith("@")) {
+        // TODO(zerny): Replace this with pipe reading.
+        String argsFile = arg.substring(1);
+        try {
+          List<String> linesInFile = FileUtils.readTextFile(Paths.get(argsFile));
+          List<String> argsInFile = new ArrayList<>();
+          for (String line : linesInFile) {
+            for (String word : line.split("\\s")) {
+              String trimmed = word.trim();
+              if (!trimmed.isEmpty()) {
+                argsInFile.add(trimmed);
+              }
+            }
+          }
+          // TODO(zerny): We need to define what CWD should be for files referenced in an args file.
+          state = parse(argsInFile.toArray(new String[argsInFile.size()]), builder, state);
+        } catch (IOException | CompilationException e) {
+          throw new CompilationException(
+              "Failed to read arguments from file " + argsFile + ": " + e.getMessage());
+        }
+      } else {
+        if (arg.startsWith("--")) {
+          throw new CompilationException("Unknown option: " + arg);
+        }
+        builder.addProgramFiles(Paths.get(arg));
+      }
+    }
+    return state;
+  }
+
+  private R8Command(
+      AndroidApp inputApp,
+      Path outputPath,
+      ProguardConfiguration proguardConfiguration,
+      CompilationMode mode,
+      int minApiLevel,
+      boolean useTreeShaking,
+      boolean useMinification,
+      boolean ignoreMissingClasses) {
+    super(inputApp, outputPath, mode, minApiLevel);
+    assert proguardConfiguration != null;
+    this.proguardConfiguration = proguardConfiguration;
+    this.useTreeShaking = useTreeShaking;
+    this.useMinification = useMinification;
+    this.ignoreMissingClasses = ignoreMissingClasses;
+  }
+
+  private R8Command(boolean printHelp, boolean printVersion) {
+    super(printHelp, printVersion);
+    proguardConfiguration = null;
+    useTreeShaking = false;
+    useMinification = false;
+    ignoreMissingClasses = false;
+  }
+
+  public boolean useTreeShaking() {
+    return useTreeShaking;
+  }
+
+  public boolean useMinification() {
+    return useMinification;
+  }
+
+  InternalOptions getInternalOptions() {
+    InternalOptions internal = new InternalOptions(proguardConfiguration.getDexItemFactory());
+    internal.debug = getMode() == CompilationMode.DEBUG;
+    internal.minApiLevel = getMinApiLevel();
+    internal.skipMinification = !useMinification();
+    internal.useTreeShaking = useTreeShaking();
+    internal.ignoreMissingClasses = ignoreMissingClasses;
+
+    // TODO(zerny): Consider which other proguard options should be given flags.
+    internal.packagePrefix = proguardConfiguration.getPackagePrefix();
+    internal.allowAccessModification = proguardConfiguration.getAllowAccessModification();
+    for (String pattern : proguardConfiguration.getAttributesRemovalPatterns()) {
+      internal.attributeRemoval.applyPattern(pattern);
+    }
+    if (proguardConfiguration.isIgnoreWarnings()) {
+      internal.ignoreMissingClasses = true;
+    }
+    if (proguardConfiguration.getSeedFile() != null) {
+      internal.seedsFile = proguardConfiguration.getSeedFile();
+    }
+    if (proguardConfiguration.isVerbose()) {
+      internal.verbose = true;
+    }
+    if (!proguardConfiguration.isObfuscating()) {
+      internal.skipMinification = true;
+    }
+    internal.printSeeds |= proguardConfiguration.getPrintSeeds();
+    internal.printMapping |= proguardConfiguration.isPrintingMapping();
+    internal.printMappingFile = proguardConfiguration.getPrintMappingOutput();
+    internal.classObfuscationDictionary = proguardConfiguration.getClassObfuscationDictionary();
+    internal.obfuscationDictionary = proguardConfiguration.getObfuscationDictionary();
+    internal.keepRules = proguardConfiguration.getRules();
+    internal.dontWarnPatterns = proguardConfiguration.getDontWarnPatterns();
+    return internal;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ReadKeepFile.java b/src/main/java/com/android/tools/r8/ReadKeepFile.java
new file mode 100644
index 0000000..355d42c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ReadKeepFile.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationParser;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+
+/**
+ * Benchmark for testing ability to and speed of parsing Proguard keep files.
+ */
+public class ReadKeepFile {
+
+  private static final String DEFAULT_KEEP_FILE_NAME = "build/proguard.cfg";
+
+  final Timing timing = new Timing("ReadKeepFile");
+
+  private void readProguardKeepFile(String fileName) throws ProguardRuleParserException {
+    try {
+      System.out.println("  - reading " + fileName);
+      timing.begin("Reading " + fileName);
+      new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(fileName));
+      timing.end();
+    } catch (IOException e) {
+      System.err.print("Failed to parse Proguard keep file: " + e.getMessage());
+    }
+  }
+
+  public static void main(String[] args) throws ProguardRuleParserException {
+    new ReadKeepFile().run(args);
+  }
+
+  private void run(String[] args) throws ProguardRuleParserException {
+    System.out.println("ProguardKeepRuleParser benchmark.");
+    if (args.length == 0) {
+      readProguardKeepFile(DEFAULT_KEEP_FILE_NAME);
+    } else {
+      for (String name : args) {
+        readProguardKeepFile(name);
+      }
+    }
+    timing.report();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ReadProguardMap.java b/src/main/java/com/android/tools/r8/ReadProguardMap.java
new file mode 100644
index 0000000..2ea2ca7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ReadProguardMap.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+
+/**
+ * Benchmark for testing speed of parsing Proguard mapping files.
+ */
+public class ReadProguardMap {
+
+  private static final String DEFAULT_MAP_FILE_NAME = "third_party/gmscore/v5/proguard.map";
+
+  final Timing timing = new Timing("ReadProguardMap");
+
+  private void readProguardMapFile(String fileName) {
+    try {
+      System.out.println("  - reading " + fileName);
+      timing.begin("Reading " + fileName);
+      ProguardMapReader.mapperFromFile(Paths.get(fileName));
+      timing.end();
+    } catch (IOException e) {
+      System.err.print("Failed to parse Proguard mapping file: " + e.getMessage());
+    }
+  }
+
+  public static void main(String[] args) {
+    new ReadProguardMap().run(args);
+  }
+
+  private void run(String[] args) {
+    System.out.println("ReadProguardMap benchmark.");
+    if (args.length == 0) {
+      readProguardMapFile(DEFAULT_MAP_FILE_NAME);
+    } else {
+      Arrays.asList(args).forEach((String name) -> readProguardMapFile(name));
+    }
+    timing.report();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/Resource.java b/src/main/java/com/android/tools/r8/Resource.java
new file mode 100644
index 0000000..8ea23d1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/Resource.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Represents application resources. */
+public abstract class Resource {
+
+  /** Application resource kind. */
+  public enum Kind {
+    PROGRAM, CLASSPATH, LIBRARY
+  }
+
+  /** Kind of the resource. */
+  public final Kind kind;
+
+  protected Resource(Kind kind) {
+    this.kind = kind;
+  }
+
+  /** Get the resource as a stream. */
+  public abstract InputStream getStream(Closer closer) throws IOException;
+}
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
new file mode 100644
index 0000000..b48f268
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -0,0 +1,203 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.bisect;
+
+import com.android.tools.r8.bisect.BisectOptions.Result;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.io.CharStreams;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class Bisect {
+
+  private final BisectOptions options;
+  private final DexItemFactory factory = new DexItemFactory();
+  private final Timing timing = new Timing("bisect");
+
+  public interface Command {
+
+    Result apply(DexApplication application) throws Exception;
+  }
+
+  private static class StreamReader implements Runnable {
+
+    private InputStream stream;
+    private String result;
+
+    public StreamReader(InputStream stream) {
+      this.stream = stream;
+    }
+
+    public String getResult() {
+      return result;
+    }
+
+    @Override
+    public void run() {
+      try {
+        result = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
+        stream.close();
+      } catch (IOException e) {
+        result = "Failed reading result for stream " + stream;
+      }
+    }
+  }
+
+  public Bisect(BisectOptions options) {
+    this.options = options;
+  }
+
+  public static DexProgramClass run(BisectState state, Command command, Path output,
+      ExecutorService executor)
+      throws Exception {
+    while (true) {
+      DexApplication app = state.bisect();
+      state.write();
+      if (app == null) {
+        return state.getFinalClass();
+      }
+      if (command == null) {
+        writeApp(app, output, executor);
+        System.out.println("Bisecting completed with build in " + output + "/");
+        System.out.println("Continue bisection by passing either --"
+            + BisectOptions.RESULT_GOOD_FLAG + " or --"
+            + BisectOptions.RESULT_BAD_FLAG);
+        return null;
+      }
+      state.setPreviousResult(command.apply(app));
+    }
+  }
+
+  public DexProgramClass run() throws Exception {
+    // Setup output directory (or write to a temp dir).
+    Path output;
+    if (options.output != null) {
+      output = options.output.toPath();
+    } else {
+      File temp = File.createTempFile("bisect", "", new File("/tmp"));
+      temp.delete();
+      temp.mkdir();
+      output = temp.toPath();
+    }
+
+    ExecutorService executor = Executors.newWorkStealingPool();
+    try {
+      DexApplication goodApp = readApp(options.goodBuild, executor);
+      DexApplication badApp = readApp(options.badBuild, executor);
+
+      File stateFile = options.stateFile != null
+          ? options.stateFile
+          : output.resolve("bisect.state").toFile();
+
+      // Setup initial (or saved) bisection state.
+      BisectState state = new BisectState(goodApp, badApp, stateFile);
+      if (options.stateFile != null) {
+        state.read();
+      }
+
+      // If a "previous" result is supplied on the command line, record it.
+      if (options.result != Result.UNKNOWN) {
+        state.setPreviousResult(options.result);
+      }
+
+      // Setup post-build command.
+      Command command = null;
+      if (options.command != null) {
+        command = (application) -> {
+          writeApp(application, output, executor);
+          return runCommand(output);
+        };
+      }
+
+      // Run bisection.
+      return run(state, command, output, executor);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  private Result runCommand(Path output) throws IOException {
+    List<String> args = new ArrayList<>();
+    args.add("/bin/bash");
+    args.add(options.command.toString());
+    args.add(output.toString());
+    ProcessBuilder builder = new ProcessBuilder(args);
+    Process process = builder.start();
+    StreamReader stdoutReader = new StreamReader(process.getInputStream());
+    StreamReader stderrReader = new StreamReader(process.getErrorStream());
+    Thread stdoutThread = new Thread(stdoutReader);
+    Thread stderrThread = new Thread(stderrReader);
+    stdoutThread.start();
+    stderrThread.start();
+    try {
+      process.waitFor();
+      stdoutThread.join();
+      stderrThread.join();
+    } catch (InterruptedException e) {
+      throw new RuntimeException("Execution interrupted", e);
+    }
+    int result = process.exitValue();
+    if (result == 0) {
+      return Result.GOOD;
+    } else if (result == 1) {
+      return Result.BAD;
+    }
+    System.out.println("Failed to run command " + args);
+    System.out.println("Exit code: " + result + " (expected 0 for good, 1 for bad)");
+    System.out.println("Std out:\n" + stdoutReader.getResult());
+    System.out.println("Std err:\n" + stderrReader.getResult());
+    throw new CompilationError("Failed to run command " + args);
+  }
+
+  private DexApplication readApp(File apk, ExecutorService executor)
+      throws IOException, ExecutionException {
+    AndroidApp app = AndroidApp.fromProgramFiles(apk.toPath());
+    return new ApplicationReader(app, new InternalOptions(), timing).read(executor);
+  }
+
+  private static void writeApp(DexApplication app, Path output, ExecutorService executor)
+      throws IOException, ExecutionException {
+    InternalOptions options = new InternalOptions();
+    AppInfo appInfo = new AppInfo(app);
+    ApplicationWriter writer = new ApplicationWriter(app, appInfo, options, null, null);
+    AndroidApp outApp = writer.write(null, executor);
+    outApp.writeToDirectory(output);
+  }
+
+  public static void main(String[] args) throws Exception {
+    BisectOptions options = null;
+    try {
+      options = BisectOptions.parse(args);
+    } catch (CompilationError e) {
+      System.err.println(e.getMessage());
+      BisectOptions.printHelp(System.err);
+      return;
+    }
+    if (options == null) {
+      return;
+    }
+    DexProgramClass clazz = new Bisect(options).run();
+    if (clazz != null) {
+      System.out.println("Bisection found final bad class " + clazz);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectOptions.java b/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
new file mode 100644
index 0000000..d0051dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.bisect;
+
+import com.android.tools.r8.errors.CompilationError;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+public class BisectOptions {
+  private static final String HELP_FLAG = "help";
+  public static final String BUILD_GOOD_FLAG = "good";
+  public static final String BUILD_BAD_FLAG = "bad";
+  public static final String RESULT_GOOD_FLAG = "result-good";
+  public static final String RESULT_BAD_FLAG = "result-bad";
+  public static final String STATE_FLAG = "state";
+  public static final String OUTPUT_FLAG = "output";
+  public static final String COMMAND_FLAG = "command";
+
+  public final File goodBuild;
+  public final File badBuild;
+  public final File stateFile;
+  public final File command;
+  public final File output;
+  public final Result result;
+
+  public enum Result { UNKNOWN, GOOD, BAD }
+
+  private static class ParserSpec {
+    OptionSpec<String> goodBuild;
+    OptionSpec<String> badBuild;
+    OptionSpec<String> command;
+    OptionSpec<String> stateFile;
+    OptionSpec<String> output;
+    OptionSpec<Void> resultGood;
+    OptionSpec<Void> resultBad;
+    OptionSpec<Void> help;
+
+    void init(OptionParser parser) {
+      help = parser.accepts(HELP_FLAG).forHelp();
+      resultGood = parser.accepts(RESULT_GOOD_FLAG, "Bisect again assuming previous run was good.");
+      resultBad = parser.accepts(RESULT_BAD_FLAG, "Bisect again assuming previous run was bad.");
+      goodBuild = parser.accepts(BUILD_GOOD_FLAG, "Known good APK.")
+          .withRequiredArg()
+          .describedAs("apk");
+      badBuild = parser.accepts(BUILD_BAD_FLAG, "Known bad APK.")
+          .withRequiredArg()
+          .describedAs("apk");
+      stateFile = parser.accepts(STATE_FLAG, "Bisection state.")
+          .requiredIf(resultGood, resultBad)
+          .withRequiredArg()
+          .describedAs("file");
+      output = parser.accepts(OUTPUT_FLAG, "Output directory.")
+          .withRequiredArg()
+          .describedAs("dir");
+      command = parser.accepts(COMMAND_FLAG, "Command to run after each bisection.")
+          .requiredUnless(stateFile)
+          .withRequiredArg()
+          .describedAs("file");
+    }
+
+    OptionSet parse(String[] args) {
+      OptionParser parser = new OptionParser();
+      init(parser);
+      return parser.parse(args);
+    }
+
+    static void printHelp(OutputStream out) throws IOException {
+      OptionParser parser = new OptionParser();
+      new ParserSpec().init(parser);
+      parser.printHelpOn(out);
+    }
+  }
+
+  private BisectOptions(File goodBuild, File badBuild, File stateFile, File command, File output,
+      Result result) {
+    this.goodBuild = goodBuild;
+    this.badBuild = badBuild;
+    this.stateFile = stateFile;
+    this.command = command;
+    this.output = output;
+    this.result = result;
+  }
+
+  public static BisectOptions parse(String[] args) throws IOException {
+    ParserSpec parser = new ParserSpec();
+    OptionSet options = parser.parse(args);
+    if (options.has(parser.help)) {
+      printHelp(System.out);
+      return null;
+    }
+    File goodBuild = exists(require(options, parser.goodBuild, BUILD_GOOD_FLAG), BUILD_GOOD_FLAG);
+    File badBuild = exists(require(options, parser.badBuild, BUILD_BAD_FLAG), BUILD_BAD_FLAG);
+    File stateFile = null;
+    if (options.valueOf(parser.stateFile) != null) {
+      stateFile = exists(options.valueOf(parser.stateFile), STATE_FLAG);
+    }
+    File command = null;
+    if (options.valueOf(parser.command) != null) {
+      command = exists(options.valueOf(parser.command), COMMAND_FLAG);
+    }
+    File output = null;
+    if (options.valueOf(parser.output) != null) {
+      output = directoryExists(options.valueOf(parser.output), OUTPUT_FLAG);
+    }
+    Result result = Result.UNKNOWN;
+    if (options.has(parser.resultGood)) {
+      result = Result.GOOD;
+    }
+    if (options.has(parser.resultBad)) {
+      if (result == Result.GOOD) {
+        throw new CompilationError("Cannot specify --" + RESULT_GOOD_FLAG
+            + " and --" + RESULT_BAD_FLAG + " simultaneously");
+      }
+      result = Result.BAD;
+    }
+    return new BisectOptions(goodBuild, badBuild, stateFile, command, output, result);
+  }
+
+  private static <T> T require(OptionSet options, OptionSpec<T> option, String flag)
+      throws IOException {
+    T value = options.valueOf(option);
+    if (value != null) {
+      return value;
+    }
+    throw new CompilationError("Missing required option: --" + flag);
+  }
+
+  private static File exists(String path, String flag) throws IOException {
+    File file = new File(path);
+    if (file.exists()) {
+      return file;
+    }
+    throw new CompilationError("File --" + flag + ": " + file + " does not exist");
+  }
+
+  private static File directoryExists(String path, String flag) throws IOException {
+    File file = new File(path);
+    if (file.exists() && file.isDirectory()) {
+      return file;
+    }
+    throw new CompilationError("File --" + flag + ": " + file + " is not a valid directory");
+  }
+
+  public static void printHelp(OutputStream out) throws IOException {
+    ParserSpec.printHelp(out);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
new file mode 100644
index 0000000..945578a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -0,0 +1,347 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.bisect;
+
+import com.android.tools.r8.bisect.BisectOptions.Result;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingLens;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.hash.Hashing;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class BisectState {
+
+  private static class Range {
+    final int start;
+    final int end;
+
+    public Range(int start, int end) {
+      this.start = start;
+      this.end = end;
+      assert verify();
+    }
+
+    public Range(String range) {
+      int sep = range.indexOf(' ');
+      start = Integer.parseInt(range.substring(0, sep).trim());
+      end = Integer.parseInt(range.substring(sep + 1).trim());
+      assert verify();
+    }
+
+    public void write(Writer writer) throws IOException {
+      writer.write("" + start);
+      writer.write(" ");
+      writer.write("" + end);
+    }
+
+    public boolean isEmpty() {
+      return start == end;
+    }
+
+    public int size() {
+      return end - start;
+    }
+
+    public Range add(Range other) {
+      if (isEmpty()) {
+        return other;
+      }
+      if (other.isEmpty()) {
+        return this;
+      }
+      assert start == other.end || end == other.start;
+      return new Range(Integer.min(start, other.start), Integer.max(end, other.end));
+    }
+
+    public Range sub(Range other) {
+      if (other.isEmpty()) {
+        return this;
+      }
+      assert start <= other.start && other.end <= end;
+      if (start == other.start) {
+        return new Range(other.end, end);
+      }
+      assert end == other.end;
+      return new Range(start, other.start);
+    }
+
+    public Range split() {
+      int length = size() / 2;
+      return new Range(start, start + length);
+    }
+
+    public boolean contains(int index) {
+      return start <= index && index < end;
+    }
+
+    @Override
+    public String toString() {
+      return "["+start+";"+end+"]";
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (!(other instanceof Range)) {
+        return false;
+      }
+      Range o = (Range) other;
+      return start == o.start && end == o.end;
+    }
+
+    private boolean verify() {
+      return start <= end;
+    }
+  }
+
+  private static class Run {
+    final boolean good;
+    final Range range;
+
+    public Run(Result result, Range range) {
+      assert result != Result.UNKNOWN;
+      good = result == Result.GOOD;
+      this.range = range;
+    }
+
+    public Run(String nonLastEntry) {
+      int sep1 = nonLastEntry.indexOf(':');
+      good = nonLastEntry.substring(0, sep1).trim().equals("good");
+      String rangeEntry = nonLastEntry.substring(sep1 + 1).trim();
+      range = new Range(rangeEntry);
+    }
+
+    public void write(Writer writer) throws IOException {
+      writer.write(good ? "good" : "bad");
+      writer.write(':');
+      range.write(writer);
+    }
+
+    public boolean isGood() {
+      return good;
+    }
+
+    public boolean isBad() {
+      return !good;
+    }
+  }
+
+  private final String signature;
+  private final DexApplication goodApp;
+  private final DexApplication badApp;
+  private final List<DexProgramClass> sortedGoodClasses;
+  private final Map<DexType, Integer> indexMap;
+  private final File stateFile;
+
+  private List<Run> runs = new ArrayList<>();
+
+  // Computed data
+  private Range nextRange = null;
+
+  public BisectState(DexApplication goodApp, DexApplication badApp, File stateFile) {
+    this.goodApp = goodApp;
+    this.badApp = badApp;
+    this.stateFile = stateFile;
+    signature = makeSignature(goodApp);
+    if (!signature.equals(makeSignature(badApp))) {
+      throw new CompilationError(
+          "Bisecting application classes do not match classes in reference APK");
+    }
+    sortedGoodClasses = ImmutableList.copyOf(getSortedClasses(goodApp));
+    ImmutableMap.Builder<DexType, Integer> builder = ImmutableMap.builder();
+    for (int i = 0; i < sortedGoodClasses.size(); i++) {
+      builder.put(sortedGoodClasses.get(i).type, i);
+    }
+    indexMap = builder.build();
+  }
+
+  public void read() throws IOException {
+    if (stateFile == null) {
+      return;
+    }
+    List<String> data = new ArrayList<>();
+    try (BufferedReader reader = new BufferedReader(new FileReader(stateFile))) {
+      if (!signature.equals(readSignature(reader))) {
+        throw new CompilationError(
+            "Bisection state file does not match the reference build signature");
+      }
+      String run;
+      while ((run = reader.readLine()) != null) {
+        data.add(run);
+      }
+    }
+    if (data.isEmpty()) {
+      return;
+    }
+    runs = new ArrayList<>(data.size());
+    for (int i = 0; i < data.size() - 1; i++) {
+      runs.add(new Run(data.get(i)));
+    }
+    nextRange = new Range(data.get(data.size() - 1));
+  }
+
+  public void setPreviousResult(Result result) {
+    if (nextRange == null) {
+      throw new CompilationError(
+          "Invalid bisection state. Could not find information on previous runs.");
+    }
+    if (runs.size() == 0) {
+      assert nextRange.equals(new Range(0, 0));
+      if (result != Result.GOOD) {
+        throw new CompilationError(
+            "Expected good state for reference application run, got " + result);
+      }
+    }
+    if (runs.size() == 1) {
+      assert nextRange.equals(new Range(0, indexMap.size()));
+      if (result != Result.BAD) {
+        throw new CompilationError(
+            "Expected bad state for input application run, got " + result);
+      }
+    }
+    runs.add(new Run(result, nextRange));
+    System.out.println("Marked range " + nextRange + ": " + result);
+    nextRange = null;
+  }
+
+  public void verifySignature(DexApplication application) {
+    if (signatureMismatch(makeSignature(application))) {
+      throw new CompilationError(
+          "Bisection state file does not match the application signature");
+    }
+  }
+
+  public DexProgramClass getFinalClass() {
+    if (nextRange.size() == 1) {
+      int index = nextRange.start;
+      return (DexProgramClass) badApp.definitionFor(sortedGoodClasses.get(index).type);
+    }
+    return null;
+  }
+
+  public DexApplication bisect() {
+    assert nextRange == null;
+    if (runs.isEmpty()) {
+      // First run is a sanity check ensuring that the reference app results in a good state.
+      nextRange = new Range(0, 0);
+    } else if (runs.size() == 1) {
+      // Second run is a sanity check ensuring that full application results in a bad state.
+      nextRange = new Range(0, sortedGoodClasses.size());
+    } else {
+      // Subsequent runs continue with half of the currently-known bad range.
+      Range badRange = getLastBadRange();
+      if (badRange.isEmpty()) {
+        throw new CompilationError("Bad range is empty. Cannot continue bisecting :-(");
+      }
+      if (badRange.size() == 1) {
+        nextRange = badRange;
+        return null;
+      }
+      System.out.println("Last bad range: " + badRange);
+      nextRange = badRange.split();
+    }
+    System.out.println("Next bisection range: " + nextRange);
+    int goodClasses = 0;
+    int badClasses = 0;
+    Map<DexType, DexClassPromise> classes = new HashMap<>();
+    for (DexLibraryClass clazz : badApp.libraryClasses()) {
+      classes.put(clazz.type, clazz);
+    }
+    for (DexProgramClass clazz : badApp.classes()) {
+      DexProgramClass goodClass = getGoodClass(clazz);
+      if (goodClass != null) {
+        classes.put(goodClass.type, goodClass);
+        ++goodClasses;
+      } else {
+        classes.put(clazz.type, clazz);
+        assert !nextRange.isEmpty();
+        ++badClasses;
+      }
+    }
+    System.out.println("Class split is good: " + goodClasses + ", bad: " + badClasses);
+    return new Builder(badApp, classes).build();
+  }
+
+  private DexProgramClass getGoodClass(DexProgramClass clazz) {
+    Integer index = indexMap.get(clazz.type);
+    if (index != null && !nextRange.contains(index)) {
+      return sortedGoodClasses.get(index);
+    }
+    return null;
+  }
+
+  private Range getLastBadRange() {
+    Range good = new Range(0, 0);
+    for (int i = runs.size() - 1; i >= 0; i--) {
+      Run run = runs.get(i);
+      if (run.isBad()) {
+        return run.range.sub(good);
+      }
+      good = good.add(run.range);
+    }
+    throw new Unreachable("Did not find any bad range in bisection state");
+  }
+
+  private boolean signatureMismatch(String appSignature) {
+    return !signature.equals(appSignature);
+  }
+
+  private static String readSignature(BufferedReader reader) throws IOException {
+    return reader.readLine();
+  }
+
+  public void write() throws IOException {
+    if (stateFile == null) {
+      return;
+    }
+    try (FileWriter writer = new FileWriter(stateFile, false)) {
+      writer.write(signature);
+      writer.write("\n");
+      for (Run run : runs) {
+        run.write(writer);
+        writer.write("\n");
+      }
+      nextRange.write(writer);
+      writer.write("\n");
+      writer.flush();
+    }
+  }
+
+  private static List<DexProgramClass> getSortedClasses(DexApplication app) {
+    List<DexProgramClass> classes = new ArrayList<>();
+    for (DexProgramClass clazz : app.classes()) {
+      classes.add(clazz);
+    }
+    app.dexItemFactory.sort(NamingLens.getIdentityLens());
+    classes.sort((a, b) -> a.type.compareTo(b.type));
+    app.dexItemFactory.resetSortedIndices();
+    return classes;
+  }
+
+  private static String makeSignature(DexApplication app) {
+    List<DexProgramClass> classes = getSortedClasses(app);
+    StringBuilder builder = new StringBuilder();
+    for (DexProgramClass clazz : classes) {
+      builder.append(clazz.toString()).append(";");
+    }
+    return Hashing.sha1().hashString(builder.toString(), Charsets.UTF_8).toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddDouble.java b/src/main/java/com/android/tools/r8/code/AddDouble.java
new file mode 100644
index 0000000..11d431c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddDouble extends Format23x {
+
+  public static final int OPCODE = 0xab;
+  public static final String NAME = "AddDouble";
+  public static final String SMALI_NAME = "add-double";
+
+  AddDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.DOUBLE, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddDouble2Addr.java b/src/main/java/com/android/tools/r8/code/AddDouble2Addr.java
new file mode 100644
index 0000000..b910977
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddDouble2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddDouble2Addr extends Format12x {
+
+  public static final int OPCODE = 0xcb;
+  public static final String NAME = "AddDouble2Addr";
+  public static final String SMALI_NAME = "add-double/2addr";
+
+  AddDouble2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddDouble2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.DOUBLE, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddFloat.java b/src/main/java/com/android/tools/r8/code/AddFloat.java
new file mode 100644
index 0000000..e13c1a8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddFloat extends Format23x {
+
+  public static final int OPCODE = 0xa6;
+  public static final String NAME = "AddFloat";
+  public static final String SMALI_NAME = "add-float";
+
+  AddFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.FLOAT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddFloat2Addr.java b/src/main/java/com/android/tools/r8/code/AddFloat2Addr.java
new file mode 100644
index 0000000..867694e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddFloat2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc6;
+  public static final String NAME = "AddFloat2Addr";
+  public static final String SMALI_NAME = "add-float/2addr";
+
+  AddFloat2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddFloat2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.FLOAT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddInt.java b/src/main/java/com/android/tools/r8/code/AddInt.java
new file mode 100644
index 0000000..fccb17e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddInt extends Format23x {
+
+  public static final int OPCODE = 0x90;
+  public static final String NAME = "AddInt";
+  public static final String SMALI_NAME = "add-int";
+
+  AddInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddInt2Addr.java b/src/main/java/com/android/tools/r8/code/AddInt2Addr.java
new file mode 100644
index 0000000..243bc73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb0;
+  public static final String NAME = "AddInt2Addr";
+  public static final String SMALI_NAME = "add-int/2addr";
+
+  AddInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddIntLit16.java b/src/main/java/com/android/tools/r8/code/AddIntLit16.java
new file mode 100644
index 0000000..a2d458a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd0;
+  public static final String NAME = "AddIntLit16";
+  public static final String SMALI_NAME = "add-int/lit16";
+
+  AddIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddIntLit16(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAddLiteral(NumericType.INT, A, B, CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddIntLit8.java b/src/main/java/com/android/tools/r8/code/AddIntLit8.java
new file mode 100644
index 0000000..c736cbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AddIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xd8;
+  public static final String NAME = "AddIntLit8";
+  public static final String SMALI_NAME = "add-int/lit8";
+
+  AddIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAddLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddLong.java b/src/main/java/com/android/tools/r8/code/AddLong.java
new file mode 100644
index 0000000..a50490a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddLong extends Format23x {
+
+  public static final int OPCODE = 0x9b;
+  public static final String NAME = "AddLong";
+  public static final String SMALI_NAME = "add-long";
+
+  AddLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AddLong2Addr.java b/src/main/java/com/android/tools/r8/code/AddLong2Addr.java
new file mode 100644
index 0000000..fbd0c78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AddLong2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AddLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xbb;
+  public static final String NAME = "AddLong2Addr";
+  public static final String SMALI_NAME = "add-long/2addr";
+
+  AddLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AddLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAdd(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Aget.java b/src/main/java/com/android/tools/r8/code/Aget.java
new file mode 100644
index 0000000..57216ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Aget.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Aget extends Format23x {
+
+  public static final int OPCODE = 0x44;
+  public static final String NAME = "Aget";
+  public static final String SMALI_NAME = "aget";
+
+  /*package*/ Aget(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Aget(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.SINGLE, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetBoolean.java b/src/main/java/com/android/tools/r8/code/AgetBoolean.java
new file mode 100644
index 0000000..fbae5e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetBoolean.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetBoolean extends Format23x {
+
+  public static final int OPCODE = 0x47;
+  public static final String NAME = "AgetBoolean";
+  public static final String SMALI_NAME = "aget-boolean";
+
+  AgetBoolean(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AgetBoolean(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.BOOLEAN, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetByte.java b/src/main/java/com/android/tools/r8/code/AgetByte.java
new file mode 100644
index 0000000..bc30152
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetByte.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetByte extends Format23x {
+
+  public static final int OPCODE = 0x48;
+  public static final String NAME = "AgetByte";
+  public static final String SMALI_NAME = "aget-byte";
+
+  /*package*/ AgetByte(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AgetByte(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.BYTE, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetChar.java b/src/main/java/com/android/tools/r8/code/AgetChar.java
new file mode 100644
index 0000000..f46fedf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetChar.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetChar extends Format23x {
+
+  public static final int OPCODE = 0x49;
+  public static final String NAME = "AgetChar";
+  public static final String SMALI_NAME = "aget-char";
+
+  /*package*/ AgetChar(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AgetChar(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.CHAR, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetObject.java b/src/main/java/com/android/tools/r8/code/AgetObject.java
new file mode 100644
index 0000000..7ddd0f7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetObject.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AgetObject extends Format23x {
+
+  public static final int OPCODE = 0x46;
+  public static final String NAME = "AgetObject";
+  public static final String SMALI_NAME = "aget-object";
+
+  AgetObject(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AgetObject(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.OBJECT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetShort.java b/src/main/java/com/android/tools/r8/code/AgetShort.java
new file mode 100644
index 0000000..bc54dee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetShort.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetShort extends Format23x {
+
+  public static final int OPCODE = 0x4a;
+  public static final String NAME = "AgetShort";
+  public static final String SMALI_NAME = "aget-short";
+
+  AgetShort(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AgetShort(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.SHORT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AgetWide.java b/src/main/java/com/android/tools/r8/code/AgetWide.java
new file mode 100644
index 0000000..a788da8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AgetWide.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AgetWide extends Format23x {
+
+  public static final int OPCODE = 0x45;
+  public static final String NAME = "AgetWide";
+  public static final String SMALI_NAME = "aget-wide";
+
+  /*package*/ AgetWide(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AgetWide(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayGet(MemberType.WIDE, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndInt.java b/src/main/java/com/android/tools/r8/code/AndInt.java
new file mode 100644
index 0000000..1631409
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndInt extends Format23x {
+
+  public static final int OPCODE = 0x95;
+  public static final String NAME = "AndInt";
+  public static final String SMALI_NAME = "and-int";
+
+  AndInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AndInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAnd(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndInt2Addr.java b/src/main/java/com/android/tools/r8/code/AndInt2Addr.java
new file mode 100644
index 0000000..92f29e9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb5;
+  public static final String NAME = "AndInt2Addr";
+  public static final String SMALI_NAME = "and-int/2addr";
+
+  AndInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AndInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAnd(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndIntLit16.java b/src/main/java/com/android/tools/r8/code/AndIntLit16.java
new file mode 100644
index 0000000..d440125
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd5;
+  public static final String NAME = "AndIntLit16";
+  public static final String SMALI_NAME = "and-int/lit16";
+
+  AndIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AndIntLit16(int dest, int left, int constant) {
+    super(dest, left, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAndLiteral(NumericType.INT, A, B, CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndIntLit8.java b/src/main/java/com/android/tools/r8/code/AndIntLit8.java
new file mode 100644
index 0000000..4546375
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndIntLit8.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AndIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xdd;
+  public static final String NAME = "AndIntLit8";
+  public static final String SMALI_NAME = "and-int/lit8";
+
+  AndIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AndIntLit8(int dest, int left, int constant) {
+    super(dest, left, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAndLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndLong.java b/src/main/java/com/android/tools/r8/code/AndLong.java
new file mode 100644
index 0000000..54eb5c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AndLong extends Format23x {
+
+  public static final int OPCODE = 0xA0;
+  public static final String NAME = "AndLong";
+  public static final String SMALI_NAME = "and-long";
+
+  AndLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AndLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAnd(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AndLong2Addr.java b/src/main/java/com/android/tools/r8/code/AndLong2Addr.java
new file mode 100644
index 0000000..8ab3e14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AndLong2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class AndLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc0;
+  public static final String NAME = "AndLong2Addr";
+  public static final String SMALI_NAME = "and-long/2addr";
+
+  AndLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AndLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addAnd(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Aput.java b/src/main/java/com/android/tools/r8/code/Aput.java
new file mode 100644
index 0000000..3698393
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Aput.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Aput extends Format23x {
+
+  public static final int OPCODE = 0x4b;
+  public static final String NAME = "Aput";
+  public static final String SMALI_NAME = "aput";
+
+  /*package*/ Aput(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Aput(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.SINGLE, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputBoolean.java b/src/main/java/com/android/tools/r8/code/AputBoolean.java
new file mode 100644
index 0000000..859a30d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputBoolean.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputBoolean extends Format23x {
+
+  public static final int OPCODE = 0x4e;
+  public static final String NAME = "AputBoolean";
+  public static final String SMALI_NAME = "aput-boolean";
+
+  /*package*/ AputBoolean(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AputBoolean(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.BOOLEAN, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputByte.java b/src/main/java/com/android/tools/r8/code/AputByte.java
new file mode 100644
index 0000000..926a109
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputByte.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputByte extends Format23x {
+
+  public static final int OPCODE = 0x4f;
+  public static final String NAME = "AputByte";
+  public static final String SMALI_NAME = "aput-byte";
+
+  /*package*/ AputByte(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AputByte(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.BYTE, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputChar.java b/src/main/java/com/android/tools/r8/code/AputChar.java
new file mode 100644
index 0000000..4777d24
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputChar.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputChar extends Format23x {
+
+  public static final int OPCODE = 0x50;
+  public static final String NAME = "AputChar";
+  public static final String SMALI_NAME = "aput-char";
+
+  /*package*/ AputChar(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AputChar(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.CHAR, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputObject.java b/src/main/java/com/android/tools/r8/code/AputObject.java
new file mode 100644
index 0000000..0f52da5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputObject.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputObject extends Format23x {
+
+  public static final int OPCODE = 0x4d;
+  public static final String NAME = "AputObject";
+  public static final String SMALI_NAME = "aput-object";
+
+  /*package*/ AputObject(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AputObject(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.OBJECT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputShort.java b/src/main/java/com/android/tools/r8/code/AputShort.java
new file mode 100644
index 0000000..ab477e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputShort.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputShort extends Format23x {
+
+  public static final int OPCODE = 0x51;
+  public static final String NAME = "AputShort";
+  public static final String SMALI_NAME = "aput-short";
+
+  /*package*/ AputShort(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AputShort(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.SHORT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/AputWide.java b/src/main/java/com/android/tools/r8/code/AputWide.java
new file mode 100644
index 0000000..86adc5a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/AputWide.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class AputWide extends Format23x {
+
+  public static final int OPCODE = 0x4c;
+  public static final String NAME = "AputWide";
+  public static final String SMALI_NAME = "aput-wide";
+
+  /*package*/ AputWide(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public AputWide(int AA, int BB, int CC) {
+    super(AA, BB, CC);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayPut(MemberType.WIDE, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ArrayLength.java b/src/main/java/com/android/tools/r8/code/ArrayLength.java
new file mode 100644
index 0000000..59e5c00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ArrayLength.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ArrayLength extends Format12x {
+
+  public static final int OPCODE = 0x21;
+  public static final String NAME = "ArrayLength";
+  public static final String SMALI_NAME = "array-length";
+
+  ArrayLength(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ArrayLength(int dest, int array) {
+    super(dest, array);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addArrayLength(A, B);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Base1Format.java b/src/main/java/com/android/tools/r8/code/Base1Format.java
new file mode 100644
index 0000000..b62853d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base1Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base1Format extends Instruction {
+
+  public Base1Format(BytecodeStream stream) {
+    super(stream);
+  }
+
+  protected Base1Format() {}
+
+  public int getSize() {
+    return 1;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Base2Format.java b/src/main/java/com/android/tools/r8/code/Base2Format.java
new file mode 100644
index 0000000..f8a48ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base2Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base2Format extends Instruction {
+
+  protected Base2Format() {}
+
+  public Base2Format(BytecodeStream stream) {
+    super(stream);
+  }
+
+  public int getSize() {
+    return 2;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Base3Format.java b/src/main/java/com/android/tools/r8/code/Base3Format.java
new file mode 100644
index 0000000..34bda57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base3Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base3Format extends Instruction {
+
+  protected Base3Format() {}
+
+  public Base3Format(BytecodeStream stream) {
+    super(stream);
+  }
+
+  public int getSize() {
+    return 3;
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/Base4Format.java b/src/main/java/com/android/tools/r8/code/Base4Format.java
new file mode 100644
index 0000000..7cdf1c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base4Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base4Format extends Instruction {
+
+  protected Base4Format() {}
+
+  public Base4Format(BytecodeStream stream) {
+    super(stream);
+  }
+
+  public int getSize() {
+    return 4;
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/Base5Format.java b/src/main/java/com/android/tools/r8/code/Base5Format.java
new file mode 100644
index 0000000..cc67572
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Base5Format.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public abstract class Base5Format extends Instruction {
+
+  protected Base5Format() {}
+
+  public Base5Format(BytecodeStream stream) {
+    super(stream);
+  }
+
+  public int getSize() {
+    return 5;
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
new file mode 100644
index 0000000..8637cca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/BaseInstructionFactory.java
@@ -0,0 +1,461 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+
+abstract class BaseInstructionFactory {
+
+  static Instruction create(int high, int opcode, BytecodeStream stream,
+      OffsetToObjectMapping mapping) {
+    switch (opcode) {
+      case 0x0:
+        return Nop.create(high, stream);
+      case Move.OPCODE:
+        return new Move(high, stream);
+      case MoveFrom16.OPCODE:
+        return new MoveFrom16(high, stream);
+      case Move16.OPCODE:
+        return new Move16(high, stream);
+      case MoveWide.OPCODE:
+        return new MoveWide(high, stream);
+      case MoveWideFrom16.OPCODE:
+        return new MoveWideFrom16(high, stream);
+      case MoveWide16.OPCODE:
+        return new MoveWide16(high, stream);
+      case MoveObject.OPCODE:
+        return new MoveObject(high, stream);
+      case MoveObjectFrom16.OPCODE:
+        return new MoveObjectFrom16(high, stream);
+      case MoveObject16.OPCODE:
+        return new MoveObject16(high, stream);
+      case MoveResult.OPCODE:
+        return new MoveResult(high, stream);
+      case MoveResultWide.OPCODE:
+        return new MoveResultWide(high, stream);
+      case MoveResultObject.OPCODE:
+        return new MoveResultObject(high, stream);
+      case MoveException.OPCODE:
+        return new MoveException(high, stream);
+      case ReturnVoid.OPCODE:
+        return new ReturnVoid(high, stream);
+      case Return.OPCODE:
+        return new Return(high, stream);
+      case ReturnWide.OPCODE:
+        return new ReturnWide(high, stream);
+      case ReturnObject.OPCODE:
+        return new ReturnObject(high, stream);
+      case Const4.OPCODE:
+        return new Const4(high, stream);
+      case Const16.OPCODE:
+        return new Const16(high, stream);
+      case Const.OPCODE:
+        return new Const(high, stream);
+      case ConstHigh16.OPCODE:
+        return new ConstHigh16(high, stream);
+      case ConstWide16.OPCODE:
+        return new ConstWide16(high, stream);
+      case ConstWide32.OPCODE:
+        return new ConstWide32(high, stream);
+      case ConstWide.OPCODE:
+        return new ConstWide(high, stream);
+      case ConstWideHigh16.OPCODE:
+        return new ConstWideHigh16(high, stream);
+      case ConstString.OPCODE:
+        return new ConstString(high, stream, mapping);
+      case ConstStringJumbo.OPCODE:
+        return new ConstStringJumbo(high, stream, mapping);
+      case ConstClass.OPCODE:
+        return new ConstClass(high, stream, mapping);
+      case MonitorEnter.OPCODE:
+        return new MonitorEnter(high, stream);
+      case MonitorExit.OPCODE:
+        return new MonitorExit(high, stream);
+      case CheckCast.OPCODE:
+        return new CheckCast(high, stream, mapping);
+      case InstanceOf.OPCODE:
+        return new InstanceOf(high, stream, mapping);
+      case ArrayLength.OPCODE:
+        return new ArrayLength(high, stream);
+      case NewInstance.OPCODE:
+        return new NewInstance(high, stream, mapping);
+      case NewArray.OPCODE:
+        return new NewArray(high, stream, mapping);
+      case FilledNewArray.OPCODE:
+        return new FilledNewArray(high, stream, mapping);
+      case FilledNewArrayRange.OPCODE:
+        return new FilledNewArrayRange(high, stream, mapping);
+      case FillArrayData.OPCODE:
+        return new FillArrayData(high, stream);
+      case Throw.OPCODE:
+        return new Throw(high, stream);
+      case Goto.OPCODE:
+        return new Goto(high, stream);
+      case Goto16.OPCODE:
+        return new Goto16(high, stream);
+      case Goto32.OPCODE:
+        return new Goto32(high, stream);
+      case PackedSwitch.OPCODE:
+        return new PackedSwitch(high, stream);
+      case SparseSwitch.OPCODE:
+        return new SparseSwitch(high, stream);
+      case CmplFloat.OPCODE:
+        return new CmplFloat(high, stream);
+      case CmpgFloat.OPCODE:
+        return new CmpgFloat(high, stream);
+      case CmplDouble.OPCODE:
+        return new CmplDouble(high, stream);
+      case CmpgDouble.OPCODE:
+        return new CmpgDouble(high, stream);
+      case CmpLong.OPCODE:
+        return new CmpLong(high, stream);
+      case IfEq.OPCODE:
+        return new IfEq(high, stream);
+      case IfNe.OPCODE:
+        return new IfNe(high, stream);
+      case IfLt.OPCODE:
+        return new IfLt(high, stream);
+      case IfGe.OPCODE:
+        return new IfGe(high, stream);
+      case IfGt.OPCODE:
+        return new IfGt(high, stream);
+      case IfLe.OPCODE:
+        return new IfLe(high, stream);
+      case IfEqz.OPCODE:
+        return new IfEqz(high, stream);
+      case IfNez.OPCODE:
+        return new IfNez(high, stream);
+      case IfLtz.OPCODE:
+        return new IfLtz(high, stream);
+      case IfGez.OPCODE:
+        return new IfGez(high, stream);
+      case IfGtz.OPCODE:
+        return new IfGtz(high, stream);
+      case IfLez.OPCODE:
+        return new IfLez(high, stream);
+      case Aget.OPCODE:
+        return new Aget(high, stream);
+      case AgetWide.OPCODE:
+        return new AgetWide(high, stream);
+      case AgetObject.OPCODE:
+        return new AgetObject(high, stream);
+      case AgetBoolean.OPCODE:
+        return new AgetBoolean(high, stream);
+      case AgetByte.OPCODE:
+        return new AgetByte(high, stream);
+      case AgetChar.OPCODE:
+        return new AgetChar(high, stream);
+      case AgetShort.OPCODE:
+        return new AgetShort(high, stream);
+      case Aput.OPCODE:
+        return new Aput(high, stream);
+      case AputWide.OPCODE:
+        return new AputWide(high, stream);
+      case AputObject.OPCODE:
+        return new AputObject(high, stream);
+      case AputBoolean.OPCODE:
+        return new AputBoolean(high, stream);
+      case AputByte.OPCODE:
+        return new AputByte(high, stream);
+      case AputChar.OPCODE:
+        return new AputChar(high, stream);
+      case AputShort.OPCODE:
+        return new AputShort(high, stream);
+      case Iget.OPCODE:
+        return new Iget(high, stream, mapping);
+      case IgetWide.OPCODE:
+        return new IgetWide(high, stream, mapping);
+      case IgetObject.OPCODE:
+        return new IgetObject(high, stream, mapping);
+      case IgetBoolean.OPCODE:
+        return new IgetBoolean(high, stream, mapping);
+      case IgetByte.OPCODE:
+        return new IgetByte(high, stream, mapping);
+      case IgetChar.OPCODE:
+        return new IgetChar(high, stream, mapping);
+      case IgetShort.OPCODE:
+        return new IgetShort(high, stream, mapping);
+      case Iput.OPCODE:
+        return new Iput(high, stream, mapping);
+      case IputWide.OPCODE:
+        return new IputWide(high, stream, mapping);
+      case IputObject.OPCODE:
+        return new IputObject(high, stream, mapping);
+      case IputBoolean.OPCODE:
+        return new IputBoolean(high, stream, mapping);
+      case IputByte.OPCODE:
+        return new IputByte(high, stream, mapping);
+      case IputChar.OPCODE:
+        return new IputChar(high, stream, mapping);
+      case IputShort.OPCODE:
+        return new IputShort(high, stream, mapping);
+      case Sget.OPCODE:
+        return new Sget(high, stream, mapping);
+      case SgetWide.OPCODE:
+        return new SgetWide(high, stream, mapping);
+      case SgetObject.OPCODE:
+        return new SgetObject(high, stream, mapping);
+      case SgetBoolean.OPCODE:
+        return new SgetBoolean(high, stream, mapping);
+      case SgetByte.OPCODE:
+        return new SgetByte(high, stream, mapping);
+      case SgetChar.OPCODE:
+        return new SgetChar(high, stream, mapping);
+      case SgetShort.OPCODE:
+        return new SgetShort(high, stream, mapping);
+      case Sput.OPCODE:
+        return new Sput(high, stream, mapping);
+      case SputWide.OPCODE:
+        return new SputWide(high, stream, mapping);
+      case SputObject.OPCODE:
+        return new SputObject(high, stream, mapping);
+      case SputBoolean.OPCODE:
+        return new SputBoolean(high, stream, mapping);
+      case SputByte.OPCODE:
+        return new SputByte(high, stream, mapping);
+      case SputChar.OPCODE:
+        return new SputChar(high, stream, mapping);
+      case SputShort.OPCODE:
+        return new SputShort(high, stream, mapping);
+      case InvokeVirtual.OPCODE:
+        return new InvokeVirtual(high, stream, mapping);
+      case InvokeSuper.OPCODE:
+        return new InvokeSuper(high, stream, mapping);
+      case InvokeDirect.OPCODE:
+        return new InvokeDirect(high, stream, mapping);
+      case InvokeStatic.OPCODE:
+        return new InvokeStatic(high, stream, mapping);
+      case InvokeInterface.OPCODE:
+        return new InvokeInterface(high, stream, mapping);
+      case InvokeVirtualRange.OPCODE:
+        return new InvokeVirtualRange(high, stream, mapping);
+      case InvokeSuperRange.OPCODE:
+        return new InvokeSuperRange(high, stream, mapping);
+      case InvokeDirectRange.OPCODE:
+        return new InvokeDirectRange(high, stream, mapping);
+      case InvokeStaticRange.OPCODE:
+        return new InvokeStaticRange(high, stream, mapping);
+      case InvokeInterfaceRange.OPCODE:
+        return new InvokeInterfaceRange(high, stream, mapping);
+      case NegInt.OPCODE:
+        return new NegInt(high, stream);
+      case NotInt.OPCODE:
+        return new NotInt(high, stream);
+      case NegLong.OPCODE:
+        return new NegLong(high, stream);
+      case NotLong.OPCODE:
+        return new NotLong(high, stream);
+      case NegFloat.OPCODE:
+        return new NegFloat(high, stream);
+      case NegDouble.OPCODE:
+        return new NegDouble(high, stream);
+      case IntToLong.OPCODE:
+        return new IntToLong(high, stream);
+      case IntToFloat.OPCODE:
+        return new IntToFloat(high, stream);
+      case IntToDouble.OPCODE:
+        return new IntToDouble(high, stream);
+      case LongToInt.OPCODE:
+        return new LongToInt(high, stream);
+      case LongToFloat.OPCODE:
+        return new LongToFloat(high, stream);
+      case LongToDouble.OPCODE:
+        return new LongToDouble(high, stream);
+      case FloatToInt.OPCODE:
+        return new FloatToInt(high, stream);
+      case FloatToLong.OPCODE:
+        return new FloatToLong(high, stream);
+      case FloatToDouble.OPCODE:
+        return new FloatToDouble(high, stream);
+      case DoubleToInt.OPCODE:
+        return new DoubleToInt(high, stream);
+      case DoubleToLong.OPCODE:
+        return new DoubleToLong(high, stream);
+      case DoubleToFloat.OPCODE:
+        return new DoubleToFloat(high, stream);
+      case IntToByte.OPCODE:
+        return new IntToByte(high, stream);
+      case IntToChar.OPCODE:
+        return new IntToChar(high, stream);
+      case IntToShort.OPCODE:
+        return new IntToShort(high, stream);
+      case AddInt.OPCODE:
+        return new AddInt(high, stream);
+      case SubInt.OPCODE:
+        return new SubInt(high, stream);
+      case MulInt.OPCODE:
+        return new MulInt(high, stream);
+      case DivInt.OPCODE:
+        return new DivInt(high, stream);
+      case RemInt.OPCODE:
+        return new RemInt(high, stream);
+      case AndInt.OPCODE:
+        return new AndInt(high, stream);
+      case OrInt.OPCODE:
+        return new OrInt(high, stream);
+      case XorInt.OPCODE:
+        return new XorInt(high, stream);
+      case ShlInt.OPCODE:
+        return new ShlInt(high, stream);
+      case ShrInt.OPCODE:
+        return new ShrInt(high, stream);
+      case UshrInt.OPCODE:
+        return new UshrInt(high, stream);
+      case AddLong.OPCODE:
+        return new AddLong(high, stream);
+      case SubLong.OPCODE:
+        return new SubLong(high, stream);
+      case MulLong.OPCODE:
+        return new MulLong(high, stream);
+      case DivLong.OPCODE:
+        return new DivLong(high, stream);
+      case RemLong.OPCODE:
+        return new RemLong(high, stream);
+      case AndLong.OPCODE:
+        return new AndLong(high, stream);
+      case OrLong.OPCODE:
+        return new OrLong(high, stream);
+      case XorLong.OPCODE:
+        return new XorLong(high, stream);
+      case ShlLong.OPCODE:
+        return new ShlLong(high, stream);
+      case ShrLong.OPCODE:
+        return new ShrLong(high, stream);
+      case UshrLong.OPCODE:
+        return new UshrLong(high, stream);
+      case AddFloat.OPCODE:
+        return new AddFloat(high, stream);
+      case SubFloat.OPCODE:
+        return new SubFloat(high, stream);
+      case MulFloat.OPCODE:
+        return new MulFloat(high, stream);
+      case DivFloat.OPCODE:
+        return new DivFloat(high, stream);
+      case RemFloat.OPCODE:
+        return new RemFloat(high, stream);
+      case AddDouble.OPCODE:
+        return new AddDouble(high, stream);
+      case SubDouble.OPCODE:
+        return new SubDouble(high, stream);
+      case MulDouble.OPCODE:
+        return new MulDouble(high, stream);
+      case DivDouble.OPCODE:
+        return new DivDouble(high, stream);
+      case RemDouble.OPCODE:
+        return new RemDouble(high, stream);
+      case AddInt2Addr.OPCODE:
+        return new AddInt2Addr(high, stream);
+      case SubInt2Addr.OPCODE:
+        return new SubInt2Addr(high, stream);
+      case MulInt2Addr.OPCODE:
+        return new MulInt2Addr(high, stream);
+      case DivInt2Addr.OPCODE:
+        return new DivInt2Addr(high, stream);
+      case RemInt2Addr.OPCODE:
+        return new RemInt2Addr(high, stream);
+      case AndInt2Addr.OPCODE:
+        return new AndInt2Addr(high, stream);
+      case OrInt2Addr.OPCODE:
+        return new OrInt2Addr(high, stream);
+      case XorInt2Addr.OPCODE:
+        return new XorInt2Addr(high, stream);
+      case ShlInt2Addr.OPCODE:
+        return new ShlInt2Addr(high, stream);
+      case ShrInt2Addr.OPCODE:
+        return new ShrInt2Addr(high, stream);
+      case UshrInt2Addr.OPCODE:
+        return new UshrInt2Addr(high, stream);
+      case AddLong2Addr.OPCODE:
+        return new AddLong2Addr(high, stream);
+      case SubLong2Addr.OPCODE:
+        return new SubLong2Addr(high, stream);
+      case MulLong2Addr.OPCODE:
+        return new MulLong2Addr(high, stream);
+      case DivLong2Addr.OPCODE:
+        return new DivLong2Addr(high, stream);
+      case RemLong2Addr.OPCODE:
+        return new RemLong2Addr(high, stream);
+      case AndLong2Addr.OPCODE:
+        return new AndLong2Addr(high, stream);
+      case OrLong2Addr.OPCODE:
+        return new OrLong2Addr(high, stream);
+      case XorLong2Addr.OPCODE:
+        return new XorLong2Addr(high, stream);
+      case ShlLong2Addr.OPCODE:
+        return new ShlLong2Addr(high, stream);
+      case ShrLong2Addr.OPCODE:
+        return new ShrLong2Addr(high, stream);
+      case UshrLong2Addr.OPCODE:
+        return new UshrLong2Addr(high, stream);
+      case AddFloat2Addr.OPCODE:
+        return new AddFloat2Addr(high, stream);
+      case SubFloat2Addr.OPCODE:
+        return new SubFloat2Addr(high, stream);
+      case MulFloat2Addr.OPCODE:
+        return new MulFloat2Addr(high, stream);
+      case DivFloat2Addr.OPCODE:
+        return new DivFloat2Addr(high, stream);
+      case RemFloat2Addr.OPCODE:
+        return new RemFloat2Addr(high, stream);
+      case AddDouble2Addr.OPCODE:
+        return new AddDouble2Addr(high, stream);
+      case SubDouble2Addr.OPCODE:
+        return new SubDouble2Addr(high, stream);
+      case MulDouble2Addr.OPCODE:
+        return new MulDouble2Addr(high, stream);
+      case DivDouble2Addr.OPCODE:
+        return new DivDouble2Addr(high, stream);
+      case RemDouble2Addr.OPCODE:
+        return new RemDouble2Addr(high, stream);
+      case AddIntLit16.OPCODE:
+        return new AddIntLit16(high, stream);
+      case RsubInt.OPCODE:
+        return new RsubInt(high, stream);
+      case MulIntLit16.OPCODE:
+        return new MulIntLit16(high, stream);
+      case DivIntLit16.OPCODE:
+        return new DivIntLit16(high, stream);
+      case RemIntLit16.OPCODE:
+        return new RemIntLit16(high, stream);
+      case AndIntLit16.OPCODE:
+        return new AndIntLit16(high, stream);
+      case OrIntLit16.OPCODE:
+        return new OrIntLit16(high, stream);
+      case XorIntLit16.OPCODE:
+        return new XorIntLit16(high, stream);
+      case AddIntLit8.OPCODE:
+        return new AddIntLit8(high, stream);
+      case RsubIntLit8.OPCODE:
+        return new RsubIntLit8(high, stream);
+      case MulIntLit8.OPCODE:
+        return new MulIntLit8(high, stream);
+      case DivIntLit8.OPCODE:
+        return new DivIntLit8(high, stream);
+      case RemIntLit8.OPCODE:
+        return new RemIntLit8(high, stream);
+      case AndIntLit8.OPCODE:
+        return new AndIntLit8(high, stream);
+      case OrIntLit8.OPCODE:
+        return new OrIntLit8(high, stream);
+      case XorIntLit8.OPCODE:
+        return new XorIntLit8(high, stream);
+      case ShlIntLit8.OPCODE:
+        return new ShlIntLit8(high, stream);
+      case ShrIntLit8.OPCODE:
+        return new ShrIntLit8(high, stream);
+      case UshrIntLit8.OPCODE:
+        return new UshrIntLit8(high, stream);
+      case InvokePolymorphic.OPCODE:
+        return new InvokePolymorphic(high, stream, mapping);
+      case InvokePolymorphicRange.OPCODE:
+        return new InvokePolymorphicRange(high, stream, mapping);
+      case InvokeCustom.OPCODE:
+        return new InvokeCustom(high, stream, mapping);
+      case InvokeCustomRange.OPCODE:
+        return new InvokeCustomRange(high, stream, mapping);
+      default:
+        throw new IllegalArgumentException("Illegal Opcode: 0x" + Integer.toString(opcode, 16));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/BytecodeStream.java b/src/main/java/com/android/tools/r8/code/BytecodeStream.java
new file mode 100644
index 0000000..e928c3e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/BytecodeStream.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+public interface BytecodeStream {
+
+  /**
+   * Returns the current position from the starting index in shorts.
+   *
+   * @return offset from start in shorts.
+   */
+  int getOffset();
+
+  /**
+   * Returns the next short value from the stream of values.
+   *
+   * @return next short value in stream.
+   */
+  int nextShort();
+
+  /**
+   * Returns the next byte value from the stream, i.e., the high value of the next short followed by
+   * the low value.
+   *
+   * Both bytes need to be consumed before the next call to {@link #nextShort()}.
+   *
+   * @return next byte value in the stream.
+   */
+  int nextByte();
+
+  /**
+   * Returns true of there are more values to be consumed.
+   *
+   * @return true if more values can be consumed.
+   */
+  boolean hasMore();
+}
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java
new file mode 100644
index 0000000..1c7973b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CheckCast extends Format21c {
+
+  public static final int OPCODE = 0x1f;
+  public static final String NAME = "CheckCast";
+  public static final String SMALI_NAME = "check-cast";
+
+  CheckCast(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public CheckCast(int valueRegister, DexType type) {
+    super(valueRegister, type);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerTypeReference(getType());
+  }
+
+  public DexType getType() {
+    return (DexType) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addCheckCast(AA, getType());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmpLong.java b/src/main/java/com/android/tools/r8/code/CmpLong.java
new file mode 100644
index 0000000..65d80fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmpLong.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmpLong extends Format23x {
+
+  public static final int OPCODE = 0x31;
+  public static final String NAME = "CmpLong";
+  public static final String SMALI_NAME = "cmp-long";
+
+  CmpLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public CmpLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addCmp(NumericType.LONG, Bias.NONE, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmpgDouble.java b/src/main/java/com/android/tools/r8/code/CmpgDouble.java
new file mode 100644
index 0000000..5eea470
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmpgDouble.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmpgDouble extends Format23x {
+
+  public static final int OPCODE = 0x30;
+  public static final String NAME = "CmpgDouble";
+  public static final String SMALI_NAME = "cmpg-double";
+
+  CmpgDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public CmpgDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addCmp(NumericType.DOUBLE, Bias.GT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmpgFloat.java b/src/main/java/com/android/tools/r8/code/CmpgFloat.java
new file mode 100644
index 0000000..0462c8d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmpgFloat.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmpgFloat extends Format23x {
+
+  public static final int OPCODE = 0x2e;
+  public static final String NAME = "CmpgFloat";
+  public static final String SMALI_NAME = "cmpg-float";
+
+  CmpgFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public CmpgFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addCmp(NumericType.FLOAT, Bias.GT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmplDouble.java b/src/main/java/com/android/tools/r8/code/CmplDouble.java
new file mode 100644
index 0000000..474d03b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmplDouble.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmplDouble extends Format23x {
+
+  public static final int OPCODE = 0x2f;
+  public static final String NAME = "CmplDouble";
+  public static final String SMALI_NAME = "cmpl-double";
+
+  CmplDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public CmplDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addCmp(NumericType.DOUBLE, Bias.LT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/CmplFloat.java b/src/main/java/com/android/tools/r8/code/CmplFloat.java
new file mode 100644
index 0000000..3054fbf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/CmplFloat.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class CmplFloat extends Format23x {
+
+  public static final int OPCODE = 0x2d;
+  public static final String NAME = "CmplFloat";
+  public static final String SMALI_NAME = "cmpl-float";
+
+  CmplFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public CmplFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addCmp(NumericType.FLOAT, Bias.LT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Const.java b/src/main/java/com/android/tools/r8/code/Const.java
new file mode 100644
index 0000000..301f982
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Const.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class Const extends Format31i implements SingleConstant {
+
+  public static final int OPCODE = 0x14;
+  public static final String NAME = "Const";
+  public static final String SMALI_NAME = "const";
+
+  Const(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Const(int register, int constant) {
+    super(register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int decodedValue() {
+    return BBBBBBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+        "  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.SINGLE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Const16.java b/src/main/java/com/android/tools/r8/code/Const16.java
new file mode 100644
index 0000000..aa00341
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Const16.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class Const16 extends Format21s implements SingleConstant {
+
+  public static final int OPCODE = 0x13;
+  public static final String NAME = "Const16";
+  public static final String SMALI_NAME = "const/16";
+
+  /*package*/ Const16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Const16(int dest, int constant) {
+    super(dest, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int decodedValue() {
+    return BBBB;
+  }
+
+  @Override
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 4) +
+        " (" + decodedValue() + ")");
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.SINGLE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Const4.java b/src/main/java/com/android/tools/r8/code/Const4.java
new file mode 100644
index 0000000..26da02a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Const4.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class Const4 extends Format11n implements SingleConstant {
+
+  public static final int OPCODE = 0x12;
+  public static final String NAME = "Const4";
+  public static final String SMALI_NAME = "const/4";
+
+  Const4(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Const4(int dest, int constant) {
+    super(dest, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int decodedValue() {
+    return B;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + A + ", 0x" + StringUtils.hexString(decodedValue(), 1) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + A + ", 0x" + StringUtils.hexString(decodedValue(), 2) +
+        "  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.SINGLE, A, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java
new file mode 100644
index 0000000..04fda4b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ConstClass extends Format21c {
+
+  public static final int OPCODE = 0x1c;
+  public static final String NAME = "ConstClass";
+  public static final String SMALI_NAME = "const-class";
+
+  ConstClass(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public ConstClass(int dest, DexType type) {
+    super(dest, type);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerTypeReference(getType());
+  }
+
+  public DexType getType() {
+    return (DexType) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConstClass(AA, getType());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstHigh16.java b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
new file mode 100644
index 0000000..3245fa9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstHigh16.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstHigh16 extends Format21h implements SingleConstant {
+
+  public static final int OPCODE = 0x15;
+  public static final String NAME = "ConstHigh16";
+  public static final String SMALI_NAME = "const/high16";
+
+  /*package*/ ConstHigh16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ConstHigh16(int register, int constantHighBits) {
+    super(register, constantHighBits);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int decodedValue() {
+    return BBBB << 16;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 8) +
+        "  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.SINGLE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java
new file mode 100644
index 0000000..ea4bd77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public class ConstString extends Format21c {
+
+  public static final int OPCODE = 0x1a;
+  public static final String NAME = "ConstString";
+  public static final String SMALI_NAME = "const-string";
+
+  ConstString(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getStringMap());
+  }
+
+  public ConstString(int register, DexString string) {
+    super(register, string);
+  }
+
+  public DexString getString() {
+    return (DexString) BBBB;
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", \"" + BBBB.toString() + "\"");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", \"" + BBBB.toString() + "\"");
+  }
+
+  @Override
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    int index = BBBB.getOffset(mapping);
+    if (index != (index & 0xffff)) {
+      throw new InternalCompilerError("String-index overflow.");
+    }
+    super.write(dest, mapping);
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConstString(AA, (DexString) BBBB);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstStringJumbo.java b/src/main/java/com/android/tools/r8/code/ConstStringJumbo.java
new file mode 100644
index 0000000..f68de1b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstStringJumbo.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class ConstStringJumbo extends Format31c {
+
+  public static final int OPCODE = 0x1b;
+  public static final String NAME = "ConstStringJumbo";
+  public static final String SMALI_NAME = "const-string/jumbo";
+
+  ConstStringJumbo(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getStringMap());
+  }
+
+  public ConstStringJumbo(int register, DexString string) {
+    super(register, string);
+  }
+
+  public DexString getString() {
+    return BBBBBBBB;
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", \"" + BBBBBBBB.toString() + "\"");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", \"" + BBBBBBBB.toString() + "\"");
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConstString(AA, BBBBBBBB);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide.java b/src/main/java/com/android/tools/r8/code/ConstWide.java
new file mode 100644
index 0000000..7323f9b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWide extends Format51l implements WideConstant {
+
+  public static final int OPCODE = 0x18;
+  public static final String NAME = "ConstWide";
+  public static final String SMALI_NAME = "const-wide";
+
+  ConstWide(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ConstWide(int dest, long constant) {
+    super(dest, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public long decodedValue() {
+    return BBBBBBBBBBBBBBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+        "L  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.WIDE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide16.java b/src/main/java/com/android/tools/r8/code/ConstWide16.java
new file mode 100644
index 0000000..edd93fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWide16.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWide16 extends Format21s implements WideConstant {
+
+  public static final int OPCODE = 0x16;
+  public static final String NAME = "ConstWide16";
+  public static final String SMALI_NAME = "const-wide/16";
+
+  ConstWide16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ConstWide16(int dest, int constant) {
+    super(dest, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public long decodedValue() {
+    return BBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(
+        "v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) + "L  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.WIDE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWide32.java b/src/main/java/com/android/tools/r8/code/ConstWide32.java
new file mode 100644
index 0000000..540e840
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWide32.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWide32 extends Format31i implements WideConstant {
+
+  public static final int OPCODE = 0x17;
+  public static final String NAME = "ConstWide32";
+  public static final String SMALI_NAME = "const-wide/32";
+
+  ConstWide32(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ConstWide32(int dest, int constant) {
+    super(dest, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public long decodedValue() {
+    return BBBBBBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(
+        "v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) + "  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.WIDE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java b/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
new file mode 100644
index 0000000..864a079
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ConstWideHigh16.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+
+public class ConstWideHigh16 extends Format21h implements WideConstant {
+
+  public static final int OPCODE = 0x19;
+  public static final String NAME = "ConstWideHigh16";
+  public static final String SMALI_NAME = "const-wide/high16";
+
+  ConstWideHigh16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ConstWideHigh16(int dest, int constantHighBits) {
+    super(dest, constantHighBits);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public long decodedValue() {
+    return ((long) BBBB) << 48;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+        " (" + decodedValue() + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(decodedValue(), 16) +
+        "L  # " + decodedValue());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConst(MoveType.WIDE, AA, decodedValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivDouble.java b/src/main/java/com/android/tools/r8/code/DivDouble.java
new file mode 100644
index 0000000..d9164de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivDouble extends Format23x {
+
+  public static final int OPCODE = 0xae;
+  public static final String NAME = "DivDouble";
+  public static final String SMALI_NAME = "div-double";
+
+  DivDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.DOUBLE, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivDouble2Addr.java b/src/main/java/com/android/tools/r8/code/DivDouble2Addr.java
new file mode 100644
index 0000000..02347a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivDouble2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivDouble2Addr extends Format12x {
+
+  public static final int OPCODE = 0xce;
+  public static final String NAME = "DivDouble2Addr";
+  public static final String SMALI_NAME = "div-double/2addr";
+
+  DivDouble2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivDouble2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.DOUBLE, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivFloat.java b/src/main/java/com/android/tools/r8/code/DivFloat.java
new file mode 100644
index 0000000..2a3b360
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivFloat extends Format23x {
+
+  public static final int OPCODE = 0xa9;
+  public static final String NAME = "DivFloat";
+  public static final String SMALI_NAME = "div-float";
+
+  DivFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.FLOAT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivFloat2Addr.java b/src/main/java/com/android/tools/r8/code/DivFloat2Addr.java
new file mode 100644
index 0000000..2b3812f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivFloat2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc9;
+  public static final String NAME = "DivFloat2Addr";
+  public static final String SMALI_NAME = "div-float/2addr";
+
+  DivFloat2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivFloat2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.FLOAT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivInt.java b/src/main/java/com/android/tools/r8/code/DivInt.java
new file mode 100644
index 0000000..cc437e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivInt.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivInt extends Format23x {
+
+  public static final int OPCODE = 0x93;
+  public static final String NAME = "DivInt";
+  public static final String SMALI_NAME = "div-int";
+
+  /*package*/ DivInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.INT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivInt2Addr.java b/src/main/java/com/android/tools/r8/code/DivInt2Addr.java
new file mode 100644
index 0000000..21cff23
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivInt2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb3;
+  public static final String NAME = "DivInt2Addr";
+  public static final String SMALI_NAME = "div-int/2addr";
+
+  /*package*/ DivInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.INT, A, A, B);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivIntLit16.java b/src/main/java/com/android/tools/r8/code/DivIntLit16.java
new file mode 100644
index 0000000..4a4035c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivIntLit16.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd3;
+  public static final String NAME = "DivIntLit16";
+  public static final String SMALI_NAME = "div-int/lit16";
+
+  /*package*/ DivIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivIntLit16(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDivLiteral(NumericType.INT, A, B, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivIntLit8.java b/src/main/java/com/android/tools/r8/code/DivIntLit8.java
new file mode 100644
index 0000000..12b1ee8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivIntLit8.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xdb;
+  public static final String NAME = "DivIntLit8";
+  public static final String SMALI_NAME = "div-int/lit8";
+
+  DivIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivIntLit8(int dest, int left, int constant) {
+    super(dest, left, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDivLiteral(NumericType.INT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivLong.java b/src/main/java/com/android/tools/r8/code/DivLong.java
new file mode 100644
index 0000000..26f66a7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivLong.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DivLong extends Format23x {
+
+  public static final int OPCODE = 0x9e;
+  public static final String NAME = "DivLong";
+  public static final String SMALI_NAME = "div-long";
+
+  DivLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.LONG, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DivLong2Addr.java b/src/main/java/com/android/tools/r8/code/DivLong2Addr.java
new file mode 100644
index 0000000..6a79fbd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DivLong2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DivLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xbe;
+  public static final String NAME = "DivLong2Addr";
+  public static final String SMALI_NAME = "div-long/2addr";
+
+  DivLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DivLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addDiv(NumericType.LONG, A, A, B);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DoubleToFloat.java b/src/main/java/com/android/tools/r8/code/DoubleToFloat.java
new file mode 100644
index 0000000..5c2ce2c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DoubleToFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class DoubleToFloat extends Format12x {
+
+  public static final int OPCODE = 0x8c;
+  public static final String NAME = "DoubleToFloat";
+  public static final String SMALI_NAME = "double-to-float";
+
+  DoubleToFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DoubleToFloat(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.FLOAT, NumericType.DOUBLE, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DoubleToInt.java b/src/main/java/com/android/tools/r8/code/DoubleToInt.java
new file mode 100644
index 0000000..1b4619e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DoubleToInt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DoubleToInt extends Format12x {
+
+  public static final int OPCODE = 0x8a;
+  public static final String NAME = "DoubleToInt";
+  public static final String SMALI_NAME = "doubleto-int";
+
+  DoubleToInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DoubleToInt(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.INT, NumericType.DOUBLE, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DoubleToLong.java b/src/main/java/com/android/tools/r8/code/DoubleToLong.java
new file mode 100644
index 0000000..dfc271d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DoubleToLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class DoubleToLong extends Format12x {
+
+  public static final int OPCODE = 0x8b;
+  public static final String NAME = "DoubleToLong";
+  public static final String SMALI_NAME = "double-to-long";
+
+  DoubleToLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public DoubleToLong(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.LONG, NumericType.DOUBLE, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayData.java b/src/main/java/com/android/tools/r8/code/FillArrayData.java
new file mode 100644
index 0000000..06ba020
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FillArrayData.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class FillArrayData extends Format31t {
+
+  public static final int OPCODE = 0x26;
+  public static final String NAME = "FillArrayData";
+  public static final String SMALI_NAME = "fill-array-data";
+
+  FillArrayData(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public FillArrayData(int value) {
+    super(value, -1);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.resolveAndBuildNewArrayFilledData(AA, getOffset() + getPayloadOffset());
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
new file mode 100644
index 0000000..0fb8f1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+
+public class FillArrayDataPayload extends Nop {
+
+  public final int element_width;
+  public final long size;
+  public final short[] data;
+
+  FillArrayDataPayload(int high, BytecodeStream stream) {
+    super(high, stream);
+    element_width = read16BitValue(stream);
+    size = read32BitValue(stream);
+    assert size * element_width < Integer.MAX_VALUE;
+    // Read the data as shorts (which is faster than reading bytes) as they are always 2-byte
+    // aligned and the size is 2-byte aligned as well.
+    int numberOfShorts = (int) (size * element_width + 1) / 2;
+    data = new short[numberOfShorts];
+    for (int i = 0; i < data.length; i++) {
+      data[i] = (short) readSigned16BitValue(stream);
+    }
+  }
+
+  public FillArrayDataPayload(int element_width, long size, short[] data) {
+    this.element_width = element_width;
+    this.size = size;
+    this.data = data;
+  }
+
+  public boolean isPayload() {
+    return true;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(3, dest);  // Pseudo-opcode = 0x0300
+    write16BitValue(element_width, dest);
+    write32BitValue(size, dest);
+    for (int i = 0; i < data.length; i++) {
+      write16BitValue(data[i], dest);
+    }
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (!super.equals(other)) {
+      return false;
+    }
+    FillArrayDataPayload that = (FillArrayDataPayload) other;
+    return size == that.size && element_width == that.element_width
+        && Arrays.equals(data, that.data);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + element_width;
+    result = 31 * result + (int) (size ^ (size >>> 32));
+    result = 31 * result + Arrays.hashCode(data);
+    return result;
+  }
+
+  public int getSize() {
+    return 4 + data.length;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return super.toString(naming) + "[FillArrayPayload], " +
+        "width: " + element_width + ", size:  " + size;
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    builder.append("    ");
+    builder.append(".array-data");
+    builder.append(" 0x");
+    builder.append(StringUtils.hexString(element_width, 1));
+    builder.append("  # ");
+    builder.append(element_width);
+    builder.append("\n");
+    if (element_width == 1) {
+      // For element width 1 split the 16-bit data units into bytes.
+      for (int i = 0; i < data.length; i++) {
+        for (int j = 0; j < 2; j++) {
+          int value = (data[i] >> j * 8) & 0xff;
+          if (i * 2 + j < size) {
+            builder.append("      ");
+            builder.append("0x");
+            builder.append(StringUtils.hexString(value, 2));
+            builder.append("  # ");
+            builder.append(value);
+            builder.append("\n");
+          }
+        }
+      }
+    } else {
+      // For element width > 1 combine the 16-bit data units into 16-bit/32-bit or 64-bit values.
+      assert element_width == 2 || element_width == 4 || element_width == 8;
+      long value = 0;
+      for (int i = 0; i < data.length; i++) {
+        value = (Short.toUnsignedLong(data[i]) << (16 * (i % (element_width / 2)))) | value;
+        if ((((i + 1) * 2) % element_width) == 0) {
+          builder.append("      ");
+          builder.append("0x");
+          builder.append(StringUtils.hexString(value, element_width * 2));
+          builder.append("  # ");
+          builder.append(value);
+          builder.append("\n");
+          value = 0;
+        }
+      }
+    }
+    builder.append("    ");
+    builder.append(".end array-data");
+    return builder.toString();
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    // FilledArrayData payloads are not represented in the IR.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
new file mode 100644
index 0000000..085b6fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FilledNewArray extends Format35c {
+
+  public static final int OPCODE = 0x24;
+  public static final String NAME = "FilledNewArray";
+  public static final String SMALI_NAME = "filled-new-array";
+
+  FilledNewArray(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public FilledNewArray(int size, DexType type, int v0, int v1, int v2, int v3, int v4) {
+    super(size, type, v0, v1, v2, v3, v4);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public DexType getType() {
+    return (DexType) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeNewArray(getType(), A, new int[]{C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
new file mode 100644
index 0000000..25a941f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FilledNewArrayRange extends Format3rc {
+
+  public static final int OPCODE = 0x25;
+  public static final String NAME = "FilledNewArrayRange";
+  public static final String SMALI_NAME = "filled-new-array/range";
+
+  FilledNewArrayRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public FilledNewArrayRange(int firstContentRegister, int size, DexType type) {
+    super(firstContentRegister, size, type);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public DexType getType() {
+    return (DexType) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRangeNewArray(getType(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FloatToDouble.java b/src/main/java/com/android/tools/r8/code/FloatToDouble.java
new file mode 100644
index 0000000..ce76060
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FloatToDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FloatToDouble extends Format12x {
+
+  public static final int OPCODE = 0x89;
+  public static final String NAME = "FloatToDouble";
+  public static final String SMALI_NAME = "float-to-double";
+
+  FloatToDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public FloatToDouble(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.DOUBLE, NumericType.FLOAT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FloatToInt.java b/src/main/java/com/android/tools/r8/code/FloatToInt.java
new file mode 100644
index 0000000..46fad1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FloatToInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class FloatToInt extends Format12x {
+
+  public static final int OPCODE = 0x87;
+  public static final String NAME = "FloatToInt";
+  public static final String SMALI_NAME = "float-to-int";
+
+  FloatToInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public FloatToInt(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.INT, NumericType.FLOAT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/FloatToLong.java b/src/main/java/com/android/tools/r8/code/FloatToLong.java
new file mode 100644
index 0000000..afbc599
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/FloatToLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class FloatToLong extends Format12x {
+
+  public static final int OPCODE = 0x88;
+  public static final String NAME = "FloatToLong";
+  public static final String SMALI_NAME = "float-to-long";
+
+  FloatToLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public FloatToLong(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.LONG, NumericType.FLOAT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java
new file mode 100644
index 0000000..ee2e6fa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format10t extends Base1Format {
+
+  public final /* offset */ int AA;
+
+  // +AA | op
+  Format10t(int high, BytecodeStream stream) {
+    super(stream);
+    // AA is an offset, so convert to signed.
+    AA = (byte) high;
+  }
+
+  protected Format10t(int AA) {
+    assert Byte.MIN_VALUE <= AA && AA <= Byte.MAX_VALUE;
+    this.AA = AA;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+  }
+
+  public final int hashCode() {
+    return AA ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    return ((Format10t) other).AA == AA;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    String relative = AA >= 0 ? ("+" + AA) : Integer.toString(AA);
+    return formatString(relative + " (" + (getOffset() + AA) + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(":label_" + (getOffset() + AA));
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format10x.java b/src/main/java/com/android/tools/r8/code/Format10x.java
new file mode 100644
index 0000000..132a5b9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format10x.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format10x extends Base1Format {
+
+  // øø | op
+  Format10x(int high, BytecodeStream stream) {
+    // Intentionally left empty.
+    super(stream);
+  }
+
+  protected Format10x() {
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(0, dest);
+  }
+
+
+  public int hashCode() {
+    return getClass().hashCode();
+  }
+
+  public boolean equals(Object other) {
+    return (other != null) && (this.getClass() == other.getClass());
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString(null);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(null);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format11n.java b/src/main/java/com/android/tools/r8/code/Format11n.java
new file mode 100644
index 0000000..561c7c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format11n.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format11n extends Base1Format {
+
+  public final int A, B;
+
+  // #+B | vA | op
+  /*package*/ Format11n(int high, BytecodeStream stream) {
+    super(stream);
+    A = high & 0xf;
+    // Sign extend 4bit value.
+    high >>= 4;
+    if ((high & Constants.S4BIT_SIGN_MASK) != 0) {
+      B = ~(~high & 0xf);
+    } else {
+      B = high & 0xf;
+    }
+  }
+
+  /*package*/ Format11n(int A, int B) {
+    assert 0 <= A && A <= Constants.U4BIT_MAX;
+    assert Constants.S4BIT_MIN <= B && B <= Constants.S4BIT_MAX;
+    this.A = A;
+    this.B = B;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(B, A, dest);
+  }
+
+  public final int hashCode() {
+    return ((A << 4) | B) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format11n o = (Format11n) other;
+    return o.A == A && o.B == B;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + A + ", #" + B);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format11x.java b/src/main/java/com/android/tools/r8/code/Format11x.java
new file mode 100644
index 0000000..baf4b57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format11x.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format11x extends Base1Format {
+
+  public final int AA;
+
+  // vAA | op
+  Format11x(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+  }
+
+  protected Format11x(int AA) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    this.AA = AA;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+  }
+
+  public final int hashCode() {
+    return AA ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    return ((Format11x) other).AA == AA;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format12x.java b/src/main/java/com/android/tools/r8/code/Format12x.java
new file mode 100644
index 0000000..4338b46
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format12x.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format12x extends Base1Format {
+
+  public final int A, B;
+
+  // vB | vA | op
+  Format12x(int high, BytecodeStream stream) {
+    super(stream);
+    A = high & 0xF;
+    B = (high >> 4) & 0xF;
+  }
+
+  Format12x(int A, int B) {
+    assert 0 <= A && A <= Constants.U4BIT_MAX;
+    assert 0 <= B && B <= Constants.U4BIT_MAX;
+    this.A = A;
+    this.B = B;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(B, A, dest);
+  }
+
+  public final int hashCode() {
+    return ((A << 4) | B) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format12x o = (Format12x) other;
+    return o.A == A && o.B == B;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + A + ", v" + B);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + A + ", v" + B);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java
new file mode 100644
index 0000000..4f6f235
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format20t extends Base2Format {
+
+  public final /* offset */ int AAAA;
+
+  // øø | op | +AAAA
+  Format20t(int high, BytecodeStream stream) {
+    super(stream);
+    AAAA = readSigned16BitValue(stream);
+  }
+
+  protected Format20t(int AAAA) {
+    assert Short.MIN_VALUE <= AAAA && AAAA <= Short.MAX_VALUE;
+    this.AAAA = AAAA;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(0, dest);
+    write16BitValue(AAAA, dest);
+  }
+
+  public final int hashCode() {
+    return AAAA ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    return ((Format20t) other).AAAA == AAAA;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("" + AAAA + " (" + (getOffset() + AAAA) + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(":label_" + (getOffset() + AAAA));
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21c.java b/src/main/java/com/android/tools/r8/code/Format21c.java
new file mode 100644
index 0000000..411bc02
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21c.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+abstract class Format21c extends Base2Format {
+
+  public final int AA;
+  public IndexedDexItem BBBB;
+
+  // AA | op | [type|field|string]@BBBB
+  Format21c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+    super(stream);
+    AA = high;
+    BBBB = map[read16BitValue(stream)];
+  }
+
+  protected Format21c(int AA, IndexedDexItem BBBB) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    this.AA = AA;
+    this.BBBB = BBBB;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitReference(BBBB, dest, mapping);
+  }
+
+  public final int hashCode() {
+    return ((BBBB.hashCode() << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format21c o = (Format21c) other;
+    return o.AA == AA && o.BBBB.equals(BBBB);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString(
+        "v" + AA + ", " + (naming == null ? BBBB.toString() : naming.originalNameOf(BBBB)));
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    // TODO(sgjesse): Add support for smali name mapping.
+    return formatSmaliString("v" + AA + ", " + BBBB.toSmaliString());
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format21c o = (Format21c) other;
+    return o.AA == AA && equality.test(BBBB, o.BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21h.java b/src/main/java/com/android/tools/r8/code/Format21h.java
new file mode 100644
index 0000000..5f3f247
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21h.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import java.nio.ShortBuffer;
+
+abstract class Format21h extends Base2Format {
+
+  public final int AA;
+  public final int BBBB;
+
+  // AA | op | BBBB0000[00000000]
+  /*package*/ Format21h(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBB = read16BitValue(stream);
+  }
+
+  /*package*/ Format21h(int AA, int BBBB) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    assert 0 <= BBBB && BBBB <= Constants.U16BIT_MAX;
+    this.AA = AA;
+    this.BBBB = BBBB;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitValue(BBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((BBBB << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format21h o = (Format21h) other;
+    return o.AA == AA && o.BBBB == BBBB;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21s.java b/src/main/java/com/android/tools/r8/code/Format21s.java
new file mode 100644
index 0000000..79d15ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21s.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+
+abstract class Format21s extends Base2Format {
+
+  public final int AA;
+  public final int BBBB;
+
+  // AA | op | #+BBBB
+  /*package*/ Format21s(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBB = readSigned16BitValue(stream);
+  }
+
+  /*package*/ Format21s(int AA, int BBBB) {
+    assert Short.MIN_VALUE <= BBBB && BBBB <= Short.MAX_VALUE;
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    this.AA = AA;
+    this.BBBB = BBBB;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitValue(BBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((BBBB << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format21s o = (Format21s) other;
+    return o.AA == AA && o.BBBB == BBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", #" + BBBB);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", 0x" + StringUtils.hexString(BBBB, 4) + "  # " + BBBB);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
new file mode 100644
index 0000000..a49abb5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format21t extends Base2Format {
+
+  public final int AA;
+  public final /* offset */ int BBBB;
+
+  // AA | op | +BBBB
+  Format21t(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBB = readSigned16BitValue(stream);
+  }
+
+  Format21t(int register, int offset) {
+    assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+    assert 0 <= register && register <= Constants.U8BIT_MAX;
+    AA = register;
+    BBBB = offset;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitValue(BBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((BBBB << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format21t o = (Format21t) other;
+    return o.AA == AA && o.BBBB == BBBB;
+  }
+
+  public abstract Type getType();
+
+  @Override
+  public int[] getTargets() {
+    return new int[]{BBBB, getSize()};
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    int offset = getOffset();
+    int size = getSize();
+    builder.addIfZero(getType(), AA, offset + BBBB, offset + size);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", +" + BBBB + " (" + (getOffset() + BBBB) + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBB));
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22b.java b/src/main/java/com/android/tools/r8/code/Format22b.java
new file mode 100644
index 0000000..a92a9f1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22b.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+
+public abstract class Format22b extends Base2Format {
+
+  public final int AA;
+  public final int BB;
+  public final int CC;
+
+  // vAA | op | #+CC | VBB
+  /*package*/ Format22b(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    CC = readSigned8BitValue(stream);
+    BB = read8BitValue(stream);
+  }
+
+  /*package*/ Format22b(int AA, int BB, int CC) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    assert 0 <= BB && BB <= Constants.U8BIT_MAX;
+    assert Byte.MIN_VALUE <= CC && CC <= Byte.MAX_VALUE;
+    this.AA = AA;
+    this.BB = BB;
+    this.CC = CC;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitValue(combineBytes(CC, BB), dest);
+  }
+
+  public final int hashCode() {
+    return ((AA << 16) | (BB << 8) | CC) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format22b o = (Format22b) other;
+    return o.AA == AA && o.BB == BB && o.CC == CC;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", v" + BB + ", #" + CC);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(
+        "v" + AA + ", v" + BB + ", 0x" + StringUtils.hexString(CC, 2) + "  # " + CC);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22c.java b/src/main/java/com/android/tools/r8/code/Format22c.java
new file mode 100644
index 0000000..f97089c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22c.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+abstract class Format22c extends Base2Format {
+
+  public final int A;
+  public final int B;
+  public IndexedDexItem CCCC;
+
+  // vB | vA | op | [type|field]@CCCC
+  /*package*/ Format22c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+    super(stream);
+    A = high & 0xf;
+    B = (high >> 4) & 0xf;
+    CCCC = map[read16BitValue(stream)];
+  }
+
+  /*package*/ Format22c(int A, int B, IndexedDexItem CCCC) {
+    assert 0 <= A && A <= Constants.U4BIT_MAX;
+    assert 0 <= B && B <= Constants.U4BIT_MAX;
+    this.A = A;
+    this.B = B;
+    this.CCCC = CCCC;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(B, A, dest);
+    write16BitReference(CCCC, dest, mapping);
+  }
+
+  public final int hashCode() {
+    return ((CCCC.hashCode() << 8) | (A << 4) | B) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format22c o = (Format22c) other;
+    return o.A == A && o.B == B && o.CCCC.equals(CCCC);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString(
+        "v" + A + ", v" + B + ", " + (naming == null ? CCCC : naming.originalNameOf(CCCC)));
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    // TODO(sgjesse): Add support for smali name mapping.
+    return formatSmaliString("v" + A + ", v" + B + ", " + CCCC.toSmaliString());
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    CCCC.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format22c o = (Format22c) other;
+    return o.A == A && o.B == B && equality.test(CCCC, o.CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22s.java b/src/main/java/com/android/tools/r8/code/Format22s.java
new file mode 100644
index 0000000..6b045f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22s.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+
+public abstract class Format22s extends Base2Format {
+
+  public final int A;
+  public final int B;
+  public final int CCCC;
+
+  // vB | vA | op | #+CCCC
+  /*package*/ Format22s(int high, BytecodeStream stream) {
+    super(stream);
+    A = high & 0xf;
+    B = (high >> 4) & 0xf;
+    CCCC = readSigned16BitValue(stream);
+  }
+
+  /*package*/ Format22s(int A, int B, int CCCC) {
+    assert 0 <= A && A <= Constants.U4BIT_MAX;
+    assert 0 <= B && B <= Constants.U4BIT_MAX;
+    assert Short.MIN_VALUE <= CCCC && CCCC <= Short.MAX_VALUE;
+    this.A = A;
+    this.B = B;
+    this.CCCC = CCCC;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(B, A, dest);
+    write16BitValue(CCCC, dest);
+  }
+
+  public final int hashCode() {
+    return ((CCCC << 8) | (A << 4) | B) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format22s o = (Format22s) other;
+    return o.A == A && o.B == B && o.CCCC == CCCC;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + A + ", v" + B + ", #" + CCCC);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(
+        "v" + A + ", v" + B + ", 0x" + StringUtils.hexString(CCCC, 4) + "  # " + CCCC);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
new file mode 100644
index 0000000..04a3d2e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format22t extends Base2Format {
+
+  public final int A;
+  public final int B;
+  public final /* offset */ int CCCC;
+
+  // vB | vA | op | +CCCC
+  Format22t(int high, BytecodeStream stream) {
+    super(stream);
+    A = high & 0xf;
+    B = (high >> 4) & 0xf;
+    CCCC = readSigned16BitValue(stream);
+  }
+
+  Format22t(int register1, int register2, int offset) {
+    assert 0 <= register1 && register1 <= Constants.U4BIT_MAX;
+    assert 0 <= register2 && register2 <= Constants.U4BIT_MAX;
+    assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+    A = register1;
+    B = register2;
+    CCCC = offset;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(B, A, dest);
+    write16BitValue(CCCC, dest);
+  }
+
+  public final int hashCode() {
+    return ((CCCC << 8) | (B << 4) | A) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format22t o = (Format22t) other;
+    return o.A == A && o.B == B && o.CCCC == CCCC;
+  }
+
+  public abstract Type getType();
+
+  @Override
+  public int[] getTargets() {
+    return new int[]{CCCC, getSize()};
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    int offset = getOffset();
+    int size = getSize();
+    builder.addIf(getType(), A, B, offset + CCCC, offset + size);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + A + ", v" + B + ", +" + CCCC + " (" + (getOffset() + CCCC) + ")");
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + A + ", v" + B + ", :label_" + (getOffset() + CCCC));
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format22x.java b/src/main/java/com/android/tools/r8/code/Format22x.java
new file mode 100644
index 0000000..789b12c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format22x.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format22x extends Base2Format {
+
+  public final int AA;
+  public final int BBBB;
+
+  // AA | op | vBBBB
+  Format22x(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBB = read16BitValue(stream);
+  }
+
+  Format22x(int dest, int src) {
+    assert 0 <= dest && dest <= Constants.U8BIT_MAX;
+    assert 0 <= src && src <= Constants.U16BIT_MAX;
+    AA = dest;
+    BBBB = src;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitValue(BBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((BBBB << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format22x o = (Format22x) other;
+    return o.AA == AA && o.BBBB == BBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", v" + BBBB);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", v" + BBBB);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format23x.java b/src/main/java/com/android/tools/r8/code/Format23x.java
new file mode 100644
index 0000000..fd3bdab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format23x.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format23x extends Base2Format {
+
+  public final int AA;
+  public final int BB;
+  public final int CC;
+
+  // vAA | op | vCC | vBB
+  Format23x(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    CC = read8BitValue(stream);
+    BB = read8BitValue(stream);
+  }
+
+  Format23x(int AA, int BB, int CC) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    assert 0 <= BB && BB <= Constants.U8BIT_MAX;
+    assert 0 <= CC && CC <= Constants.U8BIT_MAX;
+    this.AA = AA;
+    this.BB = BB;
+    this.CC = CC;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitValue(combineBytes(CC, BB), dest);
+  }
+
+  public final int hashCode() {
+    return ((AA << 16) | (BB << 8) | CC) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format23x o = (Format23x) other;
+    return o.AA == AA && o.BB == BB && o.CC == CC;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", v" + BB + ", v" + CC);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", v" + BB + ", v" + CC);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java
new file mode 100644
index 0000000..927c879
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format30t extends Base3Format {
+
+  public final /* offset */ int AAAAAAAA;
+
+  // øø | op | AAAAlo | AAAAhi
+  Format30t(int high, BytecodeStream stream) {
+    super(stream);
+    AAAAAAAA = readSigned32BitValue(stream);
+  }
+
+  protected Format30t(int AAAAAAAA) {
+    this.AAAAAAAA = AAAAAAAA;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(0, dest);
+    write32BitValue(AAAAAAAA, dest);
+  }
+
+  public final int hashCode() {
+    return AAAAAAAA ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    return ((Format30t) other).AAAAAAAA == AAAAAAAA;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("" + AAAAAAAA);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString(":label_" + (getOffset() + AAAAAAAA));
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format31c.java b/src/main/java/com/android/tools/r8/code/Format31c.java
new file mode 100644
index 0000000..b86d58b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format31c.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+abstract class Format31c extends Base3Format {
+
+  public final int AA;
+  public DexString BBBBBBBB;
+
+  // vAA | op | string@BBBBlo | string@#+BBBBhi
+  Format31c(int high, BytecodeStream stream, DexString[] map) {
+    super(stream);
+    AA = high;
+    BBBBBBBB = map[(int) read32BitValue(stream)];
+  }
+
+  Format31c(int AA, DexString BBBBBBBB) {
+    assert 0 <= AA && AA <= U8BIT_MAX;
+    this.AA = AA;
+    this.BBBBBBBB = BBBBBBBB;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write32BitReference(BBBBBBBB, dest, mapping);
+  }
+
+  public final int hashCode() {
+    return ((BBBBBBBB.hashCode() << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format31c o = (Format31c) other;
+    return o.AA == AA && o.BBBBBBBB.equals(BBBBBBBB);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString(
+        "v" + AA + ", " + (naming == null ? BBBBBBBB : naming.originalNameOf(BBBBBBBB)));
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBBBBBB.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format31c o = (Format31c) other;
+    return o.AA == AA && equality.test(BBBBBBBB, o.BBBBBBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format31i.java b/src/main/java/com/android/tools/r8/code/Format31i.java
new file mode 100644
index 0000000..09192d6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format31i.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format31i extends Base3Format {
+
+  public final int AA;
+  public final int BBBBBBBB;
+
+  // vAA | op | #+BBBBlo | #+BBBBhi
+  /*package*/ Format31i(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBBBBBB = readSigned32BitValue(stream);
+  }
+
+  /*package*/ Format31i(int AA, int BBBBBBBB) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    this.AA = AA;
+    this.BBBBBBBB = BBBBBBBB;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write32BitValue(BBBBBBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((BBBBBBBB << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format31i o = (Format31i) other;
+    return o.AA == AA && o.BBBBBBBB == BBBBBBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", #" + BBBBBBBB);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format31t.java b/src/main/java/com/android/tools/r8/code/Format31t.java
new file mode 100644
index 0000000..f179017
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format31t.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+public abstract class Format31t extends Base3Format {
+
+  public final int AA;
+  protected /* offset */ int BBBBBBBB;
+
+  // vAA | op | +BBBBlo | +BBBBhi
+  Format31t(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBBBBBB = readSigned32BitValue(stream);
+  }
+
+  Format31t(int register, int payloadOffset) {
+    assert 0 <= register && register <= Constants.U8BIT_MAX;
+    AA = register;
+    BBBBBBBB = payloadOffset;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    assert (getOffset() + BBBBBBBB) % 2 == 0;
+    write32BitValue(BBBBBBBB, dest);
+  }
+
+  public boolean hasPayload() {
+    return true;
+  }
+
+  public int getPayloadOffset() {
+    return BBBBBBBB;
+  }
+
+  public void setPayloadOffset(int offset) {
+    BBBBBBBB = offset;
+  }
+
+  public final int hashCode() {
+    return ((BBBBBBBB << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format31t o = (Format31t) other;
+    return o.AA == AA && o.BBBBBBBB == BBBBBBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", +" + BBBBBBBB + " (" + (getOffset() + BBBBBBBB) + ")");
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format32x.java b/src/main/java/com/android/tools/r8/code/Format32x.java
new file mode 100644
index 0000000..1dace6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format32x.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.dex.Constants.U16BIT_MAX;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format32x extends Base3Format {
+
+  public final int AAAA;
+  public final int BBBB;
+
+  // øø | op | AAAA | BBBB
+  Format32x(int high, BytecodeStream stream) {
+    super(stream);
+    AAAA = read16BitValue(stream);
+    BBBB = read16BitValue(stream);
+  }
+
+  Format32x(int dest, int src) {
+    assert 0 <= dest && dest <= U16BIT_MAX;
+    assert 0 <= src && src <= U16BIT_MAX;
+    AAAA = dest;
+    BBBB = src;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(0, dest);
+    write16BitValue(AAAA, dest);
+    write16BitValue(BBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((AAAA << 16) | BBBB) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format32x o = (Format32x) other;
+    return o.AAAA == AAAA && o.BBBB == BBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AAAA + ", v" + BBBB);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AAAA + ", v" + BBBB);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format35c.java b/src/main/java/com/android/tools/r8/code/Format35c.java
new file mode 100644
index 0000000..03771e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format35c.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+public abstract class Format35c extends Base3Format {
+
+  public final int A;
+  public final int C;
+  public final int D;
+  public final int E;
+  public final int F;
+  public final int G;
+  public IndexedDexItem BBBB;
+
+  // A | G | op | BBBB | F | E | D | C
+  Format35c(int high, BytecodeStream stream, IndexedDexItem[] map) {
+    super(stream);
+    G = high & 0xf;
+    A = (high >> 4) & 0xf;
+    BBBB = map[read16BitValue(stream)];
+    int next = read8BitValue(stream);
+    E = next & 0xf;
+    F = (next >> 4) & 0xf;
+    next = read8BitValue(stream);
+    C = next & 0xf;
+    D = (next >> 4) & 0xf;
+  }
+
+  protected Format35c(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+    assert 0 <= A && A <= Constants.U4BIT_MAX;
+    assert 0 <= C && C <= Constants.U4BIT_MAX;
+    assert 0 <= D && D <= Constants.U4BIT_MAX;
+    assert 0 <= E && E <= Constants.U4BIT_MAX;
+    assert 0 <= F && F <= Constants.U4BIT_MAX;
+    assert 0 <= G && G <= Constants.U4BIT_MAX;
+    this.A = A;
+    this.BBBB = BBBB;
+    this.C = C;
+    this.D = D;
+    this.E = E;
+    this.F = F;
+    this.G = G;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(A, G, dest);
+    write16BitReference(BBBB, dest, mapping);
+    write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
+  }
+
+  public final int hashCode() {
+    return ((BBBB.hashCode() << 24) | (A << 20) | (C << 16) | (D << 12) | (E << 8) | (F << 4)
+        | G) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format35c o = (Format35c) other;
+    return o.A == A && o.C == C && o.D == D && o.E == E && o.F == F && o.G == G
+        && o.BBBB.equals(BBBB);
+  }
+
+  private void appendRegisterArguments(StringBuilder builder, String separator) {
+    builder.append("{ ");
+    int[] values = new int[]{C, D, E, F, G};
+    for (int i = 0; i < A; i++) {
+      if (i != 0) {
+        builder.append(separator);
+      }
+      builder.append("v").append(values[i]);
+    }
+    builder.append(" }");
+  }
+
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterArguments(builder, " ");
+    builder.append(" ");
+    if (naming == null) {
+      builder.append(BBBB.toSmaliString());
+    } else {
+      builder.append(naming.originalNameOf(BBBB));
+    }
+    return formatString(builder.toString());
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterArguments(builder, ", ");
+    builder.append(", ");
+    // TODO(sgjesse): Add support for smali name mapping.
+    builder.append(BBBB.toSmaliString());
+    return formatSmaliString(builder.toString());
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format35c o = (Format35c) other;
+    return o.A == A && o.C == C && o.D == D && o.E == E && o.F == F && o.G == G
+        && equality.test(BBBB, o.BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format3rc.java b/src/main/java/com/android/tools/r8/code/Format3rc.java
new file mode 100644
index 0000000..b8ee74e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format3rc.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+public abstract class Format3rc extends Base3Format {
+
+  public final int AA;
+  public final int CCCC;
+  public IndexedDexItem BBBB;
+
+  // AA | op | [meth|type]@BBBBB | CCCC
+  Format3rc(int high, BytecodeStream stream, IndexedDexItem[] map) {
+    super(stream);
+    this.AA = high;
+    this.BBBB = map[read16BitValue(stream)];
+    this.CCCC = read16BitValue(stream);
+  }
+
+  Format3rc(int firstArgumentRegister, int argumentCount, IndexedDexItem dexItem) {
+    assert 0 <= firstArgumentRegister && firstArgumentRegister <= Constants.U16BIT_MAX;
+    assert 0 <= argumentCount && argumentCount <= Constants.U8BIT_MAX;
+    this.CCCC = firstArgumentRegister;
+    this.AA = argumentCount;
+    BBBB = dexItem;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitReference(BBBB, dest, mapping);
+    write16BitValue(CCCC, dest);
+  }
+
+  public final int hashCode() {
+    return ((CCCC << 24) | (BBBB.hashCode() << 4) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format3rc o = (Format3rc) other;
+    return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB);
+  }
+
+  private void appendRegisterRange(StringBuilder builder) {
+    builder.append("{ ");
+    builder.append("v").append(CCCC);
+    if (AA != 1) {
+      builder.append(" .. v").append(CCCC + AA - 1);
+    }
+    builder.append(" }");
+  }
+
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterRange(builder);
+    builder.append(" ");
+    if (naming == null) {
+      builder.append(BBBB.toSmaliString());
+    } else {
+      builder.append(naming.originalNameOf(BBBB));
+    }
+    return formatString(builder.toString());
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterRange(builder);
+    builder.append(", ");
+    // TODO(sgjesse): Add support for smali name mapping.
+    builder.append(BBBB.toSmaliString());
+    return formatSmaliString(builder.toString());
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format3rc o = (Format3rc) other;
+    return o.AA == AA && o.CCCC == CCCC && equality.test(BBBB, o.BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java
new file mode 100644
index 0000000..bea1807
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+/** Format45cc for instructions of size 4, with 5 registers and 2 constant pool index. */
+public abstract class Format45cc extends Base4Format {
+
+  public final int A;
+  public final int C;
+  public final int D;
+  public final int E;
+  public final int F;
+  public final int G;
+  public DexMethod BBBB;
+  public DexProto HHHH;
+
+  Format45cc(int high, BytecodeStream stream, DexMethod[] methodMap, DexProto[] protoMap) {
+    super(stream);
+    G = high & 0xf;
+    A = (high >> 4) & 0xf;
+    BBBB = methodMap[read16BitValue(stream)];
+    int next = read8BitValue(stream);
+    E = next & 0xf;
+    F = (next >> 4) & 0xf;
+    next = read8BitValue(stream);
+    C = next & 0xf;
+    D = (next >> 4) & 0xf;
+    HHHH = protoMap[read16BitValue(stream)];
+  }
+
+  // A | G | op | [meth]@BBBB | F | E | D | C | [proto]@HHHH
+  protected Format45cc(int A, DexMethod BBBB, DexProto HHHH, int C, int D, int E, int F, int G) {
+    assert 0 <= A && A <= U4BIT_MAX;
+    assert 0 <= C && C <= U4BIT_MAX;
+    assert 0 <= D && D <= U4BIT_MAX;
+    assert 0 <= E && E <= U4BIT_MAX;
+    assert 0 <= F && F <= U4BIT_MAX;
+    assert 0 <= G && G <= U4BIT_MAX;
+    this.A = A;
+    this.BBBB = BBBB;
+    this.HHHH = HHHH;
+    this.C = C;
+    this.D = D;
+    this.E = E;
+    this.F = F;
+    this.G = G;
+  }
+
+  public final int hashCode() {
+    return ((HHHH.hashCode() << 28)
+            | (BBBB.hashCode() << 24)
+            | (A << 20)
+            | (C << 16)
+            | (D << 12)
+            | (E << 8)
+            | (F << 4)
+            | G)
+        ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format45cc o = (Format45cc) other;
+    return o.A == A
+        && o.C == C
+        && o.D == D
+        && o.E == E
+        && o.F == F
+        && o.G == G
+        && o.BBBB.equals(BBBB)
+        && o.HHHH.equals(HHHH);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+    HHHH.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(A, G, dest);
+    write16BitReference(BBBB, dest, mapping);
+    write16BitValue(combineBytes(makeByte(F, E), makeByte(D, C)), dest);
+    write16BitReference(HHHH, dest, mapping);
+  }
+
+  @Override
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterArguments(builder, ", ");
+    builder.append(", ");
+    // TODO(sgjesse): Add support for smali name mapping.
+    builder.append(BBBB.toSmaliString());
+    builder.append(", ");
+    builder.append(HHHH.toSmaliString());
+    return formatSmaliString(builder.toString());
+  }
+
+  @Override
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterArguments(builder, " ");
+    builder.append(" ");
+    builder.append(toString(BBBB, naming));
+    builder.append(", ");
+    builder.append(toString(HHHH, naming));
+    return formatString(builder.toString());
+  }
+
+  private String toString(IndexedDexItem indexedDexItem, ClassNameMapper naming) {
+    String str;
+    if (naming == null) {
+      str = indexedDexItem.toSmaliString();
+    } else {
+      str = naming.originalNameOf(indexedDexItem);
+    }
+    return str;
+  }
+
+  private void appendRegisterArguments(StringBuilder builder, String separator) {
+    builder.append("{ ");
+    int[] values = new int[] {C, D, E, F, G};
+    for (int i = 0; i < A; i++) {
+      if (i != 0) {
+        builder.append(separator);
+      }
+      builder.append("v").append(values[i]);
+    }
+    builder.append(" }");
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return BBBB;
+  }
+
+  @Override
+  public DexProto getProto() {
+    return HHHH;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java
new file mode 100644
index 0000000..7072805
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+/** Format4rcc for instructions of size 4, with a range of registers and 2 constant pool index. */
+public abstract class Format4rcc extends Base4Format {
+
+  public final int AA;
+  public final int CCCC;
+  public DexMethod BBBB;
+  public DexProto HHHH;
+
+  // AA | op | [meth]@BBBB | CCCC | [proto]@HHHH
+  Format4rcc(int high, BytecodeStream stream, DexMethod[] methodMap, DexProto[] protoMap) {
+    super(stream);
+    this.AA = high;
+    this.BBBB = methodMap[read16BitValue(stream)];
+    this.CCCC = read16BitValue(stream);
+    this.HHHH = protoMap[read16BitValue(stream)];
+  }
+
+  Format4rcc(int firstArgumentRegister, int argumentCount, DexMethod method, DexProto proto) {
+    assert 0 <= firstArgumentRegister && firstArgumentRegister <= Constants.U16BIT_MAX;
+    assert 0 <= argumentCount && argumentCount <= Constants.U8BIT_MAX;
+    this.CCCC = firstArgumentRegister;
+    this.AA = argumentCount;
+    BBBB = method;
+    HHHH = proto;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write16BitReference(BBBB, dest, mapping);
+    write16BitValue(CCCC, dest);
+    write16BitReference(HHHH, dest, mapping);
+  }
+
+  public final int hashCode() {
+    return ((CCCC << 24) | (HHHH.hashCode() << 12) | (BBBB.hashCode() << 4) | AA)
+        ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format4rcc o = (Format4rcc) other;
+    return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB) & o.HHHH.equals(HHHH);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterRange(builder);
+    builder.append(" ");
+    if (naming == null) {
+      builder.append(BBBB.toSmaliString());
+    } else {
+      builder.append(naming.originalNameOf(BBBB));
+    }
+    if (naming == null) {
+      builder.append(HHHH.toSmaliString());
+    } else {
+      builder.append(naming.originalNameOf(HHHH));
+    }
+    return formatString(builder.toString());
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    appendRegisterRange(builder);
+    builder.append(", ");
+    // TODO(sgjesse): Add support for smali name mapping.
+    builder.append(BBBB.toSmaliString());
+    builder.append(", ");
+    builder.append(HHHH.toSmaliString());
+    return formatSmaliString(builder.toString());
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    BBBB.collectIndexedItems(indexedItems);
+    HHHH.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    if (other == null || (this.getClass() != other.getClass())) {
+      return false;
+    }
+    Format4rcc o = (Format4rcc) other;
+    return o.AA == AA
+        && o.CCCC == CCCC
+        && equality.test(BBBB, o.BBBB)
+        && equality.test(HHHH, o.HHHH);
+  }
+
+  private void appendRegisterRange(StringBuilder builder) {
+    builder.append("{ ");
+    builder.append("v").append(CCCC);
+    if (AA != 1) {
+      builder.append(" .. v").append(CCCC + AA - 1);
+    }
+    builder.append(" }");
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return BBBB;
+  }
+
+  @Override
+  public DexProto getProto() {
+    return HHHH;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Format51l.java b/src/main/java/com/android/tools/r8/code/Format51l.java
new file mode 100644
index 0000000..d135ffe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Format51l.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import java.nio.ShortBuffer;
+
+abstract class Format51l extends Base5Format {
+
+  public final int AA;
+  public final long BBBBBBBBBBBBBBBB;
+
+  // AA | op | BBBB | BBBB | BBBB | BBBB
+  Format51l(int high, BytecodeStream stream) {
+    super(stream);
+    AA = high;
+    BBBBBBBBBBBBBBBB = read64BitValue(stream);
+  }
+
+  public Format51l(int AA, long BBBBBBBBBBBBBBBB) {
+    assert 0 <= AA && AA <= Constants.U8BIT_MAX;
+    this.AA = AA;
+    this.BBBBBBBBBBBBBBBB = BBBBBBBBBBBBBBBB;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(AA, dest);
+    write64BitValue(BBBBBBBBBBBBBBBB, dest);
+  }
+
+  public final int hashCode() {
+    return ((((int) BBBBBBBBBBBBBBBB) << 8) | AA) ^ getClass().hashCode();
+  }
+
+  public final boolean equals(Object other) {
+    if (other == null || this.getClass() != other.getClass()) {
+      return false;
+    }
+    Format51l o = (Format51l) other;
+    return o.AA == AA && o.BBBBBBBBBBBBBBBB == BBBBBBBBBBBBBBBB;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    return formatString("v" + AA + ", #" + BBBBBBBBBBBBBBBB);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    // No references.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Goto.java b/src/main/java/com/android/tools/r8/code/Goto.java
new file mode 100644
index 0000000..99ecc9b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Goto.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Goto extends Format10t {
+
+  public static final int OPCODE = 0x28;
+  public static final String NAME = "Goto";
+  public static final String SMALI_NAME = "goto";
+
+  Goto(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Goto(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return new int[] { AA };
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addGoto(getOffset() + AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Goto16.java b/src/main/java/com/android/tools/r8/code/Goto16.java
new file mode 100644
index 0000000..c1d65f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Goto16.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Goto16 extends Format20t {
+
+  public static final int OPCODE = 0x29;
+  public static final String NAME = "Goto16";
+  public static final String SMALI_NAME = "goto/16";
+
+  Goto16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Goto16(int AAAA) {
+    super(AAAA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return new int[]{AAAA};
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addGoto(getOffset() + AAAA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Goto32.java b/src/main/java/com/android/tools/r8/code/Goto32.java
new file mode 100644
index 0000000..d1c2a92
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Goto32.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Goto32 extends Format30t {
+
+  public static final int OPCODE = 0x2a;
+  public static final String NAME = "Goto32";
+  public static final String SMALI_NAME = "goto/32";
+
+  Goto32(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Goto32(int AAAAAAAA) {
+    super(AAAAAAAA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return new int[]{AAAAAAAA};
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addGoto(getOffset() + AAAAAAAA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfEq.java b/src/main/java/com/android/tools/r8/code/IfEq.java
new file mode 100644
index 0000000..a65051e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfEq.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfEq extends Format22t {
+
+  public static final int OPCODE = 0x32;
+  public static final String NAME = "IfEq";
+  public static final String SMALI_NAME = "if-eq";
+
+  IfEq(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfEq(int register1, int register2, int offset) {
+    super(register1, register2, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.EQ;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfEqz.java b/src/main/java/com/android/tools/r8/code/IfEqz.java
new file mode 100644
index 0000000..dbcb65b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfEqz.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfEqz extends Format21t {
+
+  public static final int OPCODE = 0x38;
+  public static final String NAME = "IfEqz";
+  public static final String SMALI_NAME = "if-eqz";
+
+  IfEqz(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfEqz(int register, int offset) {
+    super(register, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.EQ;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGe.java b/src/main/java/com/android/tools/r8/code/IfGe.java
new file mode 100644
index 0000000..e54280e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGe.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGe extends Format22t {
+
+  public static final int OPCODE = 0x35;
+  public static final String NAME = "IfGe";
+  public static final String SMALI_NAME = "if-ge";
+
+  IfGe(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfGe(int register1, int register2, int offset) {
+    super(register1, register2, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.GE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGez.java b/src/main/java/com/android/tools/r8/code/IfGez.java
new file mode 100644
index 0000000..04216c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGez.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGez extends Format21t {
+
+  public static final int OPCODE = 0x3b;
+  public static final String NAME = "IfGez";
+  public static final String SMALI_NAME = "if-gez";
+
+  IfGez(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfGez(int register, int offset) {
+    super(register, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.GE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGt.java b/src/main/java/com/android/tools/r8/code/IfGt.java
new file mode 100644
index 0000000..75b2f88
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGt extends Format22t {
+
+  public static final int OPCODE = 0x36;
+  public static final String NAME = "IfGt";
+  public static final String SMALI_NAME = "if-gt";
+
+  IfGt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfGt(int register1, int register2, int offset) {
+    super(register1, register2, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.GT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfGtz.java b/src/main/java/com/android/tools/r8/code/IfGtz.java
new file mode 100644
index 0000000..e4cc597
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfGtz.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfGtz extends Format21t {
+
+  public static final int OPCODE = 0x3c;
+  public static final String NAME = "IfGtz";
+  public static final String SMALI_NAME = "if-gtz";
+
+  IfGtz(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfGtz(int register, int offset) {
+    super(register, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.GT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLe.java b/src/main/java/com/android/tools/r8/code/IfLe.java
new file mode 100644
index 0000000..1a1f0b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLe.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLe extends Format22t {
+
+  public static final int OPCODE = 0x37;
+  public static final String NAME = "IfLe";
+  public static final String SMALI_NAME = "if-le";
+
+  IfLe(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfLe(int register1, int register2, int offset) {
+    super(register1, register2, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.LE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLez.java b/src/main/java/com/android/tools/r8/code/IfLez.java
new file mode 100644
index 0000000..ff42a07
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLez.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLez extends Format21t {
+
+  public static final int OPCODE = 0x3d;
+  public static final String NAME = "IfLez";
+  public static final String SMALI_NAME = "if-lez";
+
+  IfLez(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfLez(int register, int offset) {
+    super(register, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.LE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLt.java b/src/main/java/com/android/tools/r8/code/IfLt.java
new file mode 100644
index 0000000..3b45ece
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLt extends Format22t {
+
+  public static final int OPCODE = 0x34;
+  public static final String NAME = "IfLt";
+  public static final String SMALI_NAME = "if-lt";
+
+  IfLt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfLt(int register1, int register2, int offset) {
+    super(register1, register2, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.LT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfLtz.java b/src/main/java/com/android/tools/r8/code/IfLtz.java
new file mode 100644
index 0000000..a4162bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfLtz.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfLtz extends Format21t {
+
+  public static final int OPCODE = 0x3a;
+  public static final String NAME = "IfLtz";
+  public static final String SMALI_NAME = "if-ltz";
+
+  IfLtz(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfLtz(int register, int offset) {
+    super(register, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.LT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfNe.java b/src/main/java/com/android/tools/r8/code/IfNe.java
new file mode 100644
index 0000000..2a69bbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfNe.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfNe extends Format22t {
+
+  public static final int OPCODE = 0x33;
+  public static final String NAME = "IfNe";
+  public static final String SMALI_NAME = "if-ne";
+
+  IfNe(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfNe(int register1, int register2, int offset) {
+    super(register1, register2, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.NE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IfNez.java b/src/main/java/com/android/tools/r8/code/IfNez.java
new file mode 100644
index 0000000..a0073f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IfNez.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.If.Type;
+
+public class IfNez extends Format21t {
+
+  public static final int OPCODE = 0x39;
+  public static final String NAME = "IfNez";
+  public static final String SMALI_NAME = "if-nez";
+
+  IfNez(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IfNez(int register, int offset) {
+    super(register, offset);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.NE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Iget.java b/src/main/java/com/android/tools/r8/code/Iget.java
new file mode 100644
index 0000000..c3887b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Iget.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Iget extends Format22c {
+
+  public static final int OPCODE = 0x52;
+  public static final String NAME = "Iget";
+  public static final String SMALI_NAME = "iget";
+
+  /*package*/ Iget(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public Iget(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.SINGLE, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetBoolean.java b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
new file mode 100644
index 0000000..02dd1db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetBoolean extends Format22c {
+
+  public static final int OPCODE = 0x55;
+  public static final String NAME = "IgetBoolean";
+  public static final String SMALI_NAME = "iget-boolean";
+
+  /*package*/ IgetBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IgetBoolean(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.BOOLEAN, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetByte.java b/src/main/java/com/android/tools/r8/code/IgetByte.java
new file mode 100644
index 0000000..51d3698
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetByte.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetByte extends Format22c {
+
+  public static final int OPCODE = 0x56;
+  public static final String NAME = "IgetByte";
+  public static final String SMALI_NAME = "iget-byte";
+
+  /*package*/ IgetByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IgetByte(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.BYTE, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetChar.java b/src/main/java/com/android/tools/r8/code/IgetChar.java
new file mode 100644
index 0000000..f9f5d66
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetChar.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetChar extends Format22c {
+
+  public static final int OPCODE = 0x57;
+  public static final String NAME = "IgetChar";
+  public static final String SMALI_NAME = "iget-char";
+
+  /*package*/ IgetChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IgetChar(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.CHAR, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetObject.java b/src/main/java/com/android/tools/r8/code/IgetObject.java
new file mode 100644
index 0000000..3b28fb0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetObject extends Format22c {
+
+  public static final int OPCODE = 0x54;
+  public static final String NAME = "IgetObject";
+  public static final String SMALI_NAME = "iget-object";
+
+  /*package*/ IgetObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IgetObject(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.OBJECT, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetShort.java b/src/main/java/com/android/tools/r8/code/IgetShort.java
new file mode 100644
index 0000000..e49c4e4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetShort extends Format22c {
+
+  public static final int OPCODE = 0x58;
+  public static final String NAME = "IgetShort";
+  public static final String SMALI_NAME = "iget-short";
+
+  /*package*/ IgetShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IgetShort(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.SHORT, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IgetWide.java b/src/main/java/com/android/tools/r8/code/IgetWide.java
new file mode 100644
index 0000000..226d995a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IgetWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IgetWide extends Format22c {
+
+  public static final int OPCODE = 0x53;
+  public static final String NAME = "IgetWide";
+  public static final String SMALI_NAME = "iget-wide";
+
+  /*package*/ IgetWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IgetWide(int destRegister, int objectRegister, DexField field) {
+    super(destRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldRead(getField());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceGet(MemberType.WIDE, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InstanceOf.java b/src/main/java/com/android/tools/r8/code/InstanceOf.java
new file mode 100644
index 0000000..bab2d4b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InstanceOf.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InstanceOf extends Format22c {
+
+  public static final int OPCODE = 0x20;
+  public static final String NAME = "InstanceOf";
+  public static final String SMALI_NAME = "instance-of";
+
+  InstanceOf(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public InstanceOf(int dest, int value, DexType type) {
+    super(dest, value, type);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerTypeReference(getType());
+  }
+
+  public DexType getType() {
+    return (DexType) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstanceOf(A, B, getType());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
new file mode 100644
index 0000000..316a759
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -0,0 +1,235 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.function.BiPredicate;
+
+public abstract class Instruction {
+
+  public final static int[] NO_TARGETS = null;
+  public final static int[] EXIT_TARGET = new int[]{};
+
+  public int offset;
+
+  Instruction(BytecodeStream stream) {
+    // When this constructor is invoked, we have already read 1 ushort from the stream.
+    this.offset = stream.getOffset() - 1;
+  }
+
+  protected Instruction() {
+    this.offset = -1;
+  }
+
+  static int readSigned8BitValue(BytecodeStream stream) {
+    return (byte) stream.nextByte();
+  }
+
+  static int read8BitValue(BytecodeStream stream) {
+    int result = stream.nextByte();
+    return result;
+  }
+
+  static int readSigned16BitValue(BytecodeStream stream) {
+    // Convert to signed.
+    return (short) stream.nextShort();
+  }
+
+  static int read16BitValue(BytecodeStream stream) {
+    return stream.nextShort() & 0xffff;
+  }
+
+  static int readSigned32BitValue(BytecodeStream stream) {
+    int low = read16BitValue(stream);
+    int high = read16BitValue(stream);
+    int result = ((high << 16) & 0xffff0000) | (low & 0xffff);
+    return result;
+  }
+
+  static long read32BitValue(BytecodeStream stream) {
+    long low = read16BitValue(stream);
+    long high = read16BitValue(stream);
+    long result = ((high & 0xffff) << 16) | (low & 0xffff);
+    return result;
+  }
+
+  static long read64BitValue(BytecodeStream stream) {
+    long low = read32BitValue(stream);
+    long high = read32BitValue(stream);
+    long result = (high << 32) | low;
+    return result;
+  }
+
+  protected static short combineBytes(int high, int low) {
+    return (short) (((high & 0xff) << 8) | (low & 0xff));
+  }
+
+  protected static int makeByte(int high, int low) {
+    return ((high & 0xf) << 4) | low & 0xf;
+  }
+
+  protected void writeFirst(int aa, ShortBuffer dest) {
+    dest.put((short) (((aa & 0xff) << 8) | (getOpcode() & 0xff)));
+  }
+
+  protected void writeFirst(int a, int b, ShortBuffer dest) {
+    dest.put((short) (((a & 0xf) << 12) | ((b & 0xf) << 8) | (getOpcode() & 0xff)));
+  }
+
+  protected void write16BitValue(int value, ShortBuffer dest) {
+    dest.put((short) value);
+  }
+
+  protected void write32BitValue(long value, ShortBuffer dest) {
+    dest.put((short) (value & 0xffff));
+    dest.put((short) ((value >> 16) & 0xffff));
+  }
+
+  protected void write64BitValue(long value, ShortBuffer dest) {
+    write32BitValue(value & 0xffffffff, dest);
+    write32BitValue((value >> 32) & 0xffffffff, dest);
+  }
+
+  protected void write16BitReference(IndexedDexItem item, ShortBuffer dest,
+      ObjectToOffsetMapping mapping) {
+    int index = item.getOffset(mapping);
+    assert index == (index & 0xffff);
+    write16BitValue(index, dest);
+  }
+
+  protected void write32BitReference(IndexedDexItem item, ShortBuffer dest,
+      ObjectToOffsetMapping mapping) {
+    write32BitValue(item.getOffset(mapping), dest);
+  }
+
+  public int getOffset() {
+    return offset;
+  }
+
+  public void setOffset(int offset) {
+    this.offset = offset;
+  }
+
+  public boolean isPayload() {
+    return false;
+  }
+
+  public boolean isSwitchPayload() {
+    return false;
+  }
+
+  public boolean hasPayload() {
+    return false;
+  }
+
+  public boolean isSwitch() {
+    return false;
+  }
+
+  public int getPayloadOffset() {
+    return 0;
+  }
+
+  String formatString(String left) {
+    StringBuilder builder = new StringBuilder();
+    StringUtils.appendLeftPadded(builder, Integer.toString(getOffset()), 6);
+    builder.append(": ");
+    StringUtils.appendRightPadded(builder, getName(), 20);
+    builder.append(left == null ? "" : left);
+    return builder.toString();
+  }
+
+  String formatSmaliString(String left) {
+    StringBuilder builder = new StringBuilder();
+    builder.append("    ");
+    if (left != null) {
+      StringUtils.appendRightPadded(builder, getSmaliName(), 20);
+      builder.append(left);
+    } else {
+      builder.append(getSmaliName());
+    }
+    return builder.toString();
+  }
+
+  public int[] getTargets() {
+    return NO_TARGETS;
+  }
+
+  public abstract void buildIR(IRBuilder builder);
+
+  public DexCallSite getCallSite() {
+    return null;
+  }
+
+  public DexMethod getMethod() {
+    return null;
+  }
+
+  public DexProto getProto() {
+    return null;
+  }
+
+  public DexField getField() {
+    return null;
+  }
+
+  @Override
+  public abstract boolean equals(Object obj);
+
+  @Override
+  public abstract int hashCode();
+
+  public abstract String getName();
+
+  public abstract String getSmaliName();
+
+  public abstract int getOpcode();
+
+  public abstract int getSize();
+
+  public String toSmaliString(Instruction payloadUser) {
+    throw new InternalCompilerError("Instruction " + payloadUser + " is not a payload user");
+  }
+
+  public abstract String toSmaliString(ClassNameMapper naming);
+
+  public String toSmaliString() {
+    return toSmaliString((ClassNameMapper) null);
+  }
+
+  public abstract String toString(ClassNameMapper naming);
+
+  public String toString() {
+    return toString(null);
+  }
+
+  public abstract void write(ShortBuffer buffer, ObjectToOffsetMapping mapping);
+
+  public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
+
+  public boolean equals(Instruction other, BiPredicate<IndexedDexItem, IndexedDexItem> equality) {
+    // In the default case, there is nothing to substitute.
+    return this.equals(other);
+  }
+
+  public void registerUse(UseRegistry registry) {
+    // Intentionally empty
+  }
+
+  public boolean canThrow() {
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InstructionFactory.java b/src/main/java/com/android/tools/r8/code/InstructionFactory.java
new file mode 100644
index 0000000..2664d83
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InstructionFactory.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class InstructionFactory extends BaseInstructionFactory {
+
+  private DexString highestSortingString = null;
+
+  static private Instruction readFrom(ShortBufferBytecodeStream stream,
+      OffsetToObjectMapping mapping) {
+    int high = stream.nextByte();
+    int opcode = stream.nextByte();
+    return create(high, opcode, stream, mapping);
+  }
+
+  public Instruction[] readSequenceFrom(ByteBuffer buffer, int startIndex, int length,
+      OffsetToObjectMapping mapping) {
+    return readSequenceFrom(buffer.asShortBuffer(), startIndex, length, mapping);
+  }
+
+  public Instruction[] readSequenceFrom(ShortBuffer buffer, int startIndex, int length,
+      OffsetToObjectMapping mapping) {
+    ShortBufferBytecodeStream range =
+        new ShortBufferBytecodeStream(buffer, startIndex, length);
+    List<Instruction> insn = new ArrayList<>(length);
+    while (range.hasMore()) {
+      Instruction instruction = readFrom(range, mapping);
+      if (instruction instanceof ConstString) {
+        updateHighestSortingString(((ConstString) instruction).getString());
+      } else if (instruction instanceof ConstStringJumbo) {
+        updateHighestSortingString(((ConstStringJumbo) instruction).getString());
+      }
+      insn.add(instruction);
+    }
+    return insn.toArray(new Instruction[insn.size()]);
+  }
+
+  public DexString getHighestSortingString() {
+    return highestSortingString;
+  }
+
+  private void updateHighestSortingString(DexString string) {
+    if (highestSortingString == null || highestSortingString.slowCompareTo(string) < 0) {
+      highestSortingString = string;
+    }
+  }
+
+  private static class ShortBufferBytecodeStream implements BytecodeStream {
+
+    private final int length;
+    private final int startIndex;
+    private final ShortBuffer source;
+
+    private int offset = 0;
+    private int nextByte;
+    private boolean cacheContainsValidByte = false;
+
+    ShortBufferBytecodeStream(ShortBuffer source, int startIndex, int length) {
+      this.startIndex = startIndex;
+      this.length = length;
+      this.source = source;
+    }
+
+    @Override
+    public int nextShort() {
+      assert !cacheContainsValidByte : "Unread byte in cache.";
+      assert offset < length;
+      int result = source.get(startIndex + offset);
+      offset += 1;
+      return result;
+    }
+
+    public int nextByte() {
+      if (cacheContainsValidByte) {
+        cacheContainsValidByte = false;
+        return nextByte;
+      } else {
+        int next = nextShort();
+        nextByte = next & 0xff;
+        cacheContainsValidByte = true;
+        return (next >> 8) & 0xff;
+      }
+    }
+
+    @Override
+    public boolean hasMore() {
+      return length - offset > 0;
+    }
+
+    @Override
+    public int getOffset() {
+      return offset;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToByte.java b/src/main/java/com/android/tools/r8/code/IntToByte.java
new file mode 100644
index 0000000..b16b2b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToByte.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class IntToByte extends Format12x {
+
+  public static final int OPCODE = 0x8d;
+  public static final String NAME = "IntToByte";
+  public static final String SMALI_NAME = "int-to-byte";
+
+  IntToByte(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IntToByte(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.BYTE, NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToChar.java b/src/main/java/com/android/tools/r8/code/IntToChar.java
new file mode 100644
index 0000000..d16d050
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToChar.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToChar extends Format12x {
+
+  public static final int OPCODE = 0x8e;
+  public static final String NAME = "IntToChar";
+  public static final String SMALI_NAME = "int-to-char";
+
+  IntToChar(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IntToChar(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.CHAR, NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToDouble.java b/src/main/java/com/android/tools/r8/code/IntToDouble.java
new file mode 100644
index 0000000..422a581
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToDouble extends Format12x {
+
+  public static final int OPCODE = 0x83;
+  public static final String NAME = "IntToDouble";
+  public static final String SMALI_NAME = "int-to-double";
+
+  IntToDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IntToDouble(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.DOUBLE, NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToFloat.java b/src/main/java/com/android/tools/r8/code/IntToFloat.java
new file mode 100644
index 0000000..fb2f922
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class IntToFloat extends Format12x {
+
+  public static final int OPCODE = 0x82;
+  public static final String NAME = "IntToFloat";
+  public static final String SMALI_NAME = "int-to-float";
+
+  IntToFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IntToFloat(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.FLOAT, NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToLong.java b/src/main/java/com/android/tools/r8/code/IntToLong.java
new file mode 100644
index 0000000..0198a60
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToLong extends Format12x {
+
+  public static final int OPCODE = 0x81;
+  public static final String NAME = "IntToLong";
+  public static final String SMALI_NAME = "int-to-long";
+
+  IntToLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IntToLong(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.LONG, NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IntToShort.java b/src/main/java/com/android/tools/r8/code/IntToShort.java
new file mode 100644
index 0000000..cd9e035
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IntToShort.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IntToShort extends Format12x {
+
+  public static final int OPCODE = 0x8f;
+  public static final String NAME = "IntToShort";
+  public static final String SMALI_NAME = "int-to-short";
+
+  IntToShort(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public IntToShort(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.SHORT, NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
new file mode 100644
index 0000000..504a9e3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeCustom extends Format35c {
+
+  public static final int OPCODE = 0xfc;
+  public static final String NAME = "InvokeCustom";
+  public static final String SMALI_NAME = "invoke-custom";
+
+  InvokeCustom(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getCallSiteMap());
+  }
+
+  public InvokeCustom(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registerCallSite(registry, getCallSite());
+  }
+
+  @Override
+  public DexCallSite getCallSite() {
+    return (DexCallSite) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeCustomRegisters(getCallSite(), A, new int[]{C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+
+  static void registerCallSite(UseRegistry registry, DexCallSite callSite) {
+    InvokeCustom.registerMethodHandle(registry, callSite.bootstrapMethod);
+
+    // Register bootstrap method arguments, only Type and MethodHandle need to be register.
+    for (DexValue arg : callSite.bootstrapArgs) {
+      if (arg instanceof DexValueType) {
+        registry.registerTypeReference(((DexValueType) arg).value);
+      } else if (arg instanceof DexValueMethodHandle) {
+        InvokeCustom.registerMethodHandle(registry, ((DexValueMethodHandle) arg).value);
+      }
+    }
+  }
+
+  static void registerMethodHandle(UseRegistry registry, DexMethodHandle methodHandle) {
+    switch (methodHandle.type) {
+      case INSTANCE_GET:
+        registry.registerInstanceFieldRead(methodHandle.asField());
+        break;
+      case INSTANCE_PUT:
+        registry.registerInstanceFieldWrite(methodHandle.asField());
+        break;
+      case STATIC_GET:
+        registry.registerStaticFieldRead(methodHandle.asField());
+        break;
+      case STATIC_PUT:
+        registry.registerStaticFieldWrite(methodHandle.asField());
+        break;
+      case INVOKE_INSTANCE:
+        registry.registerInvokeVirtual(methodHandle.asMethod());
+        break;
+      case INVOKE_STATIC:
+        registry.registerInvokeStatic(methodHandle.asMethod());
+        break;
+      case INVOKE_CONSTRUCTOR:
+        DexMethod method = methodHandle.asMethod();
+        registry.registerNewInstance(method.getHolder());
+        registry.registerInvokeDirect(method);
+        break;
+      case INVOKE_INTERFACE:
+        registry.registerInvokeInterface(methodHandle.asMethod());
+        break;
+      case INVOKE_SUPER:
+        registry.registerInvokeSuper(methodHandle.asMethod());
+        break;
+      default:
+        throw new AssertionError();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
new file mode 100644
index 0000000..c66b812
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeCustomRange extends Format3rc {
+
+  public static final int OPCODE = 0xfd;
+  public static final String NAME = "InvokeCustomRange";
+  public static final String SMALI_NAME = "invoke-custom/range";
+
+  InvokeCustomRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getCallSiteMap());
+  }
+
+  public InvokeCustomRange(int firstArgumentRegister, int argumentCount, DexCallSite callSite) {
+    super(firstArgumentRegister, argumentCount, callSite);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public DexCallSite getCallSite() {
+    return (DexCallSite) BBBB;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    InvokeCustom.registerCallSite(registry, getCallSite());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeCustomRange(getCallSite(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
new file mode 100644
index 0000000..58fb8c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeDirect extends Format35c {
+
+  public static final int OPCODE = 0x70;
+  public static final String NAME = "InvokeDirect";
+  public static final String SMALI_NAME = "invoke-direct";
+
+  InvokeDirect(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeDirect(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeDirect(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRegisters(Type.DIRECT, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
new file mode 100644
index 0000000..742546d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeDirectRange extends Format3rc {
+
+  public static final int OPCODE = 0x76;
+  public static final String NAME = "InvokeDirectRange";
+  public static final String SMALI_NAME = "invoke-direct/range";
+
+  InvokeDirectRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeDirectRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+    super(firstArgumentRegister, argumentCount, method);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeDirect(getMethod());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRange(Type.DIRECT, getMethod(), getProto(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
new file mode 100644
index 0000000..512f6bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeInterface extends Format35c {
+
+  public static final int OPCODE = 0x72;
+  public static final String NAME = "InvokeInterface";
+  public static final String SMALI_NAME = "invoke-interface";
+
+  InvokeInterface(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeInterface(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeInterface(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRegisters(
+        Type.INTERFACE, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
new file mode 100644
index 0000000..06eb69e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeInterfaceRange extends Format3rc {
+
+  public static final int OPCODE = 0x78;
+  public static final String NAME = "InvokeInterfaceRange";
+  public static final String SMALI_NAME = "invoke-interface/range";
+
+  InvokeInterfaceRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeInterfaceRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+    super(firstArgumentRegister, argumentCount, method);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeInterface(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRange(Type.INTERFACE, getMethod(), getProto(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
new file mode 100644
index 0000000..d5ecd11
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphic.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+/**
+ * An invoke-polymorphic instruction used to invoke a method in a MethodHandle using either
+ * MethodHandle.invoke or MethodHandle.invokeExact.
+ */
+public class InvokePolymorphic extends Format45cc {
+
+  public static final int OPCODE = 0xfa;
+  public static final String NAME = "InvokePolymorphic";
+  public static final String SMALI_NAME = "invoke-polymorphic";
+
+  InvokePolymorphic(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap(), mapping.getProtosMap());
+  }
+
+  public InvokePolymorphic(
+      int A, DexMethod BBBB, DexProto HHHH, int C, int D, int E, int F, int G) {
+    super(A, BBBB, HHHH, C, D, E, F, G);
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRegisters(
+        Type.POLYMORPHIC, getMethod(), getProto(), A, new int[] {C, D, E, F, G});
+  }
+
+  @Override
+  public String getName() {
+    return NAME;
+  }
+
+  @Override
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  @Override
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
new file mode 100644
index 0000000..9b102b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+/** An invoke-polymorphic range instruction used to call method with polymorphic signature. */
+public class InvokePolymorphicRange extends Format4rcc {
+
+  public static final int OPCODE = 0xfb;
+  public static final String NAME = "InvokePolymorphicRange";
+  public static final String SMALI_NAME = "invoke-polymorphic/range";
+
+  InvokePolymorphicRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap(), mapping.getProtosMap());
+  }
+
+  public InvokePolymorphicRange(
+      int firstArgumentRegister, int argumentCount, DexMethod method, DexProto proto) {
+    super(firstArgumentRegister, argumentCount, method, proto);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeDirect(getMethod());
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRange(Type.POLYMORPHIC, getMethod(), getProto(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
new file mode 100644
index 0000000..8fbf95c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeStatic extends Format35c {
+
+  public static final int OPCODE = 0x71;
+  public static final String NAME = "InvokeStatic";
+  public static final String SMALI_NAME = "invoke-static";
+
+  InvokeStatic(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeStatic(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeStatic(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRegisters(Type.STATIC, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
new file mode 100644
index 0000000..92a4047
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeStaticRange extends Format3rc {
+
+  public static final int OPCODE = 0x77;
+  public static final String NAME = "InvokeStaticRange";
+  public static final String SMALI_NAME = "invoke-static/range";
+
+  InvokeStaticRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeStaticRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+    super(firstArgumentRegister, argumentCount, method);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeStatic(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRange(Type.STATIC, getMethod(), getProto(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
new file mode 100644
index 0000000..7a6e32a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeSuper extends Format35c {
+
+  public static final int OPCODE = 0x6f;
+  public static final String NAME = "InvokeSuper";
+  public static final String SMALI_NAME = "invoke-super";
+
+  InvokeSuper(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeSuper(int A, IndexedDexItem BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeSuper(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRegisters(Type.SUPER, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
new file mode 100644
index 0000000..9ee05b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeSuperRange extends Format3rc {
+
+  public static final int OPCODE = 0x75;
+  public static final String NAME = "InvokeSuperRange";
+  public static final String SMALI_NAME = "invoke-super/range";
+
+  InvokeSuperRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeSuperRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+    super(firstArgumentRegister, argumentCount, method);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeSuper(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRange(Type.SUPER, getMethod(), getProto(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
new file mode 100644
index 0000000..1fbf9d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeVirtual extends Format35c {
+
+  public static final int OPCODE = 0x6e;
+  public static final String NAME = "InvokeVirtual";
+  public static final String SMALI_NAME = "invoke-virtual";
+
+  InvokeVirtual(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeVirtual(int A, DexMethod BBBB, int C, int D, int E, int F, int G) {
+    super(A, BBBB, C, D, E, F, G);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeVirtual(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRegisters(Type.VIRTUAL, getMethod(), getProto(), A, new int[]{C, D, E, F, G});
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
new file mode 100644
index 0000000..c5c8249
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class InvokeVirtualRange extends Format3rc {
+
+  public static final int OPCODE = 0x74;
+  public static final String NAME = "InvokeVirtualRange";
+  public static final String SMALI_NAME = "invoke-virtual/range";
+
+  InvokeVirtualRange(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getMethodMap());
+  }
+
+  public InvokeVirtualRange(int firstArgumentRegister, int argumentCount, DexMethod method) {
+    super(firstArgumentRegister, argumentCount, method);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInvokeVirtual(getMethod());
+  }
+
+  @Override
+  public DexMethod getMethod() {
+    return (DexMethod) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInvokeRange(Type.VIRTUAL, getMethod(), getProto(), AA, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Iput.java b/src/main/java/com/android/tools/r8/code/Iput.java
new file mode 100644
index 0000000..87dc47e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Iput.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Iput extends Format22c {
+
+  public static final int OPCODE = 0x59;
+  public static final String NAME = "Iput";
+  public static final String SMALI_NAME = "iput";
+
+  /*package*/ Iput(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public Iput(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.SINGLE, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputBoolean.java b/src/main/java/com/android/tools/r8/code/IputBoolean.java
new file mode 100644
index 0000000..6bc56fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputBoolean.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputBoolean extends Format22c {
+
+  public static final int OPCODE = 0x5c;
+  public static final String NAME = "IputBoolean";
+  public static final String SMALI_NAME = "iput-boolean";
+
+  /*package*/ IputBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IputBoolean(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.BOOLEAN, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputByte.java b/src/main/java/com/android/tools/r8/code/IputByte.java
new file mode 100644
index 0000000..527e11d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputByte.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputByte extends Format22c {
+
+  public static final int OPCODE = 0x5d;
+  public static final String NAME = "IputByte";
+  public static final String SMALI_NAME = "iput-byte";
+
+  /*package*/ IputByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IputByte(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.BYTE, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputChar.java b/src/main/java/com/android/tools/r8/code/IputChar.java
new file mode 100644
index 0000000..7010b3a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputChar.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputChar extends Format22c {
+
+  public static final int OPCODE = 0x5e;
+  public static final String NAME = "IputChar";
+  public static final String SMALI_NAME = "iput-char";
+
+  /*package*/ IputChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IputChar(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.CHAR, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputObject.java b/src/main/java/com/android/tools/r8/code/IputObject.java
new file mode 100644
index 0000000..7e1eb17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputObject extends Format22c {
+
+  public static final int OPCODE = 0x5b;
+  public static final String NAME = "IputObject";
+  public static final String SMALI_NAME = "iput-object";
+
+  /*package*/ IputObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IputObject(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.OBJECT, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputShort.java b/src/main/java/com/android/tools/r8/code/IputShort.java
new file mode 100644
index 0000000..0f40bb0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputShort extends Format22c {
+
+  public static final int OPCODE = 0x5f;
+  public static final String NAME = "IputShort";
+  public static final String SMALI_NAME = "iput-short";
+
+  /*package*/ IputShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IputShort(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.SHORT, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/IputWide.java b/src/main/java/com/android/tools/r8/code/IputWide.java
new file mode 100644
index 0000000..d845e1b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/IputWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class IputWide extends Format22c {
+
+  public static final int OPCODE = 0x5a;
+  public static final String NAME = "IputWide";
+  public static final String SMALI_NAME = "iput-wide";
+
+  /*package*/ IputWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public IputWide(int valueRegister, int objectRegister, DexField field) {
+    super(valueRegister, objectRegister, field);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerInstanceFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addInstancePut(MemberType.WIDE, A, B, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/LongToDouble.java b/src/main/java/com/android/tools/r8/code/LongToDouble.java
new file mode 100644
index 0000000..5b72828
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/LongToDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class LongToDouble extends Format12x {
+
+  public static final int OPCODE = 0x86;
+  public static final String NAME = "LongToDouble";
+  public static final String SMALI_NAME = "long-to-double";
+
+  LongToDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public LongToDouble(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.DOUBLE, NumericType.LONG, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/LongToFloat.java b/src/main/java/com/android/tools/r8/code/LongToFloat.java
new file mode 100644
index 0000000..db01018
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/LongToFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class LongToFloat extends Format12x {
+
+  public static final int OPCODE = 0x85;
+  public static final String NAME = "LongToFloat";
+  public static final String SMALI_NAME = "long-to-float";
+
+  LongToFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public LongToFloat(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.FLOAT, NumericType.LONG, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/LongToInt.java b/src/main/java/com/android/tools/r8/code/LongToInt.java
new file mode 100644
index 0000000..89219c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/LongToInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class LongToInt extends Format12x {
+
+  public static final int OPCODE = 0x84;
+  public static final String NAME = "LongToInt";
+  public static final String SMALI_NAME = "long-to-int";
+
+  LongToInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public LongToInt(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addConversion(NumericType.INT, NumericType.LONG, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MonitorEnter.java b/src/main/java/com/android/tools/r8/code/MonitorEnter.java
new file mode 100644
index 0000000..b6ebbff
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MonitorEnter.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Monitor.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MonitorEnter extends Format11x {
+
+  public static final int OPCODE = 0x1d;
+  public static final String NAME = "MonitorEnter";
+  public static final String SMALI_NAME = "monitor-enter";
+
+  MonitorEnter(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MonitorEnter(int register) {
+    super(register);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMonitor(Type.ENTER, AA);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MonitorExit.java b/src/main/java/com/android/tools/r8/code/MonitorExit.java
new file mode 100644
index 0000000..bad666f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MonitorExit.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Monitor.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MonitorExit extends Format11x {
+
+  public static final int OPCODE = 0x1e;
+  public static final String NAME = "MonitorExit";
+  public static final String SMALI_NAME = "monitor-exit";
+
+  MonitorExit(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MonitorExit(int register) {
+    super(register);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMonitor(Type.EXIT, AA);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Move.java b/src/main/java/com/android/tools/r8/code/Move.java
new file mode 100644
index 0000000..01eeada
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Move.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Move extends Format12x {
+
+  public static final int OPCODE = 0x1;
+  public static final String NAME = "Move";
+  public static final String SMALI_NAME = "move";
+
+  Move(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Move(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.SINGLE, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Move16.java b/src/main/java/com/android/tools/r8/code/Move16.java
new file mode 100644
index 0000000..cfa4349
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Move16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class Move16 extends Format32x {
+
+  public static final int OPCODE = 0x3;
+  public static final String NAME = "Move16";
+  public static final String SMALI_NAME = "move/16";
+
+  Move16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Move16(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.SINGLE, AAAA, BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveException.java b/src/main/java/com/android/tools/r8/code/MoveException.java
new file mode 100644
index 0000000..8b66c40
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveException.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveException extends Format11x {
+
+  public static final int OPCODE = 0xd;
+  public static final String NAME = "MoveException";
+  public static final String SMALI_NAME = "move-exception";
+
+  MoveException(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveException(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMoveException(AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveFrom16.java b/src/main/java/com/android/tools/r8/code/MoveFrom16.java
new file mode 100644
index 0000000..785babe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveFrom16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveFrom16 extends Format22x {
+
+  public static final int OPCODE = 0x2;
+  public static final String NAME = "MoveFrom16";
+  public static final String SMALI_NAME = "move-from/16";
+
+  MoveFrom16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveFrom16(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.SINGLE, AA, BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveObject.java b/src/main/java/com/android/tools/r8/code/MoveObject.java
new file mode 100644
index 0000000..a409025
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveObject.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveObject extends Format12x {
+
+  public static final int OPCODE = 0x7;
+  public static final String NAME = "MoveObject";
+  public static final String SMALI_NAME = "move-object";
+
+  MoveObject(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveObject(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.OBJECT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveObject16.java b/src/main/java/com/android/tools/r8/code/MoveObject16.java
new file mode 100644
index 0000000..a1857bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveObject16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveObject16 extends Format32x {
+
+  public static final int OPCODE = 0x9;
+  public static final String NAME = "MoveObject16";
+  public static final String SMALI_NAME = "move-object/16";
+
+  MoveObject16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveObject16(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.OBJECT, AAAA, BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveObjectFrom16.java b/src/main/java/com/android/tools/r8/code/MoveObjectFrom16.java
new file mode 100644
index 0000000..94c3101
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveObjectFrom16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveObjectFrom16 extends Format22x {
+
+  public static final int OPCODE = 0x8;
+  public static final String NAME = "MoveObjectFrom16";
+  public static final String SMALI_NAME = "move-object-from/16";
+
+  MoveObjectFrom16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveObjectFrom16(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.OBJECT, AA, BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveResult.java b/src/main/java/com/android/tools/r8/code/MoveResult.java
new file mode 100644
index 0000000..98469f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveResult.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveResult extends Format11x {
+
+  public static final int OPCODE = 0xa;
+  public static final String NAME = "MoveResult";
+  public static final String SMALI_NAME = "move-result";
+
+  /*package*/ MoveResult(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveResult(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMoveResult(MoveType.SINGLE, AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveResultObject.java b/src/main/java/com/android/tools/r8/code/MoveResultObject.java
new file mode 100644
index 0000000..6043f78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveResultObject.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveResultObject extends Format11x {
+
+  public static final int OPCODE = 0xc;
+  public static final String NAME = "MoveResultObject";
+  public static final String SMALI_NAME = "move-result-object";
+
+  /*package*/ MoveResultObject(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveResultObject(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMoveResult(MoveType.OBJECT, AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveResultWide.java b/src/main/java/com/android/tools/r8/code/MoveResultWide.java
new file mode 100644
index 0000000..44f5016
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveResultWide.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MoveResultWide extends Format11x {
+
+  public static final int OPCODE = 0xb;
+  public static final String NAME = "MoveResultWide";
+  public static final String SMALI_NAME = "move-result-wide";
+
+  /*package*/ MoveResultWide(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveResultWide(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMoveResult(MoveType.WIDE, AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveWide.java b/src/main/java/com/android/tools/r8/code/MoveWide.java
new file mode 100644
index 0000000..12c4ea7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveWide.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveWide extends Format12x {
+
+  public static final int OPCODE = 0x4;
+  public static final String NAME = "MoveWide";
+  public static final String SMALI_NAME = "move-wide";
+
+  MoveWide(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveWide(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.WIDE, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveWide16.java b/src/main/java/com/android/tools/r8/code/MoveWide16.java
new file mode 100644
index 0000000..708332d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveWide16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveWide16 extends Format32x {
+
+  public static final int OPCODE = 0x6;
+  public static final String NAME = "MoveWide16";
+  public static final String SMALI_NAME = "move-wide/16";
+
+  MoveWide16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveWide16(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.WIDE, AAAA, BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MoveWideFrom16.java b/src/main/java/com/android/tools/r8/code/MoveWideFrom16.java
new file mode 100644
index 0000000..0236e17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MoveWideFrom16.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MoveWideFrom16 extends Format22x {
+
+  public static final int OPCODE = 0x5;
+  public static final String NAME = "MoveWideFrom16";
+  public static final String SMALI_NAME = "move-wide-from/16";
+
+  MoveWideFrom16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MoveWideFrom16(int dest, int src) {
+    super(dest, src);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMove(MoveType.WIDE, AA, BBBB);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulDouble.java b/src/main/java/com/android/tools/r8/code/MulDouble.java
new file mode 100644
index 0000000..2d63172
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulDouble extends Format23x {
+
+  public static final int OPCODE = 0xad;
+  public static final String NAME = "MulDouble";
+  public static final String SMALI_NAME = "mul-double";
+
+  MulDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.DOUBLE, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulDouble2Addr.java b/src/main/java/com/android/tools/r8/code/MulDouble2Addr.java
new file mode 100644
index 0000000..523bdd3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulDouble2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulDouble2Addr extends Format12x {
+
+  public static final int OPCODE = 0xcd;
+  public static final String NAME = "MulDouble2Addr";
+  public static final String SMALI_NAME = "mul-double/2addr";
+
+  MulDouble2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulDouble2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.DOUBLE, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulFloat.java b/src/main/java/com/android/tools/r8/code/MulFloat.java
new file mode 100644
index 0000000..bb10eaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulFloat extends Format23x {
+
+  public static final int OPCODE = 0xa8;
+  public static final String NAME = "MulFloat";
+  public static final String SMALI_NAME = "mul-float";
+
+  MulFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.FLOAT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulFloat2Addr.java b/src/main/java/com/android/tools/r8/code/MulFloat2Addr.java
new file mode 100644
index 0000000..3a50b77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulFloat2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc8;
+  public static final String NAME = "MulFloat2Addr";
+  public static final String SMALI_NAME = "mul-float/2addr";
+
+  MulFloat2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulFloat2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.FLOAT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulInt.java b/src/main/java/com/android/tools/r8/code/MulInt.java
new file mode 100644
index 0000000..cb24a89
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulInt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MulInt extends Format23x {
+
+  public static final int OPCODE = 0x92;
+  public static final String NAME = "MulInt";
+  public static final String SMALI_NAME = "mul-int";
+
+  MulInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulInt2Addr.java b/src/main/java/com/android/tools/r8/code/MulInt2Addr.java
new file mode 100644
index 0000000..50fa865
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulInt2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class MulInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb2;
+  public static final String NAME = "MulInt2Addr";
+  public static final String SMALI_NAME = "mul-int/2addr";
+
+  MulInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulIntLit16.java b/src/main/java/com/android/tools/r8/code/MulIntLit16.java
new file mode 100644
index 0000000..0188ba7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd2;
+  public static final String NAME = "MulIntLit16";
+  public static final String SMALI_NAME = "mul-int/lit16";
+
+  MulIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulIntLit16(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMulLiteral(NumericType.INT, A, B, CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulIntLit8.java b/src/main/java/com/android/tools/r8/code/MulIntLit8.java
new file mode 100644
index 0000000..fde33be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xda;
+  public static final String NAME = "MulIntLit8";
+  public static final String SMALI_NAME = "mul-int/lit8";
+
+  MulIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMulLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulLong.java b/src/main/java/com/android/tools/r8/code/MulLong.java
new file mode 100644
index 0000000..487f786
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulLong extends Format23x {
+
+  public static final int OPCODE = 0x9d;
+  public static final String NAME = "MulLong";
+  public static final String SMALI_NAME = "mul-long";
+
+  MulLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/MulLong2Addr.java b/src/main/java/com/android/tools/r8/code/MulLong2Addr.java
new file mode 100644
index 0000000..ffc7bfa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/MulLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class MulLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xbd;
+  public static final String NAME = "MulLong2Addr";
+  public static final String SMALI_NAME = "mul-long/2addr";
+
+  MulLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public MulLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addMul(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegDouble.java b/src/main/java/com/android/tools/r8/code/NegDouble.java
new file mode 100644
index 0000000..4fda3d2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegDouble.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class NegDouble extends Format12x {
+
+  public static final int OPCODE = 0x80;
+  public static final String NAME = "NegDouble";
+  public static final String SMALI_NAME = "neg-double";
+
+  /*package*/ NegDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public NegDouble(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNeg(NumericType.DOUBLE, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegFloat.java b/src/main/java/com/android/tools/r8/code/NegFloat.java
new file mode 100644
index 0000000..c0618f7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NegFloat extends Format12x {
+
+  public static final int OPCODE = 0x7f;
+  public static final String NAME = "NegFloat";
+  public static final String SMALI_NAME = "neg-float";
+
+  /*package*/ NegFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public NegFloat(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNeg(NumericType.FLOAT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegInt.java b/src/main/java/com/android/tools/r8/code/NegInt.java
new file mode 100644
index 0000000..92e5cc4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NegInt extends Format12x {
+
+  public static final int OPCODE = 0x7b;
+  public static final String NAME = "NegInt";
+  public static final String SMALI_NAME = "neg-int";
+
+  /*package*/ NegInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public NegInt(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNeg(NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NegLong.java b/src/main/java/com/android/tools/r8/code/NegLong.java
new file mode 100644
index 0000000..8c020ae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NegLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class NegLong extends Format12x {
+
+  public static final int OPCODE = 0x7d;
+  public static final String NAME = "NegLong";
+  public static final String SMALI_NAME = "neg-long";
+
+  /*package*/ NegLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public NegLong(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNeg(NumericType.LONG, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NewArray.java b/src/main/java/com/android/tools/r8/code/NewArray.java
new file mode 100644
index 0000000..4dd5361
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NewArray.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NewArray extends Format22c {
+
+  public static final int OPCODE = 0x23;
+  public static final String NAME = "NewArray";
+  public static final String SMALI_NAME = "new-array";
+
+  /*package*/ NewArray(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public NewArray(int dest, int size, DexType type) {
+    super(dest, size, type);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerTypeReference(getType());
+  }
+
+  public DexType getType() {
+    return (DexType) CCCC;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNewArrayEmpty(A, B, getType());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java
new file mode 100644
index 0000000..1592d08
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NewInstance extends Format21c {
+
+  public static final int OPCODE = 0x22;
+  public static final String NAME = "NewInstance";
+  public static final String SMALI_NAME = "new-instance";
+
+  NewInstance(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getTypeMap());
+  }
+
+  public NewInstance(int AA, DexType BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerNewInstance(getType());
+  }
+
+  public DexType getType() {
+    return (DexType) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNewInstance(AA, getType());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Nop.java b/src/main/java/com/android/tools/r8/code/Nop.java
new file mode 100644
index 0000000..4a0bcdc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Nop.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Nop extends Format10x {
+
+  public static final int OPCODE = 0x0;
+  public static final String NAME = "Nop";
+  public static final String SMALI_NAME = "nop";
+
+  Nop(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Nop() {
+  }
+
+  public static Nop create(int high, BytecodeStream stream) {
+    switch (high) {
+      case 0x01:
+        return new PackedSwitchPayload(high, stream);
+      case 0x02:
+        return new SparseSwitchPayload(high, stream);
+      case 0x03:
+        return new FillArrayDataPayload(high, stream);
+      default:
+        return new Nop(high, stream);
+    }
+  }
+
+  public int hashCode() {
+    return NAME.hashCode() * 7 + super.hashCode();
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    // The IR does not represent nops.
+    // Nops needed by dex, eg, for tight infinite loops, will be created upon conversion to dex.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NotInt.java b/src/main/java/com/android/tools/r8/code/NotInt.java
new file mode 100644
index 0000000..16940ca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NotInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class NotInt extends Format12x {
+
+  public static final int OPCODE = 0x7c;
+  public static final String NAME = "NotInt";
+  public static final String SMALI_NAME = "not-int";
+
+  NotInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public NotInt(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNot(NumericType.INT, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/NotLong.java b/src/main/java/com/android/tools/r8/code/NotLong.java
new file mode 100644
index 0000000..c0efa5b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/NotLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class NotLong extends Format12x {
+
+  public static final int OPCODE = 0x7e;
+  public static final String NAME = "NotLong";
+  public static final String SMALI_NAME = "not-long";
+
+  NotLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public NotLong(int dest, int source) {
+    super(dest, source);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addNot(NumericType.LONG, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrInt.java b/src/main/java/com/android/tools/r8/code/OrInt.java
new file mode 100644
index 0000000..4d474d3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrInt.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class OrInt extends Format23x {
+
+  public static final int OPCODE = 0x96;
+  public static final String NAME = "OrInt";
+  public static final String SMALI_NAME = "or-int";
+
+  OrInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public OrInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addOr(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrInt2Addr.java b/src/main/java/com/android/tools/r8/code/OrInt2Addr.java
new file mode 100644
index 0000000..ac2f6d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb6;
+  public static final String NAME = "OrInt2Addr";
+  public static final String SMALI_NAME = "or-int/2addr";
+
+  OrInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public OrInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addOr(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrIntLit16.java b/src/main/java/com/android/tools/r8/code/OrIntLit16.java
new file mode 100644
index 0000000..d4befd5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd6;
+  public static final String NAME = "OrIntLit16";
+  public static final String SMALI_NAME = "or-int/lit16";
+
+  OrIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public OrIntLit16(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addOrLiteral(NumericType.INT, A, B, CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrIntLit8.java b/src/main/java/com/android/tools/r8/code/OrIntLit8.java
new file mode 100644
index 0000000..e4fc7e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xde;
+  public static final String NAME = "OrIntLit8";
+  public static final String SMALI_NAME = "or-int/lit8";
+
+  OrIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public OrIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addOrLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrLong.java b/src/main/java/com/android/tools/r8/code/OrLong.java
new file mode 100644
index 0000000..ebcc386
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class OrLong extends Format23x {
+
+  public static final int OPCODE = 0xA1;
+  public static final String NAME = "OrLong";
+  public static final String SMALI_NAME = "or-long";
+
+  OrLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public OrLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addOr(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/OrLong2Addr.java b/src/main/java/com/android/tools/r8/code/OrLong2Addr.java
new file mode 100644
index 0000000..c49898b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/OrLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class OrLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc1;
+  public static final String NAME = "OrLong2Addr";
+  public static final String SMALI_NAME = "or-long/2addr";
+
+  OrLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public OrLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addOr(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitch.java b/src/main/java/com/android/tools/r8/code/PackedSwitch.java
new file mode 100644
index 0000000..051da72
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/PackedSwitch.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Switch.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class PackedSwitch extends Format31t {
+
+  public static final int OPCODE = 0x2b;
+  public static final String NAME = "PackedSwitch";
+  public static final String SMALI_NAME = "packed-switch";
+
+  PackedSwitch(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public PackedSwitch(int valueRegister) {
+    super(valueRegister, -1);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public boolean isSwitch() {
+    return true;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    int offset = getOffset();
+    int payloadOffset = offset + getPayloadOffset();
+    int fallthroughOffset = offset + getSize();
+    builder.resolveAndBuildSwitch(Type.PACKED, AA, fallthroughOffset, payloadOffset);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
new file mode 100644
index 0000000..25a5dcb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+
+public class PackedSwitchPayload extends SwitchPayload {
+
+  public final int size;
+  public final int first_key;
+  public final /* offset */ int[] targets;
+
+  public PackedSwitchPayload(int high, BytecodeStream stream) {
+    super(high, stream);
+    size = read16BitValue(stream);
+    first_key = readSigned32BitValue(stream);
+    targets = new int[size];
+    for (int i = 0; i < size; i++) {
+      targets[i] = readSigned32BitValue(stream);
+    }
+  }
+
+  public PackedSwitchPayload(int size, int first_key, int[] targets) {
+    assert size > 0;  // Empty switches should be eliminated.
+    this.size = size;
+    this.first_key = first_key;
+    this.targets = targets;
+  }
+
+  public boolean isPayload() {
+    return true;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(1, dest);  // Pseudo-opcode = 0x0100
+    write16BitValue(size, dest);
+    write32BitValue(first_key, dest);
+    for (int i = 0; i < size; i++) {
+      write32BitValue(targets[i], dest);
+    }
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (!super.equals(other)) {
+      return false;
+    }
+    PackedSwitchPayload that = (PackedSwitchPayload) other;
+    return size == that.size && first_key == that.first_key && Arrays.equals(targets, that.targets);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + size;
+    result = 31 * result + first_key;
+    result = 31 * result + Arrays.hashCode(targets);
+    return result;
+  }
+
+  public int getSize() {
+    return 4 + (2 * targets.length);
+  }
+
+  public static int getSizeForTargets(int targets) {
+    return 4 + (2 * targets);
+  }
+
+  @Override
+  public int numberOfKeys() {
+    return size;
+  }
+
+  @Override
+  public int[] switchTargetOffsets() {
+    return targets;
+  }
+
+  @Override
+  public int[] keys() {
+    return new int[]{first_key};
+  }
+
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder("[PackedSwitchPayload]\n");
+    for (int i = 0; i < size; i++) {
+      StringUtils.appendLeftPadded(builder, (first_key + i) + " -> " + targets[i] + "\n", 20);
+    }
+    return super.toString(naming) + builder.toString();
+  }
+
+  public String toSmaliString(Instruction payloadUser) {
+    StringBuilder builder = new StringBuilder();
+    builder.append("    ");
+    builder.append(".packed-switch ");
+    builder.append("0x");
+    builder.append(StringUtils.hexString(first_key, 8));
+    builder.append("  # ");
+    builder.append(first_key);
+    builder.append("\n");
+    for (int target : targets) {
+      builder.append("      :label_");
+      builder.append(payloadUser.getOffset() + target);
+      builder.append("\n");
+    }
+    builder.append("    ");
+    builder.append(".end packed-switch");
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemDouble.java b/src/main/java/com/android/tools/r8/code/RemDouble.java
new file mode 100644
index 0000000..00ec16b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemDouble extends Format23x {
+
+  public static final int OPCODE = 0xaf;
+  public static final String NAME = "RemDouble";
+  public static final String SMALI_NAME = "rem-double";
+
+  RemDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.DOUBLE, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemDouble2Addr.java b/src/main/java/com/android/tools/r8/code/RemDouble2Addr.java
new file mode 100644
index 0000000..6381b5d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemDouble2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemDouble2Addr extends Format12x {
+
+  public static final int OPCODE = 0xcf;
+  public static final String NAME = "RemDouble2Addr";
+  public static final String SMALI_NAME = "rem-double/2addr";
+
+  RemDouble2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemDouble2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.DOUBLE, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemFloat.java b/src/main/java/com/android/tools/r8/code/RemFloat.java
new file mode 100644
index 0000000..77031f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemFloat.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemFloat extends Format23x {
+
+  public static final int OPCODE = 0xaA;
+  public static final String NAME = "RemFloat";
+  public static final String SMALI_NAME = "rem-float";
+
+  RemFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.FLOAT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemFloat2Addr.java b/src/main/java/com/android/tools/r8/code/RemFloat2Addr.java
new file mode 100644
index 0000000..415c5c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemFloat2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemFloat2Addr extends Format12x {
+
+  public static final int OPCODE = 0xca;
+  public static final String NAME = "RemFloat2Addr";
+  public static final String SMALI_NAME = "rem-float/2addr";
+
+  RemFloat2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemFloat2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.FLOAT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemInt.java b/src/main/java/com/android/tools/r8/code/RemInt.java
new file mode 100644
index 0000000..c045c80
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemInt.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemInt extends Format23x {
+
+  public static final int OPCODE = 0x94;
+  public static final String NAME = "RemInt";
+  public static final String SMALI_NAME = "rem-int";
+
+  RemInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.INT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemInt2Addr.java b/src/main/java/com/android/tools/r8/code/RemInt2Addr.java
new file mode 100644
index 0000000..0bab68b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemInt2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb4;
+  public static final String NAME = "RemInt2Addr";
+  public static final String SMALI_NAME = "rem-int/2addr";
+
+  RemInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.INT, A, A, B);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemIntLit16.java b/src/main/java/com/android/tools/r8/code/RemIntLit16.java
new file mode 100644
index 0000000..c2133d3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemIntLit16.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd4;
+  public static final String NAME = "RemIntLit16";
+  public static final String SMALI_NAME = "rem-int/lit16";
+
+  RemIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemIntLit16(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRemLiteral(NumericType.INT, A, B, CCCC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemIntLit8.java b/src/main/java/com/android/tools/r8/code/RemIntLit8.java
new file mode 100644
index 0000000..bc11aee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemIntLit8.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RemIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xdc;
+  public static final String NAME = "RemIntLit8";
+  public static final String SMALI_NAME = "rem-int/lit8";
+
+  RemIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRemLiteral(NumericType.INT, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemLong.java b/src/main/java/com/android/tools/r8/code/RemLong.java
new file mode 100644
index 0000000..715a15c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemLong.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemLong extends Format23x {
+
+  public static final int OPCODE = 0x9f;
+  public static final String NAME = "RemLong";
+  public static final String SMALI_NAME = "rem-long";
+
+  RemLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.LONG, AA, BB, CC);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RemLong2Addr.java b/src/main/java/com/android/tools/r8/code/RemLong2Addr.java
new file mode 100644
index 0000000..c37b099
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RemLong2Addr.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RemLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xbf;
+  public static final String NAME = "RemLong2Addr";
+  public static final String SMALI_NAME = "rem-long/2addr";
+
+  RemLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RemLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRem(NumericType.LONG, A, A, B);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Return.java b/src/main/java/com/android/tools/r8/code/Return.java
new file mode 100644
index 0000000..7e61ef3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Return.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Return extends Format11x {
+
+  public static final int OPCODE = 0xf;
+  public static final String NAME = "Return";
+  public static final String SMALI_NAME = "return";
+
+  Return(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Return(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return EXIT_TARGET;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addReturn(MoveType.SINGLE, AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ReturnObject.java b/src/main/java/com/android/tools/r8/code/ReturnObject.java
new file mode 100644
index 0000000..67a59fd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ReturnObject.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ReturnObject extends Format11x {
+
+  public static final int OPCODE = 0x11;
+  public static final String NAME = "ReturnObject";
+  public static final String SMALI_NAME = "return-object";
+
+  ReturnObject(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ReturnObject(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return EXIT_TARGET;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addReturn(MoveType.OBJECT, AA);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ReturnVoid.java b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
new file mode 100644
index 0000000..07ca0cd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ReturnVoid extends Format10x {
+
+  public static final int OPCODE = 0xe;
+  public static final String NAME = "ReturnVoid";
+  public static final String SMALI_NAME = "return-void";
+
+  ReturnVoid(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ReturnVoid() {}
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return EXIT_TARGET;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addReturn();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ReturnWide.java b/src/main/java/com/android/tools/r8/code/ReturnWide.java
new file mode 100644
index 0000000..22a8efa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ReturnWide.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ReturnWide extends Format11x {
+
+  public static final int OPCODE = 0x10;
+  public static final String NAME = "ReturnWide";
+  public static final String SMALI_NAME = "return-wide";
+
+  ReturnWide(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ReturnWide(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public int[] getTargets() {
+    return EXIT_TARGET;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addReturn(MoveType.WIDE, AA);
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/code/RsubInt.java b/src/main/java/com/android/tools/r8/code/RsubInt.java
new file mode 100644
index 0000000..93fe424
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RsubInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class RsubInt extends Format22s {
+
+  public static final int OPCODE = 0xd1;
+  public static final String NAME = "RsubInt";
+  public static final String SMALI_NAME = "rsub-int";
+
+  RsubInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RsubInt(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRsubLiteral(NumericType.INT, A, B, CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/RsubIntLit8.java b/src/main/java/com/android/tools/r8/code/RsubIntLit8.java
new file mode 100644
index 0000000..a29c878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/RsubIntLit8.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class RsubIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xd9;
+  public static final String NAME = "RsubIntLit8";
+  public static final String SMALI_NAME = "rsub-int/lit8";
+
+  RsubIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public RsubIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addRsubLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Sget.java b/src/main/java/com/android/tools/r8/code/Sget.java
new file mode 100644
index 0000000..3d068ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Sget.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Sget extends Format21c {
+
+  public static final int OPCODE = 0x60;
+  public static final String NAME = "Sget";
+  public static final String SMALI_NAME = "sget";
+
+  Sget(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public Sget(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.SINGLE, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetBoolean.java b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
new file mode 100644
index 0000000..5e6b249
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetBoolean extends Format21c {
+
+  public static final int OPCODE = 0x63;
+  public static final String NAME = "SgetBoolean";
+  public static final String SMALI_NAME = "sget-boolean";
+
+  SgetBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SgetBoolean(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.BOOLEAN, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetByte.java b/src/main/java/com/android/tools/r8/code/SgetByte.java
new file mode 100644
index 0000000..d8e9ce3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetByte.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetByte extends Format21c {
+
+  public static final int OPCODE = 0x64;
+  public static final String NAME = "SgetByte";
+  public static final String SMALI_NAME = "sget-byte";
+
+  SgetByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SgetByte(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.BYTE, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetChar.java b/src/main/java/com/android/tools/r8/code/SgetChar.java
new file mode 100644
index 0000000..126f340
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetChar.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetChar extends Format21c {
+
+  public static final int OPCODE = 0x65;
+  public static final String NAME = "SgetChar";
+  public static final String SMALI_NAME = "sget-char";
+
+  SgetChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SgetChar(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.CHAR, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetObject.java b/src/main/java/com/android/tools/r8/code/SgetObject.java
new file mode 100644
index 0000000..00dfd73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetObject extends Format21c {
+
+  public static final int OPCODE = 0x62;
+  public static final String NAME = "SgetObject";
+  public static final String SMALI_NAME = "sget-object";
+
+  SgetObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SgetObject(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.OBJECT, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetShort.java b/src/main/java/com/android/tools/r8/code/SgetShort.java
new file mode 100644
index 0000000..b525858
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetShort extends Format21c {
+
+  public static final int OPCODE = 0x66;
+  public static final String NAME = "SgetShort";
+  public static final String SMALI_NAME = "sget-short";
+
+  SgetShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SgetShort(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.SHORT, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SgetWide.java b/src/main/java/com/android/tools/r8/code/SgetWide.java
new file mode 100644
index 0000000..e82478b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SgetWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SgetWide extends Format21c {
+
+  public static final int OPCODE = 0x61;
+  public static final String NAME = "SgetWide";
+  public static final String SMALI_NAME = "sget-wide";
+
+  SgetWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SgetWide(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldRead(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticGet(MemberType.WIDE, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlInt.java b/src/main/java/com/android/tools/r8/code/ShlInt.java
new file mode 100644
index 0000000..67cce9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlInt extends Format23x {
+
+  public static final int OPCODE = 0x98;
+  public static final String NAME = "ShlInt";
+  public static final String SMALI_NAME = "shl-int";
+
+  ShlInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShlInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShl(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlInt2Addr.java b/src/main/java/com/android/tools/r8/code/ShlInt2Addr.java
new file mode 100644
index 0000000..c136476
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb8;
+  public static final String NAME = "ShlInt2Addr";
+  public static final String SMALI_NAME = "shl-int/2addr";
+
+  ShlInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShlInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShl(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlIntLit8.java b/src/main/java/com/android/tools/r8/code/ShlIntLit8.java
new file mode 100644
index 0000000..4b34c78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlIntLit8.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class ShlIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xe0;
+  public static final String NAME = "ShlIntLit8";
+  public static final String SMALI_NAME = "shl-int/lit8";
+
+  ShlIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShlIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShlLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlLong.java b/src/main/java/com/android/tools/r8/code/ShlLong.java
new file mode 100644
index 0000000..9fb3c43
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlLong extends Format23x {
+
+  public static final int OPCODE = 0xA3;
+  public static final String NAME = "ShlLong";
+  public static final String SMALI_NAME = "shl-long";
+
+  ShlLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShlLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShl(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShlLong2Addr.java b/src/main/java/com/android/tools/r8/code/ShlLong2Addr.java
new file mode 100644
index 0000000..329b455
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShlLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShlLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc3;
+  public static final String NAME = "ShlLong2Addr";
+  public static final String SMALI_NAME = "shl-long/2addr";
+
+  ShlLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShlLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShl(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrInt.java b/src/main/java/com/android/tools/r8/code/ShrInt.java
new file mode 100644
index 0000000..ed094c7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrInt extends Format23x {
+
+  public static final int OPCODE = 0x99;
+  public static final String NAME = "ShrInt";
+  public static final String SMALI_NAME = "shr-int";
+
+  ShrInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShrInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShr(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrInt2Addr.java b/src/main/java/com/android/tools/r8/code/ShrInt2Addr.java
new file mode 100644
index 0000000..66c8967
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb9;
+  public static final String NAME = "ShrInt2Addr";
+  public static final String SMALI_NAME = "shr-int/2addr";
+
+  ShrInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShrInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShr(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrIntLit8.java b/src/main/java/com/android/tools/r8/code/ShrIntLit8.java
new file mode 100644
index 0000000..b8b53ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xe1;
+  public static final String NAME = "ShrIntLit8";
+  public static final String SMALI_NAME = "shr-int/lit8";
+
+  ShrIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShrIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShrLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrLong.java b/src/main/java/com/android/tools/r8/code/ShrLong.java
new file mode 100644
index 0000000..0827c59
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrLong extends Format23x {
+
+  public static final int OPCODE = 0xA4;
+  public static final String NAME = "ShrLong";
+  public static final String SMALI_NAME = "shr-long";
+
+  ShrLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShrLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShr(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/ShrLong2Addr.java b/src/main/java/com/android/tools/r8/code/ShrLong2Addr.java
new file mode 100644
index 0000000..d99dfd6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/ShrLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class ShrLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc4;
+  public static final String NAME = "ShrLong2Addr";
+  public static final String SMALI_NAME = "shr-long/2addr";
+
+  ShrLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public ShrLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addShr(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitch.java b/src/main/java/com/android/tools/r8/code/SparseSwitch.java
new file mode 100644
index 0000000..8e6862a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SparseSwitch.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.Switch.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+
+public class SparseSwitch extends Format31t {
+
+  public static final int OPCODE = 0x2c;
+  public static final String NAME = "SparseSwitch";
+  public static final String SMALI_NAME = "sparse-switch";
+
+  SparseSwitch(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SparseSwitch(int value) {
+    super(value, -1);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  @Override
+  public boolean isSwitch() {
+    return true;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    int offset = getOffset();
+    int payloadOffset = offset + getPayloadOffset();
+    int fallthroughOffset = offset + getSize();
+    builder.resolveAndBuildSwitch(Type.SPARSE, AA, fallthroughOffset, payloadOffset);
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    return formatSmaliString("v" + AA + ", :label_" + (getOffset() + BBBBBBBB));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
new file mode 100644
index 0000000..027c253b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+
+public class SparseSwitchPayload extends SwitchPayload {
+
+  public final int size;
+  public final int[] keys;
+  public final /* offset */ int[] targets;
+
+  public SparseSwitchPayload(int high, BytecodeStream stream) {
+    super(high, stream);
+    size = read16BitValue(stream);
+    keys = new int[size];
+    for (int i = 0; i < size; i++) {
+      keys[i] = readSigned32BitValue(stream);
+    }
+
+    targets = new int[size];
+    for (int i = 0; i < size; i++) {
+      targets[i] = readSigned32BitValue(stream);
+    }
+  }
+
+  public SparseSwitchPayload(int size, int[] keys, int[] targets) {
+    assert size > 0;  // Empty switches should be eliminated.
+    this.size = size;
+    this.keys = keys;
+    this.targets = targets;
+  }
+
+  public boolean isPayload() {
+    return true;
+  }
+
+  public void write(ShortBuffer dest, ObjectToOffsetMapping mapping) {
+    writeFirst(2, dest);  // Pseudo-opcode = 0x0200
+    write16BitValue(size, dest);
+    for (int i = 0; i < size; i++) {
+      write32BitValue(keys[i], dest);
+    }
+    for (int i = 0; i < size; i++) {
+      write32BitValue(targets[i], dest);
+    }
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (!super.equals(other)) {
+      return false;
+    }
+    SparseSwitchPayload that = (SparseSwitchPayload) other;
+    return size == that.size && Arrays.equals(keys, that.keys) && Arrays
+        .equals(targets, that.targets);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + size;
+    result = 31 * result + Arrays.hashCode(keys);
+    result = 31 * result + Arrays.hashCode(targets);
+    return result;
+  }
+
+  public int getSize() {
+    return 2 + (2 * keys.length) + (2 * targets.length);
+  }
+
+  public static int getSizeForTargets(int targets) {
+    return 2 + (4 * targets);
+  }
+
+  @Override
+  public int numberOfKeys() {
+    return size;
+  }
+
+  @Override
+  public int[] keys() {
+    return keys;
+  }
+
+  public int[] switchTargetOffsets() {
+    return targets;
+  }
+
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder("[SparseSwitchPayload]\n");
+    for (int i = 0; i < size; i++) {
+      StringUtils.appendLeftPadded(builder, keys[i] + " -> " + targets[i] + "\n", 20);
+    }
+    return super.toString(naming) + builder.toString();
+  }
+
+  public String toSmaliString(Instruction payloadUser) {
+    StringBuilder builder = new StringBuilder();
+    builder.append("    ");
+    builder.append(".sparse-switch");
+    builder.append("\n");
+    for (int i = 0; i < keys.length; i++) {
+      builder.append("      ");
+      builder.append("0x");
+      builder.append(StringUtils.hexString(keys[i], 8));
+      builder.append(" -> :label_");
+      builder.append(payloadUser.getOffset() + targets[i]);
+      builder.append("  # ");
+      builder.append(keys[i]);
+      builder.append("\n");
+    }
+    builder.append("    ");
+    builder.append(".end sparse-switch");
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Sput.java b/src/main/java/com/android/tools/r8/code/Sput.java
new file mode 100644
index 0000000..c501fe9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Sput.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Sput extends Format21c {
+
+  public static final int OPCODE = 0x67;
+  public static final String NAME = "Sput";
+  public static final String SMALI_NAME = "sput";
+
+  Sput(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public Sput(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(MemberType.SINGLE, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputBoolean.java b/src/main/java/com/android/tools/r8/code/SputBoolean.java
new file mode 100644
index 0000000..cea42d1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputBoolean.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static com.android.tools.r8.ir.code.MemberType.BOOLEAN;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputBoolean extends Format21c {
+
+  public static final int OPCODE = 0x6a;
+  public static final String NAME = "SputBoolean";
+  public static final String SMALI_NAME = "sput-boolean";
+
+  /*package*/ SputBoolean(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SputBoolean(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(BOOLEAN, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputByte.java b/src/main/java/com/android/tools/r8/code/SputByte.java
new file mode 100644
index 0000000..a60c477
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputByte.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputByte extends Format21c {
+
+  public static final int OPCODE = 0x6b;
+  public static final String NAME = "SputByte";
+  public static final String SMALI_NAME = "sput-byte";
+
+  /*package*/ SputByte(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SputByte(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(MemberType.BYTE, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputChar.java b/src/main/java/com/android/tools/r8/code/SputChar.java
new file mode 100644
index 0000000..c2bf45a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputChar.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputChar extends Format21c {
+
+  public static final int OPCODE = 0x6c;
+  public static final String NAME = "SputChar";
+  public static final String SMALI_NAME = "sput-char";
+
+  /*package*/ SputChar(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SputChar(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(MemberType.CHAR, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputObject.java b/src/main/java/com/android/tools/r8/code/SputObject.java
new file mode 100644
index 0000000..55bdf82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputObject.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputObject extends Format21c {
+
+  public static final int OPCODE = 0x69;
+  public static final String NAME = "SputObject";
+  public static final String SMALI_NAME = "sput-object";
+
+  /*package*/ SputObject(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SputObject(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(MemberType.OBJECT, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputShort.java b/src/main/java/com/android/tools/r8/code/SputShort.java
new file mode 100644
index 0000000..62819e2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputShort.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputShort extends Format21c {
+
+  public static final int OPCODE = 0x6d;
+  public static final String NAME = "SputShort";
+  public static final String SMALI_NAME = "sput-short";
+
+  /*package*/ SputShort(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SputShort(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(MemberType.SHORT, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SputWide.java b/src/main/java/com/android/tools/r8/code/SputWide.java
new file mode 100644
index 0000000..3c4f5c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SputWide.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SputWide extends Format21c {
+
+  public static final int OPCODE = 0x68;
+  public static final String NAME = "SputWide";
+  public static final String SMALI_NAME = "sput-wide";
+
+  /*package*/ SputWide(int high, BytecodeStream stream, OffsetToObjectMapping mapping) {
+    super(high, stream, mapping.getFieldMap());
+  }
+
+  public SputWide(int AA, DexField BBBB) {
+    super(AA, BBBB);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void registerUse(UseRegistry registry) {
+    registry.registerStaticFieldWrite(getField());
+  }
+
+  @Override
+  public DexField getField() {
+    return (DexField) BBBB;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addStaticPut(MemberType.WIDE, AA, getField());
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubDouble.java b/src/main/java/com/android/tools/r8/code/SubDouble.java
new file mode 100644
index 0000000..b5600b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubDouble.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubDouble extends Format23x {
+
+  public static final int OPCODE = 0xac;
+  public static final String NAME = "SubDouble";
+  public static final String SMALI_NAME = "sub-double";
+
+  SubDouble(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubDouble(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.DOUBLE, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubDouble2Addr.java b/src/main/java/com/android/tools/r8/code/SubDouble2Addr.java
new file mode 100644
index 0000000..725fd33
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubDouble2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubDouble2Addr extends Format12x {
+
+  public static final int OPCODE = 0xcc;
+  public static final String NAME = "SubDouble2Addr";
+  public static final String SMALI_NAME = "sub-double/2addr";
+
+  SubDouble2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubDouble2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.DOUBLE, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubFloat.java b/src/main/java/com/android/tools/r8/code/SubFloat.java
new file mode 100644
index 0000000..7ab0dfa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubFloat.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubFloat extends Format23x {
+
+  public static final int OPCODE = 0xA7;
+  public static final String NAME = "SubFloat";
+  public static final String SMALI_NAME = "sub-float";
+
+  SubFloat(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubFloat(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.FLOAT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubFloat2Addr.java b/src/main/java/com/android/tools/r8/code/SubFloat2Addr.java
new file mode 100644
index 0000000..0126bd2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubFloat2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubFloat2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc7;
+  public static final String NAME = "SubFloat2Addr";
+  public static final String SMALI_NAME = "sub-float/2addr";
+
+  SubFloat2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubFloat2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.FLOAT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubInt.java b/src/main/java/com/android/tools/r8/code/SubInt.java
new file mode 100644
index 0000000..735005c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubInt extends Format23x {
+
+  public static final int OPCODE = 0x91;
+  public static final String NAME = "SubInt";
+  public static final String SMALI_NAME = "sub-int";
+
+  SubInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubInt2Addr.java b/src/main/java/com/android/tools/r8/code/SubInt2Addr.java
new file mode 100644
index 0000000..5cd8be8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubInt2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class SubInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb1;
+  public static final String NAME = "SubInt2Addr";
+  public static final String SMALI_NAME = "sub-int/2addr";
+
+  SubInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubLong.java b/src/main/java/com/android/tools/r8/code/SubLong.java
new file mode 100644
index 0000000..a8a0b3b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class SubLong extends Format23x {
+
+  public static final int OPCODE = 0x9c;
+  public static final String NAME = "SubLong";
+  public static final String SMALI_NAME = "sub-long";
+
+  SubLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SubLong2Addr.java b/src/main/java/com/android/tools/r8/code/SubLong2Addr.java
new file mode 100644
index 0000000..b902e00
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SubLong2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class SubLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xbc;
+  public static final String NAME = "SubLong2Addr";
+  public static final String SMALI_NAME = "sub-long/2addr";
+
+  SubLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SubLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addSub(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/SwitchPayload.java b/src/main/java/com/android/tools/r8/code/SwitchPayload.java
new file mode 100644
index 0000000..0525e41
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/SwitchPayload.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public abstract class SwitchPayload extends Nop {
+  SwitchPayload(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public SwitchPayload() {
+  }
+
+  public abstract int[] keys();
+  public abstract int numberOfKeys();
+  public abstract int[] switchTargetOffsets();
+
+  @Override
+  public boolean isSwitchPayload() {
+    return true;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    // Switch payloads are not represented in the IR.
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/Throw.java b/src/main/java/com/android/tools/r8/code/Throw.java
new file mode 100644
index 0000000..eabef03
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/Throw.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class Throw extends Format11x {
+
+  public static final int OPCODE = 0x27;
+  public static final String NAME = "Throw";
+  public static final String SMALI_NAME = "throw";
+
+  Throw(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public Throw(int AA) {
+    super(AA);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addThrow(AA);
+  }
+
+  @Override
+  public boolean canThrow() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrInt.java b/src/main/java/com/android/tools/r8/code/UshrInt.java
new file mode 100644
index 0000000..64a7c3e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrInt extends Format23x {
+
+  public static final int OPCODE = 0x9A;
+  public static final String NAME = "UshrInt";
+  public static final String SMALI_NAME = "ushr-int";
+
+  UshrInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public UshrInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addUshr(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrInt2Addr.java b/src/main/java/com/android/tools/r8/code/UshrInt2Addr.java
new file mode 100644
index 0000000..240b663
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrInt2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xba;
+  public static final String NAME = "UshrInt2Addr";
+  public static final String SMALI_NAME = "ushr-int/2addr";
+
+  UshrInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public UshrInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addUshr(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrIntLit8.java b/src/main/java/com/android/tools/r8/code/UshrIntLit8.java
new file mode 100644
index 0000000..d3fada0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xe2;
+  public static final String NAME = "UshrIntLit8";
+  public static final String SMALI_NAME = "ushr-int/lit8";
+
+  UshrIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public UshrIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addUshrLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrLong.java b/src/main/java/com/android/tools/r8/code/UshrLong.java
new file mode 100644
index 0000000..0143494
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrLong.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class UshrLong extends Format23x {
+
+  public static final int OPCODE = 0xa5;
+  public static final String NAME = "UshrLong";
+  public static final String SMALI_NAME = "ushr-long";
+
+  UshrLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public UshrLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addUshr(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/UshrLong2Addr.java b/src/main/java/com/android/tools/r8/code/UshrLong2Addr.java
new file mode 100644
index 0000000..0ac851d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/UshrLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class UshrLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc5;
+  public static final String NAME = "UshrLong2Addr";
+  public static final String SMALI_NAME = "ushr-long/2addr";
+
+  UshrLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public UshrLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addUshr(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorInt.java b/src/main/java/com/android/tools/r8/code/XorInt.java
new file mode 100644
index 0000000..a3f6b0e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorInt.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorInt extends Format23x {
+
+  public static final int OPCODE = 0x97;
+  public static final String NAME = "XorInt";
+  public static final String SMALI_NAME = "xor-int";
+
+  XorInt(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public XorInt(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addXor(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorInt2Addr.java b/src/main/java/com/android/tools/r8/code/XorInt2Addr.java
new file mode 100644
index 0000000..0c6486f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorInt2Addr.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+public class XorInt2Addr extends Format12x {
+
+  public static final int OPCODE = 0xb7;
+  public static final String NAME = "XorInt2Addr";
+  public static final String SMALI_NAME = "xor-int/2addr";
+
+  XorInt2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public XorInt2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addXor(NumericType.INT, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorIntLit16.java b/src/main/java/com/android/tools/r8/code/XorIntLit16.java
new file mode 100644
index 0000000..8ce8120
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorIntLit16.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorIntLit16 extends Format22s {
+
+  public static final int OPCODE = 0xd7;
+  public static final String NAME = "XorIntLit16";
+  public static final String SMALI_NAME = "xor-int/lit16";
+
+  XorIntLit16(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public XorIntLit16(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addXorLiteral(NumericType.INT, A, B, CCCC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorIntLit8.java b/src/main/java/com/android/tools/r8/code/XorIntLit8.java
new file mode 100644
index 0000000..14beeef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorIntLit8.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorIntLit8 extends Format22b {
+
+  public static final int OPCODE = 0xdf;
+  public static final String NAME = "XorIntLit8";
+  public static final String SMALI_NAME = "xor-int/lit8";
+
+  XorIntLit8(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public XorIntLit8(int dest, int register, int constant) {
+    super(dest, register, constant);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addXorLiteral(NumericType.INT, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorLong.java b/src/main/java/com/android/tools/r8/code/XorLong.java
new file mode 100644
index 0000000..ec6de04
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorLong.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorLong extends Format23x {
+
+  public static final int OPCODE = 0xA2;
+  public static final String NAME = "XorLong";
+  public static final String SMALI_NAME = "xor-long";
+
+  XorLong(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public XorLong(int dest, int left, int right) {
+    super(dest, left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addXor(NumericType.LONG, AA, BB, CC);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/code/XorLong2Addr.java b/src/main/java/com/android/tools/r8/code/XorLong2Addr.java
new file mode 100644
index 0000000..23e4f36
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/XorLong2Addr.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+
+public class XorLong2Addr extends Format12x {
+
+  public static final int OPCODE = 0xc2;
+  public static final String NAME = "XorLong2Addr";
+  public static final String SMALI_NAME = "xor-long/2addr";
+
+  XorLong2Addr(int high, BytecodeStream stream) {
+    super(high, stream);
+  }
+
+  public XorLong2Addr(int left, int right) {
+    super(left, right);
+  }
+
+  public String getName() {
+    return NAME;
+  }
+
+  public String getSmaliName() {
+    return SMALI_NAME;
+  }
+
+  public int getOpcode() {
+    return OPCODE;
+  }
+
+  @Override
+  public void buildIR(IRBuilder builder) {
+    builder.addXor(NumericType.LONG, A, A, B);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
new file mode 100644
index 0000000..1e6751c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -0,0 +1,487 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.compatdx;
+
+import static com.android.tools.r8.utils.FileUtils.isApkFile;
+import static com.android.tools.r8.utils.FileUtils.isClassFile;
+import static com.android.tools.r8.utils.FileUtils.isDexFile;
+import static com.android.tools.r8.utils.FileUtils.isJarFile;
+import static com.android.tools.r8.utils.FileUtils.isZipFile;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.D8Output;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.DxUsageMessage;
+import com.android.tools.r8.compatdx.CompatDx.DxCompatOptions.PositionInfo;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.Closer;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+/**
+ * Dx compatibility interface for d8.
+ *
+ * This should become a mostly drop-in replacement for uses of the DX dexer (eg, dx --dex ...).
+ */
+public class CompatDx {
+
+  private static final String USAGE_HEADER = "Usage: compatdx [options] <input files>";
+
+  /**
+   * Compatibility options parsing for the DX --dex sub-command.
+   */
+  public static class DxCompatOptions {
+    // Final values after parsing.
+    // Note: These are ordered by their occurrence in "dx --help"
+    public final boolean help;
+    public final boolean debug;
+    public final boolean verbose;
+    public final PositionInfo positions;
+    public final boolean noLocals;
+    public final boolean noOptimize;
+    public final boolean statistics;
+    public final String optimizeList;
+    public final String noOptimizeList;
+    public final boolean noStrict;
+    public final boolean keepClasses;
+    public final String output;
+    public final String dumpTo;
+    public final int dumpWidth;
+    public final String dumpMethod;
+    public final boolean verboseDump;
+    public final boolean noFiles;
+    public final boolean coreLibrary;
+    public final int numThreads;
+    public final boolean incremental;
+    public final boolean forceJumbo;
+    public final boolean noWarning;
+    public final boolean multiDex;
+    public final String mainDexList;
+    public final boolean minimalMainDex;
+    public final int minApiLevel;
+    public final String inputList;
+    public final ImmutableList<String> inputs;
+    // Undocumented option
+    public final int maxIndexNumber;
+
+    private static final String FILE_ARG = "file";
+    private static final String NUM_ARG = "number";
+    private static final String METHOD_ARG = "method";
+
+    public enum PositionInfo {
+      NONE, IMPORTANT, LINES
+    }
+
+    // Exception thrown on invalid dx compat usage.
+    public static class DxUsageMessage extends Exception {
+      public final String message;
+
+      public DxUsageMessage() {
+        this("");
+      }
+
+      DxUsageMessage(String message) {
+        this.message = message;
+      }
+
+      void printHelpOn(PrintStream sink) throws IOException {
+        sink.println(message);
+      }
+    }
+
+    // Exception thrown on options parse error.
+    private static class DxParseError extends DxUsageMessage {
+      private final OptionParser parser;
+
+      private DxParseError(OptionParser parser) {
+        this.parser = parser;
+      }
+
+      @Override
+      public void printHelpOn(PrintStream sink) throws IOException {
+        parser.printHelpOn(sink);
+      }
+    }
+
+    // Parsing specification.
+    private static class Spec {
+      final OptionParser parser;
+
+      // Note: These are ordered by their occurrence in "dx --help"
+      final OptionSpec<Void> debug;
+      final OptionSpec<Void> verbose;
+      final OptionSpec<String> positions;
+      final OptionSpec<Void> noLocals;
+      final OptionSpec<Void> noOptimize;
+      final OptionSpec<Void> statistics;
+      final OptionSpec<String> optimizeList;
+      final OptionSpec<String> noOptimizeList;
+      final OptionSpec<Void> noStrict;
+      final OptionSpec<Void> keepClasses;
+      final OptionSpec<String> output;
+      final OptionSpec<String> dumpTo;
+      final OptionSpec<Integer> dumpWidth;
+      final OptionSpec<String> dumpMethod;
+      final OptionSpec<Void> verboseDump;
+      final OptionSpec<Void> noFiles;
+      final OptionSpec<Void> coreLibrary;
+      final OptionSpec<Integer> numThreads;
+      final OptionSpec<Void> incremental;
+      final OptionSpec<Void> forceJumbo;
+      final OptionSpec<Void> noWarning;
+      final OptionSpec<Void> multiDex;
+      final OptionSpec<String> mainDexList;
+      final OptionSpec<Void> minimalMainDex;
+      final OptionSpec<Integer> minApiLevel;
+      final OptionSpec<String> inputList;
+      final OptionSpec<String> inputs;
+      final OptionSpec<Void> help;
+      final OptionSpec<Integer> maxIndexNumber;
+
+      Spec() {
+        parser = new OptionParser();
+        parser.accepts("dex");
+        debug = parser.accepts("debug", "Print debug information");
+        verbose = parser.accepts("verbose", "Print verbose information");
+        positions = parser
+            .accepts("positions",
+                "What source-position information to keep. One of: none, lines, important")
+            .withOptionalArg()
+            .describedAs("keep")
+            .defaultsTo("lines");
+        noLocals = parser.accepts("no-locals", "Don't keep local variable information");
+        statistics = parser.accepts("statistics", "Print statistics information");
+        noOptimize = parser.accepts("no-optimize", "Don't optimize");
+        optimizeList = parser
+            .accepts("optimize-list", "File listing methods to optimize")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        noOptimizeList = parser
+            .accepts("no-optimize-list", "File listing methods not to optimize")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        noStrict = parser.accepts("no-strict", "Disable strict file/class name checks");
+        keepClasses = parser.accepts("keep-classes", "Keep input class files in in output jar");
+        output = parser
+            .accepts("output", "Output file or directory")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        dumpTo = parser
+            .accepts("dump-to", "File to dump information to")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        dumpWidth = parser
+            .accepts("dump-width", "Max width for columns in dump output")
+            .withRequiredArg()
+            .ofType(Integer.class)
+            .defaultsTo(0)
+            .describedAs(NUM_ARG);
+        dumpMethod = parser
+            .accepts("dump-method", "Method to dump information for")
+            .withRequiredArg()
+            .describedAs(METHOD_ARG);
+        verboseDump = parser.accepts("verbose-dump", "Dump verbose information");
+        noFiles = parser.accepts("no-files", "Don't fail if given no files");
+        coreLibrary = parser.accepts("core-library", "Construct a core library");
+        numThreads = parser
+            .accepts("num-threads", "Number of threads to run with")
+            .withRequiredArg()
+            .ofType(Integer.class)
+            .defaultsTo(1)
+            .describedAs(NUM_ARG);
+        incremental = parser.accepts("incremental", "Merge result with the output if it exists");
+        forceJumbo = parser.accepts("force-jumbo", "Force use of string-jumbo instructions");
+        noWarning = parser.accepts("no-warning", "Suppress warnings");
+        maxIndexNumber = parser.accepts("set-max-idx-number",
+            "Undocumented: Set maximal index number to use in a dex file.")
+            .withRequiredArg()
+            .ofType(Integer.class)
+            .defaultsTo(0)
+            .describedAs("Maximum index");
+        minimalMainDex = parser.accepts("minimal-main-dex", "Produce smallest possible main dex");
+        mainDexList = parser
+            .accepts("main-dex-list", "File listing classes that must be in the main dex file")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        multiDex = parser.accepts("multi-dex", "Allow generation of multi-dex")
+            .requiredIf(noStrict, minimalMainDex, mainDexList, maxIndexNumber);
+        minApiLevel = parser
+            .accepts("min-sdk-version", "Minimum Android API level compatibility.")
+            .withRequiredArg().ofType(Integer.class);
+        inputList = parser
+            .accepts("input-list", "File listing input files")
+            .withRequiredArg()
+            .describedAs(FILE_ARG);
+        inputs = parser.nonOptions("Input files");
+        help = parser.accepts("help", "Print this message").forHelp();
+      }
+    }
+
+    private DxCompatOptions(OptionSet options, Spec spec) throws DxParseError {
+      help = options.has(spec.help);
+      debug = options.has(spec.debug);
+      verbose = options.has(spec.verbose);
+      if (options.has(spec.positions)) {
+        switch (options.valueOf(spec.positions)) {
+          case "none":
+            positions = PositionInfo.NONE;
+            break;
+          case "important":
+            positions = PositionInfo.IMPORTANT;
+            break;
+          case "lines":
+            positions = PositionInfo.LINES;
+            break;
+          default:
+            // Should be checked by parser.
+            throw new DxParseError(spec.parser);
+        }
+      } else {
+        positions = PositionInfo.LINES;
+      }
+      noLocals = options.has(spec.noLocals);
+      noOptimize = options.has(spec.noOptimize);
+      statistics = options.has(spec.statistics);
+      optimizeList = options.valueOf(spec.optimizeList);
+      noOptimizeList = options.valueOf(spec.noOptimizeList);
+      noStrict = options.has(spec.noStrict);
+      keepClasses = options.has(spec.keepClasses);
+      output = options.valueOf(spec.output);
+      dumpTo = options.valueOf(spec.dumpTo);
+      dumpWidth = options.valueOf(spec.dumpWidth);
+      dumpMethod = options.valueOf(spec.dumpMethod);
+      verboseDump = options.has(spec.verboseDump);
+      noFiles = options.has(spec.noFiles);
+      coreLibrary = options.has(spec.coreLibrary);
+      numThreads = options.valueOf(spec.numThreads);
+      incremental = options.has(spec.incremental);
+      forceJumbo = options.has(spec.forceJumbo);
+      noWarning = options.has(spec.noWarning);
+      multiDex = options.has(spec.multiDex);
+      mainDexList = options.valueOf(spec.mainDexList);
+      minimalMainDex = options.has(spec.minimalMainDex);
+      minApiLevel = options.has(spec.minApiLevel)
+              ? options.valueOf(spec.minApiLevel)
+              : Constants.DEFAULT_ANDROID_API;
+      inputList = options.valueOf(spec.inputList);
+      inputs = ImmutableList.copyOf(options.valuesOf(spec.inputs));
+      maxIndexNumber = options.valueOf(spec.maxIndexNumber);
+    }
+
+    public static DxCompatOptions parse(String[] args) throws DxParseError {
+      Spec spec = new Spec();
+      return new DxCompatOptions(spec.parser.parse(args), spec);
+    }
+  }
+
+  public static void main(String[] args) throws IOException {
+    try {
+      run(args);
+    } catch (CompilationException e) {
+      System.err.println(e.getMessage());
+      System.exit(1);
+    } catch (DxUsageMessage e) {
+      System.err.println(USAGE_HEADER);
+      e.printHelpOn(System.err);
+      System.exit(1);
+    }
+  }
+
+  private static void run(String[] args) throws DxUsageMessage, IOException, CompilationException {
+    DxCompatOptions dexArgs = DxCompatOptions.parse(args);
+    if (dexArgs.help) {
+      printHelpOn(System.out);
+      return;
+    }
+    CompilationMode mode = CompilationMode.RELEASE;
+    Path output = null;
+    List<Path> inputs = new ArrayList<>();
+    boolean singleDexFile = !dexArgs.multiDex;
+    Path mainDexList = null;
+    int numberOfThreads = 1;
+
+    for (String path : dexArgs.inputs) {
+      processPath(new File(path), inputs);
+    }
+    if (inputs.isEmpty()) {
+      if (dexArgs.noFiles) {
+        return;
+      }
+      throw new DxUsageMessage("No input files specified");
+    }
+
+    if (!Log.ENABLED && (!dexArgs.noWarning || dexArgs.debug || dexArgs.verbose)) {
+      System.out.println("Warning: logging is not enabled for this build.");
+    }
+
+    if (dexArgs.verboseDump) {
+      throw new Unimplemented("verbose dump file not yet supported");
+    }
+
+    if (dexArgs.dumpMethod != null) {
+      throw new Unimplemented("method-dump not yet supported");
+    }
+
+    if (dexArgs.output != null) {
+      output = Paths.get(dexArgs.output);
+      if (FileUtils.isDexFile(output)) {
+        if (!singleDexFile) {
+          throw new DxUsageMessage("Cannot output to a single dex-file when running with multidex");
+        }
+      } else if (!FileUtils.isArchive(output)
+          && (!output.toFile().exists() || !output.toFile().isDirectory())) {
+        throw new DxUsageMessage("Unsupported output file or output directory does not exist. "
+            + "Output must be a directory or a file of type dex, apk, jar or zip.");
+      }
+    }
+
+    if (dexArgs.dumpTo != null) {
+      throw new Unimplemented("dump-to file not yet supported");
+    }
+
+    if (dexArgs.keepClasses) {
+      throw new Unimplemented("keeping classes in jar not yet supported");
+    }
+
+    if (dexArgs.positions != PositionInfo.NONE) {
+      mode = CompilationMode.DEBUG;
+    }
+
+    if (!dexArgs.noLocals) {
+      mode = CompilationMode.DEBUG;
+    }
+
+    if (dexArgs.incremental) {
+      throw new Unimplemented("incremental merge not supported yet");
+    }
+
+    if (dexArgs.forceJumbo) {
+      throw new Unimplemented("force jumbo not yet supported");
+    }
+
+    if (dexArgs.noOptimize) {
+      System.out.println("Warning: no support for not optimizing yet");
+    }
+
+    if (dexArgs.optimizeList != null) {
+      throw new Unimplemented("no support for optimize-method list");
+    }
+
+    if (dexArgs.noOptimizeList != null) {
+      throw new Unimplemented("no support for dont-optimize-method list");
+    }
+
+    if (dexArgs.statistics) {
+      throw new Unimplemented("no support for printing statistics yet");
+    }
+
+    if (dexArgs.numThreads > 1) {
+      numberOfThreads = dexArgs.numThreads;
+    }
+
+    if (dexArgs.mainDexList != null) {
+      mainDexList = Paths.get(dexArgs.mainDexList);
+    }
+
+    if (dexArgs.noStrict) {
+      System.out.println("Warning: conservative main-dex list not yet supported");
+    }
+
+    if (dexArgs.minimalMainDex) {
+      System.out.println("Warning: minimal main-dex support is not yet supported");
+    }
+
+    if (dexArgs.maxIndexNumber != 0) {
+      System.out.println("Warning: internal maximum-index setting is not supported");
+    }
+
+    if (numberOfThreads < 1) {
+      throw new DxUsageMessage("Invalid numThreads value of " + numberOfThreads);
+    }
+    ExecutorService executor = ThreadUtils.getExecutorService(numberOfThreads);
+    D8Output result;
+    try {
+       result = D8.run(
+          D8Command.builder()
+              .addProgramFiles(inputs)
+              .setMode(mode)
+              .setMinApiLevel(dexArgs.minApiLevel)
+              .setMainDexListFile(mainDexList)
+              .build());
+    } finally {
+      executor.shutdown();
+    }
+
+    if (output == null) {
+      return;
+    }
+
+    if (singleDexFile) {
+      if (result.getDexResources().size() > 1) {
+        throw new CompilationError(
+            "Compilation result could not fit into a single dex file. "
+                + "Reduce the input-program size or run with --multi-dex enabled");
+      }
+      if (isDexFile(output)) {
+        try (Closer closer = Closer.create()) {
+          InputStream stream = result.getDexResources().get(0).getStream(closer);
+          Files.copy(stream, output, StandardCopyOption.REPLACE_EXISTING);
+        }
+        return;
+      }
+    }
+
+    result.write(output);
+  }
+
+  static void printHelpOn(PrintStream sink) throws IOException {
+    sink.println(USAGE_HEADER);
+    new DxCompatOptions.Spec().parser.printHelpOn(sink);
+  }
+
+  private static void processPath(File file, List<Path> files) {
+    if (!file.exists()) {
+      throw new CompilationError("File does not exist: " + file);
+    }
+    if (file.isDirectory()) {
+      processDirectory(file, files);
+      return;
+    }
+    Path path = file.toPath();
+    if (isZipFile(path) || isJarFile(path) || isClassFile(path)) {
+      files.add(path);
+      return;
+    }
+    if (isApkFile(path)) {
+      throw new Unimplemented("apk files not yet supported");
+    }
+  }
+
+  private static void processDirectory(File directory, List<Path> files) {
+    assert directory.exists();
+    for (File file : directory.listFiles()) {
+      processPath(file, files);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
new file mode 100644
index 0000000..672f6bd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -0,0 +1,221 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.ANDROID_N_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_N_DEX_VERSION;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_DEX_VERSION;
+import static com.android.tools.r8.dex.Constants.DEFAULT_ANDROID_API;
+import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.graph.JarClassFileReader;
+import com.android.tools.r8.graph.LazyClassFileLoader;
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalResource;
+import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class ApplicationReader {
+
+  final InternalOptions options;
+  final DexItemFactory itemFactory;
+  final Timing timing;
+  private final AndroidApp inputApp;
+
+  public ApplicationReader(AndroidApp inputApp, InternalOptions options, Timing timing) {
+    this.options = options;
+    itemFactory = options.itemFactory;
+    this.timing = timing;
+    this.inputApp = inputApp;
+  }
+
+  public DexApplication read() throws IOException, ExecutionException {
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+    try {
+      return read(executor);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  public final DexApplication read(ExecutorService executorService)
+      throws IOException, ExecutionException {
+    timing.begin("DexApplication.read");
+    final DexApplication.Builder builder = new DexApplication.Builder(itemFactory, timing);
+    try (Closer closer = Closer.create()) {
+      List<Future<?>> futures = new ArrayList<>();
+      readProguardMap(builder, executorService, futures, closer);
+      readMainDexList(builder, executorService, futures, closer);
+      readDexSources(builder, executorService, futures, closer);
+      readClassSources(builder, closer);
+      ThreadUtils.awaitFutures(futures);
+    } catch (ExecutionException e) {
+      // If the reading failed with a valid compilation error, rethrow the unwrapped exception.
+      Throwable cause = e.getCause();
+      if (cause != null && cause instanceof CompilationError) {
+        throw (CompilationError) cause;
+      }
+      throw e;
+    } finally {
+      timing.end();
+    }
+    return builder.build();
+  }
+
+  private void readClassSources(DexApplication.Builder builder, Closer closer)
+      throws IOException, ExecutionException {
+    JarApplicationReader application = new JarApplicationReader(options);
+    JarClassFileReader reader = new JarClassFileReader(
+        application, builder::addClassIgnoringLibraryDuplicates);
+    for (InternalResource input : inputApp.getClassProgramResources()) {
+      reader.read(DEFAULT_DEX_FILENAME, InternalResource.Kind.PROGRAM, input.getStream(closer));
+    }
+    for (InternalResource input : inputApp.getClassClasspathResources()) {
+      if (options.lazyClasspathLoading && input.getClassDescriptor() != null) {
+        addLazyLoader(application, builder, input);
+      } else {
+        reader.read(DEFAULT_DEX_FILENAME, InternalResource.Kind.CLASSPATH, input.getStream(closer));
+      }
+    }
+    for (InternalResource input : inputApp.getClassLibraryResources()) {
+      if (options.lazyLibraryLoading && input.getClassDescriptor() != null) {
+        addLazyLoader(application, builder, input);
+      } else {
+        reader.read(DEFAULT_DEX_FILENAME, InternalResource.Kind.LIBRARY, input.getStream(closer));
+      }
+    }
+  }
+
+  private void addLazyLoader(JarApplicationReader application,
+      DexApplication.Builder builder, InternalResource resource) {
+    // Generate expected DEX type.
+    String classDescriptor = resource.getClassDescriptor();
+    assert classDescriptor != null;
+    DexType type = options.itemFactory.createType(classDescriptor);
+    LazyClassFileLoader newLoader = new LazyClassFileLoader(type, resource, application);
+    builder.addClassPromise(newLoader, true);
+  }
+
+  private void readDexSources(DexApplication.Builder builder, ExecutorService executorService,
+      List<Future<?>> futures, Closer closer)
+      throws IOException, ExecutionException {
+    List<InternalResource> dexProgramSources = inputApp.getDexProgramResources();
+    List<InternalResource> dexClasspathSources = inputApp.getDexClasspathResources();
+    List<InternalResource> dexLibrarySources = inputApp.getDexLibraryResources();
+    int numberOfFiles = dexProgramSources.size()
+        + dexLibrarySources.size() + dexClasspathSources.size();
+    if (numberOfFiles > 0) {
+      List<DexFileReader> fileReaders = new ArrayList<>(numberOfFiles);
+      int computedMinApiLevel = options.minApiLevel;
+      for (InternalResource input : dexProgramSources) {
+        DexFile file = new DexFile(input.getStream(closer));
+        computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
+        fileReaders.add(new DexFileReader(file, InternalResource.Kind.PROGRAM, itemFactory));
+      }
+      for (InternalResource input : dexClasspathSources) {
+        DexFile file = new DexFile(input.getStream(closer));
+        fileReaders.add(new DexFileReader(file, InternalResource.Kind.CLASSPATH, itemFactory));
+      }
+      for (InternalResource input : dexLibrarySources) {
+        DexFile file = new DexFile(input.getStream(closer));
+        computedMinApiLevel = verifyOrComputeMinApiLevel(computedMinApiLevel, file);
+        fileReaders.add(new DexFileReader(file, InternalResource.Kind.LIBRARY, itemFactory));
+      }
+      options.minApiLevel = computedMinApiLevel;
+      for (DexFileReader reader : fileReaders) {
+        DexFileReader.populateIndexTables(reader);
+      }
+      // Read the DexCode items and DexProgramClass items in parallel.
+      for (DexFileReader reader : fileReaders) {
+        futures.add(executorService.submit(() -> {
+          reader.addCodeItemsTo();  // Depends on Everything for parsing.
+          reader.addClassDefsTo(builder::addClassPromise);  // Depends on Methods, Code items etc.
+        }));
+      }
+    }
+  }
+
+  private int verifyOrComputeMinApiLevel(int computedMinApiLevel, DexFile file) {
+    int version = file.getDexVersion();
+    if (options.minApiLevel == DEFAULT_ANDROID_API) {
+      computedMinApiLevel = Math.max(computedMinApiLevel, dexVersionToMinSdk(version));
+    } else if (!minApiMatchesDexVersion(version)) {
+      throw new CompilationError("Dex file with version '" + version +
+          "' cannot be used with min sdk level '" + options.minApiLevel + "'.");
+    }
+    return computedMinApiLevel;
+  }
+
+  private boolean minApiMatchesDexVersion(int version) {
+    switch (version) {
+      case ANDROID_O_DEX_VERSION:
+        return options.minApiLevel >= ANDROID_O_API;
+      case ANDROID_N_DEX_VERSION:
+        return options.minApiLevel >= ANDROID_N_API;
+      default:
+        return true;
+    }
+  }
+
+  private int dexVersionToMinSdk(int version) {
+    switch (version) {
+      case ANDROID_O_DEX_VERSION:
+        return ANDROID_O_API;
+      case ANDROID_N_DEX_VERSION:
+        return ANDROID_N_API;
+      default:
+        return DEFAULT_ANDROID_API;
+    }
+  }
+
+  private void readProguardMap(DexApplication.Builder builder, ExecutorService executorService,
+      List<Future<?>> futures, Closer closer)
+      throws IOException {
+    // Read the Proguard mapping file in parallel with DexCode and DexProgramClass items.
+    if (inputApp.hasProguardMap()) {
+      futures.add(executorService.submit(() -> {
+        try {
+          InputStream map = inputApp.getProguardMap(closer);
+          builder.setProguardMap(ProguardMapReader.mapperFromInputStream(map));
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }));
+    }
+  }
+
+  private void readMainDexList(DexApplication.Builder builder, ExecutorService executorService,
+      List<Future<?>> futures, Closer closer)
+      throws IOException {
+    if (inputApp.hasMainDexList()) {
+      futures.add(executorService.submit(() -> {
+        try {
+          InputStream input = inputApp.getMainDexList(closer);
+          builder.addToMainDexList(MainDexList.parse(input, itemFactory));
+        } catch (IOException e) {
+          throw new RuntimeException(e);
+        }
+      }));
+    }
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
new file mode 100644
index 0000000..d86f929
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.naming.MinifiedNameMapPrinter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PackageDistribution;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class ApplicationWriter {
+
+  public final DexApplication application;
+  public final AppInfo appInfo;
+  public final NamingLens namingLens;
+  public final byte[] proguardSeedsData;
+  public final InternalOptions options;
+
+  private static class SortAnnotations extends MixedSectionCollection {
+
+    @Override
+    public boolean add(DexAnnotationSet dexAnnotationSet) {
+      // Annotation sets are sorted by annotation types.
+      dexAnnotationSet.sort();
+      return true;
+    }
+
+    @Override
+    public boolean add(DexAnnotation annotation) {
+      // The elements of encoded annotation must be sorted by name.
+      annotation.annotation.sort();
+      return true;
+    }
+
+    @Override
+    public boolean add(DexEncodedArray dexEncodedArray) {
+      // Dex values must potentially be sorted, eg, for DexValueAnnotation.
+      for (DexValue value : dexEncodedArray.values) {
+        value.sort();
+      }
+      return true;
+    }
+
+    @Override
+    public boolean add(DexProgramClass dexClassData) {
+      return true;
+    }
+
+    @Override
+    public boolean add(DexCode dexCode) {
+      return true;
+    }
+
+    @Override
+    public boolean add(DexDebugInfo dexDebugInfo) {
+      return true;
+    }
+
+    @Override
+    public boolean add(DexTypeList dexTypeList) {
+      return true;
+    }
+
+    @Override
+    public boolean add(DexAnnotationSetRefList annotationSetRefList) {
+      return true;
+    }
+  }
+
+  public ApplicationWriter(
+      DexApplication application,
+      AppInfo appInfo,
+      InternalOptions options,
+      NamingLens namingLens,
+      byte[] proguardSeedsData) {
+    assert application != null;
+    this.application = application;
+    this.appInfo = appInfo;
+    assert options != null;
+    this.options = options;
+    this.namingLens = namingLens;
+    this.proguardSeedsData = proguardSeedsData;
+  }
+
+  public AndroidApp write(PackageDistribution packageDistribution, ExecutorService executorService)
+      throws IOException, ExecutionException {
+    application.timing.begin("DexApplication.write");
+    try {
+      application.dexItemFactory.sort(namingLens);
+      SortAnnotations sortAnnotations = new SortAnnotations();
+      application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
+      Map<Integer, VirtualFile> newFiles =
+          VirtualFile.fileSetFrom(this, packageDistribution, executorService);
+
+      // Write the dex files and the Proguard mapping file in parallel.
+      List<Future<byte[]>> dexDataFutures = new ArrayList<>();
+      for (Integer index : newFiles.keySet()) {
+        VirtualFile newFile = newFiles.get(index);
+        if (!newFile.isEmpty()) {
+          dexDataFutures.add(executorService.submit(() -> writeDexFile(newFile)));
+        }
+      }
+
+      // Wait for all the spawned futures to terminate.
+      List<byte[]> dexData = ThreadUtils.awaitFutures(dexDataFutures);
+      AndroidApp.Builder builder = AndroidApp.builder();
+      dexData.forEach(builder::addDexProgramData);
+      // Write the proguard map file after writing the dex files, as the map writer traverses
+      // the DexProgramClass structures, which are destructively updated during dex file writing.
+      byte[] proguardMapResult = writeProguardMapFile();
+      if (proguardMapResult != null) {
+        builder.setProguardMapData(proguardMapResult);
+      }
+      if (proguardSeedsData != null) {
+        builder.setProguardSeedsData(proguardSeedsData);
+      }
+      return builder.build();
+    } finally {
+      application.timing.end();
+    }
+  }
+
+  private byte[] writeDexFile(VirtualFile vfile) {
+    FileWriter fileWriter =
+        new FileWriter(
+            vfile.computeMapping(application), application, appInfo, options, namingLens);
+    // The file writer now knows the indexes of the fixed sections including strings.
+    fileWriter.rewriteCodeWithJumboStrings(vfile.classes());
+    // Collect the non-fixed sections.
+    fileWriter.collect();
+    // Generate and write the bytes.
+    return fileWriter.generate();
+  }
+
+  private byte[] writeProguardMapFile() throws IOException {
+    // TODO(herhut): Should writing of the proguard-map file be split like this?
+    if (!namingLens.isIdentityLens()) {
+      MinifiedNameMapPrinter printer = new MinifiedNameMapPrinter(application, namingLens);
+      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+      PrintStream stream = new PrintStream(bytes);
+      printer.write(stream);
+      stream.flush();
+      return bytes.toByteArray();
+    } else if (application.getProguardMap() != null) {
+      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+      Writer writer = new PrintWriter(bytes);
+      application.getProguardMap().write(writer, !options.skipDebugLineNumberOpt);
+      writer.flush();
+      return bytes.toByteArray();
+    }
+    return null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
new file mode 100644
index 0000000..201fc82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+public class Constants {
+
+  public static final byte[] DEX_FILE_MAGIC_PREFIX = {'d', 'e', 'x', '\n'};
+  public static final byte DEX_FILE_MAGIC_SUFFIX = '\0';
+
+  public static final int ANDROID_O_API = 26;
+  public static final int ANDROID_N_API = 24;
+  public static final int ANDROID_K_API = 19;
+  public static final int DEFAULT_ANDROID_API = 1;
+
+  /** dex file version number for Android O (API level 26) */
+  public static final int ANDROID_O_DEX_VERSION = 38;
+  public static final byte[] ANDROID_O_DEX_VERSION_BYTES = {'0', '3', '8'};
+  /** dex file version number for Android N (API level 24) */
+  public static final int ANDROID_N_DEX_VERSION = 37;
+  public static final byte[] ANDROID_N_DEX_VERSION_BYTES = {'0', '3', '7'};
+  /** dex file version number for all releases prior to Android N */
+  public static final int ANDROID_PRE_N_DEX_VERSION = 35;
+  public static final byte[] ANDROID_PRE_N_DEX_VERSION_BYTES = {'0', '3', '5'};
+
+  public static final int DEX_MAGIC_SIZE = 8;
+
+  public static final int HEADER_SIZE = 0x70;
+  public static final int MAGIC_OFFSET = 0;
+  public static final int CHECKSUM_OFFSET = MAGIC_OFFSET + DEX_MAGIC_SIZE;
+  public static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + 4;
+  public static final int FILE_SIZE_OFFSET = SIGNATURE_OFFSET + 20;
+  public static final int HEADER_SIZE_OFFSET = FILE_SIZE_OFFSET + 4;
+  public static final int ENDIAN_TAG_OFFSET = HEADER_SIZE_OFFSET + 4;
+  public static final int LINK_SIZE_OFFSET = ENDIAN_TAG_OFFSET + 4;
+  public static final int LINK_OFF_OFFSET = LINK_SIZE_OFFSET + 4;
+  public static final int MAP_OFF_OFFSET = LINK_OFF_OFFSET + 4;
+  public static final int STRING_IDS_SIZE_OFFSET = MAP_OFF_OFFSET + 4;
+  public static final int STRING_IDS_OFF_OFFSET = STRING_IDS_SIZE_OFFSET + 4;
+  public static final int TYPE_IDS_SIZE_OFFSET = STRING_IDS_OFF_OFFSET + 4;
+  public static final int TYPE_IDS_OFF_OFFSET = TYPE_IDS_SIZE_OFFSET + 4;
+  public static final int PROTO_IDS_SIZE_OFFSET = TYPE_IDS_OFF_OFFSET + 4;
+  public static final int PROTO_IDS_OFF_OFFSET = PROTO_IDS_SIZE_OFFSET + 4;
+  public static final int FIELD_IDS_SIZE_OFFSET = PROTO_IDS_OFF_OFFSET + 4;
+  public static final int FIELD_IDS_OFF_OFFSET = FIELD_IDS_SIZE_OFFSET + 4;
+  public static final int METHOD_IDS_SIZE_OFFSET = FIELD_IDS_OFF_OFFSET + 4;
+  public static final int METHOD_IDS_OFF_OFFSET = METHOD_IDS_SIZE_OFFSET + 4;
+  public static final int CLASS_DEFS_SIZE_OFFSET = METHOD_IDS_OFF_OFFSET + 4;
+  public static final int CLASS_DEFS_OFF_OFFSET = CLASS_DEFS_SIZE_OFFSET + 4;
+  public static final int DATA_SIZE_OFFSET = CLASS_DEFS_OFF_OFFSET + 4;
+  public static final int DATA_OFF_OFFSET = DATA_SIZE_OFFSET + 4;
+
+  public static final int ENDIAN_CONSTANT = 0x12345678;
+  public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
+
+  public static final int TYPE_HEADER_ITEM = 0x0;
+  public static final int TYPE_HEADER_ITEM_SIZE = 0x70;
+  public static final int TYPE_STRING_ID_ITEM = 0x0001;
+  public static final int TYPE_STRING_ID_ITEM_SIZE = 0x04;
+  public static final int TYPE_TYPE_ID_ITEM = 0x0002;
+  public static final int TYPE_TYPE_ID_ITEM_SIZE = 0x04;
+  public static final int TYPE_PROTO_ID_ITEM = 0x0003;
+  public static final int TYPE_PROTO_ID_ITEM_SIZE = 0x0c;
+  public static final int TYPE_FIELD_ID_ITEM = 0x0004;
+  public static final int TYPE_FIELD_ID_ITEM_SIZE = 0x08;
+  public static final int TYPE_METHOD_ID_ITEM = 0x0005;
+  public static final int TYPE_METHOD_ID_ITEM_SIZE = 0x08;
+  public static final int TYPE_CLASS_DEF_ITEM = 0x0006;
+  public static final int TYPE_CLASS_DEF_ITEM_SIZE = 0x20;
+  public static final int TYPE_CALL_SITE_ID_ITEM = 0x0007;
+  public static final int TYPE_CALL_SITE_ID_ITEM_SIZE = 0x04;
+  public static final int TYPE_METHOD_HANDLE_ITEM = 0x0008;
+  public static final int TYPE_METHOD_HANDLE_ITEM_SIZE = 0x0008;
+  public static final int TYPE_MAP_LIST = 0x1000;
+  public static final int TYPE_MAP_LIST_ITEM_SIZE = 0x0c;
+  public static final int TYPE_TYPE_LIST = 0x1001;
+  public static final int TYPE_ANNOTATION_SET_REF_LIST = 0x1002;
+  public static final int TYPE_ANNOTATION_SET_ITEM = 0x1003;
+  public static final int TYPE_CLASS_DATA_ITEM = 0x2000;
+  public static final int TYPE_CODE_ITEM = 0x2001;
+  public static final int TYPE_STRING_DATA_ITEM = 0x2002;
+  public static final int TYPE_DEBUG_INFO_ITEM = 0x2003;
+  public static final int TYPE_ANNOTATION_ITEM = 0x2004;
+  public static final int TYPE_ENCODED_ARRAY_ITEM = 0x2005;
+  public static final int TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006;
+
+  public static final int DBG_START_LOCAL = 0x03;
+  public static final int DBG_START_LOCAL_EXTENDED = 0x04;
+  public static final int DBG_END_LOCAL = 0x05;
+  public static final int DBG_RESTART_LOCAL = 0x06;
+  public static final int DBG_SET_FILE = 0x09;
+  public static final int DBG_END_SEQUENCE = 0x00;
+  public static final int DBG_ADVANCE_PC = 0x01;
+  public static final int DBG_ADVANCE_LINE = 0x02;
+  public static final int DBG_SET_PROLOGUE_END = 0x07;
+  public static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
+  public static final int DBG_FIRST_SPECIAL = 0x0a;
+  public static final int DBG_LAST_SPECIAL = 0xff;
+  public static final int DBG_LINE_BASE = -4;
+  public static final int DBG_LINE_RANGE = 15;
+  public static final int DBG_ADDRESS_RANGE = 16;
+
+  public static final int NO_OFFSET = 0;
+  public static final int NO_INDEX = -1;
+
+  public static final int S4BIT_SIGN_MASK = 1 << 3;
+  public static final int S4BIT_MIN = -(1 << 3);
+  public static final int S4BIT_MAX = (1 << 3) - 1;
+  public static final int S8BIT_MIN = -(1 << 7);
+  public static final int S8BIT_MAX = (1 << 7) - 1;
+
+  public static final int U4BIT_MAX = (1 << 4) - 1;
+  public static final int U8BIT_MAX = (1 << 8) - 1;
+  public static final int U16BIT_MAX = (1 << 16) - 1;
+  public static final int ACC_PUBLIC = 0x1;
+  public static final int ACC_PRIVATE = 0x2;
+  public static final int ACC_PROTECTED = 0x4;
+  public static final int ACC_STATIC = 0x8;
+  public static final int ACC_FINAL = 0x10;
+  public static final int ACC_SYNCHRONIZED = 0x20;
+  public static final int ACC_VOLATILE = 0x40;
+  public static final int ACC_BRIDGE = 0x40;
+  public static final int ACC_TRANSIENT = 0x80;
+  public static final int ACC_VARARGS = 0x80;
+  public static final int ACC_NATIVE = 0x100;
+  public static final int ACC_INTERFACE = 0x200;
+  public static final int ACC_ABSTRACT = 0x400;
+  public static final int ACC_STRICT = 0x800;
+  public static final int ACC_SYNTHETIC = 0x1000;
+  public static final int ACC_ANNOTATION = 0x2000;
+  public static final int ACC_ENUM = 0x4000;
+  public static final int ACC_CONSTRUCTOR = 0x10000;
+  public static final int ACC_DECLARED_SYNCHRONIZED = 0x20000;
+
+  public static final String JAVA_LANG_OBJECT_NAME = "java/lang/Object";
+  public static final String INSTANCE_INITIALIZER_NAME = "<init>";
+  public static final String CLASS_INITIALIZER_NAME = "<clinit>";
+
+  public static final int MAX_NON_JUMBO_INDEX = U16BIT_MAX;
+  public static final int FIRST_JUMBO_INDEX = MAX_NON_JUMBO_INDEX + 1;
+
+  public static final int KILOBYTE = 1 << 10;
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
new file mode 100644
index 0000000..06dbcfe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DebugBytecodeWriter.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.LebUtils;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+public class DebugBytecodeWriter {
+
+  private final ObjectToOffsetMapping mapping;
+  private final DexDebugInfo info;
+  private ByteBuffer buffer;
+
+  public DebugBytecodeWriter(DexDebugInfo info, ObjectToOffsetMapping mapping) {
+    this.info = info;
+    this.mapping = mapping;
+    // Never allocate a zero-sized buffer, as we need to write the header, and the growth policy
+    // requires it to have a positive capacity.
+    this.buffer = ByteBuffer.allocate(info.events.length * 5 + 4);
+  }
+
+  public byte[] generate() {
+    // Header.
+    putUleb128(info.startLine); // line_start
+    putUleb128(info.parameters.length);
+    for (DexString name : info.parameters) {
+      putString(name);
+    }
+    // Body.
+    for (DexDebugEvent event : info.events) {
+      event.writeOn(this, mapping);
+    }
+    // Tail.
+    putByte(Constants.DBG_END_SEQUENCE);
+    return Arrays.copyOf(buffer.array(), buffer.position());
+  }
+
+  private void maybeGrow(int size) {
+    if (buffer.remaining() < size) {
+      ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
+      newBuffer.put(buffer.array(), 0, buffer.position());
+      buffer = newBuffer;
+    }
+  }
+
+  public void putByte(int item) {
+    maybeGrow(1);
+    buffer.put((byte) item);
+  }
+
+  public void putSleb128(int item) {
+    byte[] encoded = LebUtils.encodeSleb128(item);
+    maybeGrow(encoded.length);
+    buffer.put(encoded, 0, encoded.length);
+  }
+
+  public void putUleb128(int item) {
+    byte[] encoded = LebUtils.encodeUleb128(item);
+    maybeGrow(encoded.length);
+    buffer.put(encoded, 0, encoded.length);
+  }
+
+  private void putUleb128p1(int item) {
+    putUleb128(item + 1);
+  }
+
+  private void putNoIndex() {
+    putUleb128(0);
+  }
+
+  public void putType(DexType type) {
+    if (type == null) {
+      putNoIndex();
+    } else {
+      int index = mapping.getOffsetFor(type);
+      putUleb128p1(index);
+    }
+  }
+
+  public void putString(DexString string) {
+    if (string == null) {
+      putNoIndex();
+    } else {
+      int index = mapping.getOffsetFor(string);
+      putUleb128p1(index);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexFile.java b/src/main/java/com/android/tools/r8/dex/DexFile.java
new file mode 100644
index 0000000..28aff79
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DexFile.java
@@ -0,0 +1,169 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.DEX_FILE_MAGIC_PREFIX;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.LebUtils;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class DexFile {
+
+  final String name;
+  private final ByteBuffer buffer;
+  private final int version;
+
+  DexFile(String name) throws IOException {
+    this.name = name;
+    Path path = Paths.get(name);
+    buffer = ByteBuffer.wrap(Files.readAllBytes(path));
+    version = parseMagic(buffer);
+  }
+
+  DexFile(InputStream input) throws IOException {
+    // TODO(zerny): Remove dependencies on file names.
+    name = "input-stream.dex";
+    buffer = ByteBuffer.wrap(ByteStreams.toByteArray(input));
+    version = parseMagic(buffer);
+  }
+
+  /**
+   * Returns a File that contains the bytes provided as argument. Used for testing.
+   *
+   * @param bytes contents of the file
+   */
+  DexFile(byte[] bytes) {
+    this.name = "mockfile.dex";
+    buffer = ByteBuffer.wrap(bytes);
+    version = parseMagic(buffer);
+  }
+
+  // Parse the magic header and determine the dex file version.
+  private int parseMagic(ByteBuffer buffer) {
+    int index = 0;
+    for (byte prefixByte : DEX_FILE_MAGIC_PREFIX) {
+      if (buffer.get(index++) != prefixByte) {
+        throw new CompilationError("Dex file has invalid header: " + name);
+      }
+    }
+    if (buffer.get(index++) != '0' || buffer.get(index++) != '3') {
+      throw new CompilationError("Dex file has invalid version number: " + name);
+    }
+    byte versionByte = buffer.get(index++);
+    int version;
+    switch (versionByte) {
+      case '8':
+        version = 38;
+        break;
+      case '7':
+        version = 37;
+        break;
+      case '5':
+        version = 35;
+        break;
+      default:
+        throw new CompilationError("Dex file has invalid version number: " + name);
+    }
+    if (buffer.get(index++) != '\0') {
+      throw new CompilationError("Dex file has invalid header: " + name);
+    }
+    return version;
+  }
+
+  int getDexVersion() {
+    return version;
+  }
+
+  byte[] getByteArray(int size) {
+    byte[] result = new byte[size];
+    buffer.get(result);
+    return result;
+  }
+
+  int getUleb128() {
+    return LebUtils.parseUleb128(this);
+  }
+
+  int getSleb128() {
+    return LebUtils.parseSleb128(this);
+  }
+
+  int getUleb128p1() {
+    return getUleb128() - 1;
+  }
+
+  int getUint() {
+    int result = buffer.getInt();
+    assert result >= 0;  // Ensure the java int didn't overflow.
+    return result;
+  }
+
+  int getUshort() {
+    int result = buffer.getShort() & 0xffff;
+    assert result >= 0;  // Ensure we have a non-negative number.
+    return result;
+  }
+
+  short getShort() {
+    return buffer.getShort();
+  }
+
+  int getUint(int offset) {
+    int result = buffer.getInt(offset);
+    assert result >= 0;  // Ensure the java int didn't overflow.
+    return result;
+  }
+
+  public int getInt() {
+    return buffer.getInt();
+  }
+
+  void setByteOrder() {
+    // Make sure we set the right endian for reading.
+    buffer.order(ByteOrder.LITTLE_ENDIAN);
+    int endian = buffer.getInt(Constants.ENDIAN_TAG_OFFSET);
+    if (endian == Constants.REVERSE_ENDIAN_CONSTANT) {
+      buffer.order(ByteOrder.BIG_ENDIAN);
+    } else {
+      assert endian == Constants.ENDIAN_CONSTANT;
+    }
+  }
+
+  int position() {
+    return buffer.position();
+  }
+
+  void position(int position) {
+    buffer.position(position);
+  }
+
+  void align(int alignment) {
+    assert (alignment & (alignment - 1)) == 0;   // Check alignment is power of 2.
+    int p = buffer.position();
+    p += (alignment - (p % alignment)) & (alignment - 1);
+    buffer.position(p);
+  }
+
+  public byte get() {
+    return buffer.get();
+  }
+
+  int getUbyte() {
+    int result = buffer.get() & 0xff;
+    assert result >= 0;  // Ensure we have a non-negative result.
+    return result;
+  }
+
+  int end() {
+    return buffer.capacity();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
new file mode 100644
index 0000000..bdafdc4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -0,0 +1,1018 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.utils.EncodedValueUtils.parseDouble;
+import static com.android.tools.r8.utils.EncodedValueUtils.parseFloat;
+import static com.android.tools.r8.utils.EncodedValueUtils.parseSigned;
+import static com.android.tools.r8.utils.EncodedValueUtils.parseUnsigned;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InstructionFactory;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMemberAnnotation;
+import com.android.tools.r8.graph.DexMemberAnnotation.DexFieldAnnotation;
+import com.android.tools.r8.graph.DexMemberAnnotation.DexMethodAnnotation;
+import com.android.tools.r8.graph.DexMemberAnnotation.DexParameterAnnotation;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.IntHashMap;
+import com.android.tools.r8.utils.InternalResource;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ShortBuffer;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+public class DexFileReader {
+
+  final int NO_INDEX = -1;
+  private DexFile file;
+  private final Segment[] segments;
+  private int[] stringIDs;
+  private final InternalResource.Kind fileKind;
+
+  public static Segment[] parseMapFrom(Path file) throws IOException {
+    DexFileReader reader =
+        new DexFileReader(
+            new DexFile(file.toString()), InternalResource.Kind.PROGRAM, new DexItemFactory());
+    return reader.parseMap();
+  }
+
+  public void close() {
+    // This close behavior is needed to reduce peak memory usage of D8/R8.
+    indexedItems = null;
+    codes = null;
+    offsetMap = null;
+    file = null;
+    stringIDs = null;
+  }
+
+  // Mapping from indexes to indexable dex items.
+  private OffsetToObjectMapping indexedItems = new OffsetToObjectMapping();
+
+  // Mapping from offset to code item;
+  private IntHashMap<DexCode> codes = new IntHashMap<>();
+
+  // Mapping from offset to dex item;
+  private IntHashMap<Object> offsetMap = new IntHashMap<>();
+
+  // Factory to canonicalize certain dexitems.
+  private final DexItemFactory dexItemFactory;
+
+  public DexFileReader(
+      DexFile file, InternalResource.Kind fileKind, DexItemFactory dexItemFactory) {
+    this.file = file;
+    this.dexItemFactory = dexItemFactory;
+    file.setByteOrder();
+    segments = parseMap();
+    parseStringIDs();
+    this.fileKind = fileKind;
+  }
+
+  public OffsetToObjectMapping getIndexedItemsMap() {
+    return indexedItems;
+  }
+
+  void addCodeItemsTo() {
+    if (fileKind == InternalResource.Kind.LIBRARY) {
+      // Ignore contents of library files.
+      return;
+    }
+    Segment segment = lookupSegment(Constants.TYPE_CODE_ITEM);
+    if (segment.size == 0) {
+      return;
+    }
+    file.position(segment.offset);
+    for (int i = 0; i < segment.size; i++) {
+      file.align(4);  // code items are 4 byte aligned.
+      int offset = file.position();
+      DexCode code = parseCodeItem();
+      codes.put(offset, code);  // Update the file local offset to code mapping.
+    }
+  }
+
+  private DexTypeList parseTypeList() {
+    DexType[] result = new DexType[file.getUint()];
+    for (int j = 0; j < result.length; j++) {
+      result[j] = indexedItems.getType(file.getUshort());
+    }
+    return new DexTypeList(result);
+  }
+
+  private DexTypeList typeListAt(int offset) {
+    if (offset == 0) return DexTypeList.empty();
+    return (DexTypeList) cacheAt(offset, this::parseTypeList);
+  }
+
+  public DexValue parseEncodedValue() {
+    int header = file.get() & 0xff;
+    int valueArg = header >> 5;
+    int valueType = header & 0x1f;
+    switch (valueType) {
+      case DexValue.VALUE_BYTE: {
+        assert valueArg == 0;
+        byte value = (byte) parseSigned(file, 1);
+        return DexValue.DexValueByte.create(value);
+      }
+      case DexValue.VALUE_SHORT: {
+        int size = valueArg + 1;
+        short value = (short) parseSigned(file, size);
+        return DexValue.DexValueShort.create(value);
+      }
+      case DexValue.VALUE_CHAR: {
+        int size = valueArg + 1;
+        char value = (char) parseUnsigned(file, size);
+        return DexValue.DexValueChar.create(value);
+      }
+      case DexValue.VALUE_INT: {
+        int size = valueArg + 1;
+        int value = (int) parseSigned(file, size);
+        return DexValue.DexValueInt.create(value);
+      }
+      case DexValue.VALUE_LONG: {
+        int size = valueArg + 1;
+        long value = parseSigned(file, size);
+        return DexValue.DexValueLong.create(value);
+      }
+      case DexValue.VALUE_FLOAT: {
+        int size = valueArg + 1;
+        return DexValue.DexValueFloat.create(parseFloat(file, size));
+      }
+      case DexValue.VALUE_DOUBLE: {
+        int size = valueArg + 1;
+        return DexValue.DexValueDouble.create(parseDouble(file, size));
+      }
+      case DexValue.VALUE_STRING: {
+        int size = valueArg + 1;
+        int index = (int) parseUnsigned(file, size);
+        DexString value = indexedItems.getString(index);
+        return new DexValue.DexValueString(value);
+      }
+      case DexValue.VALUE_TYPE: {
+        int size = valueArg + 1;
+        DexType value = indexedItems.getType((int) parseUnsigned(file, size));
+        return new DexValue.DexValueType(value);
+      }
+      case DexValue.VALUE_FIELD: {
+        int size = valueArg + 1;
+        DexField value = indexedItems.getField((int) parseUnsigned(file, size));
+        return new DexValue.DexValueField(value);
+      }
+      case DexValue.VALUE_METHOD: {
+        int size = valueArg + 1;
+        DexMethod value = indexedItems.getMethod((int) parseUnsigned(file, size));
+        return new DexValue.DexValueMethod(value);
+      }
+      case DexValue.VALUE_ENUM: {
+        int size = valueArg + 1;
+        DexField value = indexedItems.getField((int) parseUnsigned(file, size));
+        return new DexValue.DexValueEnum(value);
+      }
+      case DexValue.VALUE_ARRAY: {
+        assert valueArg == 0;
+        return new DexValue.DexValueArray(parseEncodedArrayValues());
+      }
+      case DexValue.VALUE_ANNOTATION: {
+        assert valueArg == 0;
+        return new DexValue.DexValueAnnotation(parseEncodedAnnotation());
+      }
+      case DexValue.VALUE_NULL: {
+        assert valueArg == 0;
+        return DexValue.NULL;
+      }
+      case DexValue.VALUE_BOOLEAN: {
+        // 0 is false, and 1 is true.
+        return DexValue.DexValueBoolean.create(valueArg != 0);
+      }
+      case DexValue.VALUE_METHOD_TYPE: {
+        int size = valueArg + 1;
+        DexProto value = indexedItems.getProto((int) parseUnsigned(file, size));
+        return new DexValue.DexValueMethodType(value);
+      }
+      case DexValue.VALUE_METHOD_HANDLE: {
+        int size = valueArg + 1;
+        DexMethodHandle value = indexedItems.getMethodHandle((int) parseUnsigned(file, size));
+        return new DexValue.DexValueMethodHandle(value);
+      }
+      default:
+        throw new IndexOutOfBoundsException();
+    }
+  }
+
+  private DexEncodedAnnotation parseEncodedAnnotation() {
+    int typeIdx = file.getUleb128();
+    int size = file.getUleb128();
+    DexAnnotationElement[] elements = new DexAnnotationElement[size];
+    for (int i = 0; i < size; i++) {
+      int nameIdx = file.getUleb128();
+      DexValue value = parseEncodedValue();
+      elements[i] = new DexAnnotationElement(indexedItems.getString(nameIdx), value);
+    }
+    return new DexEncodedAnnotation(indexedItems.getType(typeIdx), elements);
+  }
+
+  private DexValue[] parseEncodedArrayValues() {
+    int size = file.getUleb128();
+    DexValue[] values = new DexValue[size];
+    for (int i = 0; i < size; i++) {
+      values[i] = parseEncodedValue();
+    }
+    return values;
+  }
+
+
+  private DexEncodedArray parseEncodedArray() {
+    return new DexEncodedArray(parseEncodedArrayValues());
+  }
+
+  private DexEncodedArray encodedArrayAt(int offset) {
+    return (DexEncodedArray) cacheAt(offset, this::parseEncodedArray);
+  }
+
+  private DexFieldAnnotation[] parseFieldAnnotations(int size) {
+    if (size == 0) {
+      return null;
+    }
+    int[] fieldIndices = new int[size];
+    int[] annotationOffsets = new int[size];
+    for (int i = 0; i < size; i++) {
+      fieldIndices[i] = file.getUint();
+      annotationOffsets[i] = file.getUint();
+    }
+    int saved = file.position();
+    DexFieldAnnotation[] result = new DexFieldAnnotation[size];
+    for (int i = 0; i < size; i++) {
+      DexField field = indexedItems.getField(fieldIndices[i]);
+      DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
+      result[i] = new DexFieldAnnotation(field, annotation);
+    }
+    file.position(saved);
+    return result;
+  }
+
+  private DexMethodAnnotation[] parseMethodAnnotations(int size) {
+    if (size == 0) {
+      return null;
+    }
+    int[] methodIndices = new int[size];
+    int[] annotationOffsets = new int[size];
+    for (int i = 0; i < size; i++) {
+      methodIndices[i] = file.getUint();
+      annotationOffsets[i] = file.getUint();
+    }
+    int saved = file.position();
+    DexMethodAnnotation[] result = new DexMethodAnnotation[size];
+    for (int i = 0; i < size; i++) {
+      DexMethod method = indexedItems.getMethod(methodIndices[i]);
+      DexAnnotationSet annotation = annotationSetAt(annotationOffsets[i]);
+      result[i] = new DexMethodAnnotation(method, annotation);
+    }
+    file.position(saved);
+    return result;
+  }
+
+  private DexAnnotationSetRefList annotationSetRefListAt(int offset) {
+    return (DexAnnotationSetRefList) cacheAt(offset, this::parseAnnotationSetRefList);
+  }
+
+  private DexAnnotationSetRefList parseAnnotationSetRefList() {
+    int size = file.getUint();
+    int[] annotationOffsets = new int[size];
+    for (int i = 0; i < size; i++) {
+      annotationOffsets[i] = file.getUint();
+    }
+    DexAnnotationSet[] values = new DexAnnotationSet[size];
+    for (int i = 0; i < size; i++) {
+      values[i] = annotationSetAt(annotationOffsets[i]);
+    }
+    return new DexAnnotationSetRefList(values);
+  }
+
+  private DexParameterAnnotation[] parseParameterAnnotations(int size) {
+    if (size == 0) {
+      return null;
+    }
+    int[] methodIndices = new int[size];
+    int[] annotationOffsets = new int[size];
+    for (int i = 0; i < size; i++) {
+      methodIndices[i] = file.getUint();
+      annotationOffsets[i] = file.getUint();
+    }
+    int saved = file.position();
+    DexParameterAnnotation[] result = new DexParameterAnnotation[size];
+    for (int i = 0; i < size; i++) {
+      DexMethod method = indexedItems.getMethod(methodIndices[i]);
+      result[i] = new DexParameterAnnotation(
+          method,
+          annotationSetRefListAt(annotationOffsets[i]));
+    }
+    file.position(saved);
+    return result;
+  }
+
+  private <T> Object cacheAt(int offset, Supplier<T> function, Supplier<T> defaultValue) {
+    if (offset == 0) {
+      return defaultValue.get();
+    }
+    return cacheAt(offset, function);
+  }
+
+  private <T> Object cacheAt(int offset, Supplier<T> function) {
+    if (offset == 0) return null;  // return null for offset zero.
+    Object result = offsetMap.get(offset);
+    if (result != null) return result;  // return the cached result.
+    // Cache is empty so parse the structure.
+    file.position(offset);
+    result = function.get();
+    // Update the map.
+    offsetMap.put(offset, result);
+    assert offsetMap.get(offset) == result;
+    return result;
+  }
+
+  private DexAnnotation parseAnnotation() {
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Reading Annotation @ 0x%08x.", file.position());
+    }
+    int visibility = file.get();
+    return new DexAnnotation(visibility, parseEncodedAnnotation());
+  }
+
+  private DexAnnotation annotationAt(int offset) {
+    return (DexAnnotation) cacheAt(offset, this::parseAnnotation);
+  }
+
+  private DexAnnotationSet parseAnnotationSet() {
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Reading AnnotationSet @ 0x%08x.", file.position());
+    }
+    int size = file.getUint();
+    int[] annotationOffsets = new int[size];
+    for (int i = 0; i < size; i++) {
+      annotationOffsets[i] = file.getUint();
+    }
+    DexAnnotation[] result = new DexAnnotation[size];
+    for (int i = 0; i < size; i++) {
+      result[i] = annotationAt(annotationOffsets[i]);
+    }
+    return new DexAnnotationSet(result);
+  }
+
+  private DexAnnotationSet annotationSetAt(int offset) {
+    return (DexAnnotationSet) cacheAt(offset, this::parseAnnotationSet, DexAnnotationSet::empty);
+  }
+
+  private AnnotationsDirectory annotationsDirectoryAt(int offset) {
+    return (AnnotationsDirectory) cacheAt(offset, this::parseAnnotationsDirectory,
+        AnnotationsDirectory::empty);
+  }
+
+  private AnnotationsDirectory parseAnnotationsDirectory() {
+    int classAnnotationsOff = file.getUint();
+    int fieldsSize = file.getUint();
+    int methodsSize = file.getUint();
+    int parametersSize = file.getUint();
+    final DexFieldAnnotation[] fields = parseFieldAnnotations(fieldsSize);
+    final DexMethodAnnotation[] methods = parseMethodAnnotations(methodsSize);
+    final DexParameterAnnotation[] parameters = parseParameterAnnotations(parametersSize);
+    return new AnnotationsDirectory(
+        annotationSetAt(classAnnotationsOff),
+        fields,
+        methods,
+        parameters);
+  }
+
+  private DexDebugInfo debugInfoAt(int offset) {
+    return (DexDebugInfo) cacheAt(offset, this::parseDebugInfo);
+  }
+
+  private DexDebugInfo parseDebugInfo() {
+    int start = file.getUleb128();
+    int parametersSize = file.getUleb128();
+    DexString[] parameters = new DexString[parametersSize];
+    for (int i = 0; i < parametersSize; i++) {
+      int index = file.getUleb128p1();
+      if (index != NO_INDEX) {
+        parameters[i] = indexedItems.getString(index);
+      }
+    }
+    List<DexDebugEvent> events = new ArrayList<>();
+    for (int head = file.getUbyte(); head != Constants.DBG_END_SEQUENCE; head = file.getUbyte()) {
+      switch (head) {
+        case Constants.DBG_ADVANCE_PC:
+          events.add(new DexDebugEvent.AdvancePC(file.getUleb128()));
+          break;
+        case Constants.DBG_ADVANCE_LINE:
+          events.add(new DexDebugEvent.AdvanceLine(file.getSleb128()));
+          break;
+        case Constants.DBG_START_LOCAL: {
+          int registerNum = file.getUleb128();
+          int nameIdx = file.getUleb128p1();
+          int typeIdx = file.getUleb128p1();
+          events.add(new DexDebugEvent.StartLocal(
+              registerNum,
+              nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
+              typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
+              null));
+          break;
+        }
+        case Constants.DBG_START_LOCAL_EXTENDED: {
+          int registerNum = file.getUleb128();
+          int nameIdx = file.getUleb128p1();
+          int typeIdx = file.getUleb128p1();
+          int sigIdx = file.getUleb128p1();
+          events.add(new DexDebugEvent.StartLocal(
+              registerNum,
+              nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx),
+              typeIdx == NO_INDEX ? null : indexedItems.getType(typeIdx),
+              sigIdx == NO_INDEX ? null : indexedItems.getString(sigIdx)));
+          break;
+        }
+        case Constants.DBG_END_LOCAL: {
+          events.add(new DexDebugEvent.EndLocal(file.getUleb128()));
+          break;
+        }
+        case Constants.DBG_RESTART_LOCAL: {
+          events.add(new DexDebugEvent.RestartLocal(file.getUleb128()));
+          break;
+        }
+        case Constants.DBG_SET_PROLOGUE_END: {
+          events.add(new DexDebugEvent.SetPrologueEnd());
+          break;
+        }
+        case Constants.DBG_SET_EPILOGUE_BEGIN: {
+          events.add(new SetEpilogueBegin());
+          break;
+        }
+        case Constants.DBG_SET_FILE: {
+          int nameIdx = file.getUleb128p1();
+          DexString sourceFile = nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx);
+          events.add(new DexDebugEvent.SetFile(sourceFile));
+          break;
+        }
+        default: {
+          assert head >= 0x0a && head <= 0xff;
+          events.add(new DexDebugEvent.Default(head));
+        }
+      }
+    }
+    return new DexDebugInfo(start, parameters, events.toArray(new DexDebugEvent[events.size()]));
+  }
+
+  private static class MemberAnnotationIterator<S extends Descriptor<?, S>, T extends DexItem> {
+
+    private int index = 0;
+    private final DexMemberAnnotation<S, T>[] annotations;
+    private final Supplier<T> emptyValue;
+
+    private MemberAnnotationIterator(DexMemberAnnotation<S, T>[] annotations,
+        Supplier<T> emptyValue) {
+      this.annotations = annotations;
+      this.emptyValue = emptyValue;
+    }
+
+    // Get the annotation set for an item. This method assumes that it is always called with
+    // an item that is higher in the sorting order than the last item.
+    T getNextFor(S item) {
+      // TODO(ager): We could use the indices from the file to make this search faster using
+      // compareTo instead of slowCompareTo. That would require us to assign indices during
+      // reading. Those indices should be cleared after reading to make sure that we resort
+      // everything correctly at the end.
+      while (index < annotations.length && annotations[index].item.slowCompareTo(item) < 0) {
+        index++;
+      }
+      if (index >= annotations.length || !annotations[index].item.equals(item)) {
+        return emptyValue.get();
+      }
+      return annotations[index].annotations;
+    }
+  }
+
+  private DexEncodedField[] readFields(int size, DexFieldAnnotation[] annotations,
+      DexValue[] staticValues) {
+    DexEncodedField[] fields = new DexEncodedField[size];
+    int fieldIndex = 0;
+    MemberAnnotationIterator<DexField, DexAnnotationSet> annotationIterator =
+        new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
+    for (int i = 0; i < size; i++) {
+      fieldIndex += file.getUleb128();
+      DexField field = indexedItems.getField(fieldIndex);
+      DexAccessFlags accessFlags = new DexAccessFlags(file.getUleb128());
+      DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
+      DexValue staticValue = null;
+      if (accessFlags.isStatic()) {
+        if (staticValues != null && i < staticValues.length) {
+          staticValue = staticValues[i];
+        } else {
+          staticValue = DexValue.defaultForType(field.type, dexItemFactory);
+        }
+      }
+      fields[i] = new DexEncodedField(field, accessFlags, fieldAnnotations, staticValue);
+    }
+    return fields;
+  }
+
+  private DexEncodedMethod[] readMethods(int size, DexMethodAnnotation[] annotations,
+      DexParameterAnnotation[] parameters, boolean skipCodes) {
+    DexEncodedMethod[] methods = new DexEncodedMethod[size];
+    int methodIndex = 0;
+    MemberAnnotationIterator<DexMethod, DexAnnotationSet> annotationIterator =
+        new MemberAnnotationIterator<>(annotations, DexAnnotationSet::empty);
+    MemberAnnotationIterator<DexMethod, DexAnnotationSetRefList> parameterAnnotationsIterator =
+        new MemberAnnotationIterator<>(parameters, DexAnnotationSetRefList::empty);
+    for (int i = 0; i < size; i++) {
+      methodIndex += file.getUleb128();
+      DexAccessFlags accessFlags = new DexAccessFlags(file.getUleb128());
+      int codeOff = file.getUleb128();
+      DexCode code = null;
+      if (!skipCodes) {
+        assert codeOff == 0 || codes.get(codeOff) != null;
+        code = codes.get(codeOff);
+      }
+      DexMethod method = indexedItems.getMethod(methodIndex);
+      methods[i] = new DexEncodedMethod(method, accessFlags, annotationIterator.getNextFor(method),
+          parameterAnnotationsIterator.getNextFor(method), code);
+    }
+    return methods;
+  }
+
+  void addClassDefsTo(Consumer<DexClass> classCollection) {
+    final Segment segment = lookupSegment(Constants.TYPE_CLASS_DEF_ITEM);
+    final int size = segment.size;
+    indexedItems.initializeClasses(size);
+    if (size == 0) {
+      return;
+    }
+    file.position(segment.offset);
+
+    int[] classIndices = new int[size];
+    int[] accessFlags = new int[size];
+    int[] superclassIndices = new int[size];
+    int[] interfacesOffsets = new int[size];
+    int[] sourceFileIndices = new int[size];
+    int[] annotationsOffsets = new int[size];
+    int[] classDataOffsets = new int[size];
+    int[] staticValuesOffsets = new int[size];
+
+    for (int i = 0; i < size; i++) {
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Reading ClassDef @ 0x%08x.", file.position());
+      }
+      classIndices[i] = file.getUint();
+      accessFlags[i] = file.getUint();
+      superclassIndices[i] = file.getInt();
+      interfacesOffsets[i] = file.getUint();
+      sourceFileIndices[i] = file.getInt();
+      annotationsOffsets[i] = file.getUint();
+      classDataOffsets[i] = file.getUint();
+      staticValuesOffsets[i] = file.getUint();
+    }
+
+    for (int i = 0; i < size; i++) {
+      int superclassIdx = superclassIndices[i];
+      DexType superclass = superclassIdx == NO_INDEX ? null : indexedItems.getType(superclassIdx);
+      int srcIdx = sourceFileIndices[i];
+      DexString source = srcIdx == NO_INDEX ? null : indexedItems.getString(srcIdx);
+      // fix annotations.
+      DexType type = indexedItems.getType(classIndices[i]);
+      DexAccessFlags flags = new DexAccessFlags(accessFlags[i]);
+      DexClass clazz;
+      DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
+      DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
+      DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
+      DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+      AnnotationsDirectory annotationsDirectory = annotationsDirectoryAt(annotationsOffsets[i]);
+      if (classDataOffsets[i] != 0) {
+        DexEncodedArray staticValues = encodedArrayAt(staticValuesOffsets[i]);
+
+        file.position(classDataOffsets[i]);
+        int staticFieldsSize = file.getUleb128();
+        int instanceFieldsSize = file.getUleb128();
+        int directMethodsSize = file.getUleb128();
+        int virtualMethodsSize = file.getUleb128();
+
+        staticFields = readFields(staticFieldsSize, annotationsDirectory.fields,
+            staticValues != null ? staticValues.values : null);
+        instanceFields = readFields(instanceFieldsSize, annotationsDirectory.fields, null);
+        directMethods =
+            readMethods(
+                directMethodsSize,
+                annotationsDirectory.methods,
+                annotationsDirectory.parameters,
+                fileKind != InternalResource.Kind.PROGRAM);
+        virtualMethods =
+            readMethods(
+                virtualMethodsSize,
+                annotationsDirectory.methods,
+                annotationsDirectory.parameters,
+                fileKind != InternalResource.Kind.PROGRAM);
+      }
+      clazz = DexClass.factoryForResourceKind(fileKind).create(
+          type,
+          DexClass.Origin.Dex,
+          flags,
+          superclass,
+          typeListAt(interfacesOffsets[i]),
+          source,
+          annotationsDirectory.clazz,
+          staticFields,
+          instanceFields,
+          directMethods,
+          virtualMethods);
+      classCollection.accept(clazz);  // Update the application object.
+    }
+  }
+
+  private void parseStringIDs() {
+    Segment segment = lookupSegment(Constants.TYPE_STRING_ID_ITEM);
+    stringIDs = new int[segment.size];
+    if (segment.size == 0) {
+      return;
+    }
+    file.position(segment.offset);
+    for (int i = 0; i < segment.size; i++) {
+      stringIDs[i] = file.getUint();
+    }
+  }
+
+  private Segment lookupSegment(int type) {
+    for (Segment s : segments) {
+      if (s.type == type) {
+        return s;
+      }
+    }
+    // If the segment doesn't exist, return an empty segment of this type.
+    return new Segment(type, 0, 0, 0);
+  }
+
+  private Segment[] parseMap() {
+    // Read the segments information from the MAP.
+    int mapOffset = file.getUint(Constants.MAP_OFF_OFFSET);
+    file.position(mapOffset);
+    int mapSize = file.getUint();
+    final Segment[] result = new Segment[mapSize];
+    for (int i = 0; i < mapSize; i++) {
+      int type = file.getUshort();
+      int unused = file.getUshort();
+      int size = file.getUint();
+      int offset = file.getUint();
+      result[i] = new Segment(type, unused, size, offset);
+    }
+    if (Log.ENABLED) {
+      for (int i = 0; i < result.length; i++) {
+        Segment segment = result[i];
+        int nextOffset = i < result.length - 1 ? result[i + 1].offset : segment.offset;
+        Log.debug(this.getClass(), "Read segment 0x%04x @ 0x%08x #items %08d size 0x%08x.",
+            segment.type, segment.offset, segment.size, nextOffset - segment.offset);
+      }
+    }
+    for (int i = 0; i < mapSize - 1; i++) {
+      result[i].setEnd(result[i + 1].offset);
+    }
+    result[mapSize - 1].setEnd(file.end());
+    return result;
+  }
+
+  private DexCode parseCodeItem() {
+    int registerSize = file.getUshort();
+    int insSize = file.getUshort();
+    int outsSize = file.getUshort();
+    int triesSize = file.getUshort();
+    int debugInfoOff = file.getUint();
+    int insnsSize = file.getUint();
+    short[] code = new short[insnsSize];
+    Try[] tries = new Try[triesSize];
+    DexCode.TryHandler[] handlers = null;
+
+    if (insnsSize != 0) {
+      for (int i = 0; i < insnsSize; i++) {
+        code[i] = file.getShort();
+      }
+      if (insnsSize % 2 != 0) {
+        file.getUshort();  // Skip padding ushort
+      }
+      if (triesSize > 0) {
+        Hashtable<Integer, Integer> handlerMap = new Hashtable<>();
+        // tries: try_item[tries_size].
+        for (int i = 0; i < triesSize; i++) {
+          int startAddr = file.getUint();
+          int insnCount = file.getUshort();
+          int handlerOff = file.getUshort();
+          tries[i] = new Try(startAddr, insnCount, handlerOff);
+        }
+        // handlers: encoded_catch_handler_list
+        int encodedCatchHandlerListPosition = file.position();
+        // - size: uleb128
+        int size = file.getUleb128();
+        handlers = new TryHandler[size];
+        // - list: encoded_catch_handler[handlers_size]
+        for (int i = 0; i < size; i++) {
+          // encoded_catch_handler
+          int encodedCatchHandlerOffset = file.position() - encodedCatchHandlerListPosition;
+          handlerMap.put(encodedCatchHandlerOffset, i);
+          // - size:	sleb128
+          int hsize = file.getSleb128();
+          int realHsize = Math.abs(hsize);
+          // - handlers	encoded_type_addr_pair[abs(size)]
+          TryHandler.TypeAddrPair pairs[] = new TryHandler.TypeAddrPair[realHsize];
+          for (int j = 0; j < realHsize; j++) {
+            int typeIdx = file.getUleb128();
+            int addr = file.getUleb128();
+            pairs[j] = new TypeAddrPair(indexedItems.getType(typeIdx), addr,
+                encodedCatchHandlerOffset);
+          }
+          int catchAllAddr = -1;
+          if (hsize <= 0) {
+            catchAllAddr = file.getUleb128();
+          }
+          handlers[i] = new TryHandler(pairs, catchAllAddr);
+        }
+        // Convert the handler offsets inside the Try objects to indexes.
+        for (Try t : tries) {
+          t.setHandlerIndex(handlerMap);
+        }
+      }
+    }
+    // Store and restore offset information around reading debug info.
+    int saved = file.position();
+    DexDebugInfo debugInfo = debugInfoAt(debugInfoOff);
+    file.position(saved);
+    InstructionFactory factory = new InstructionFactory();
+    Instruction[] instructions =
+        factory.readSequenceFrom(ShortBuffer.wrap(code), 0, code.length, indexedItems);
+    return new DexCode(
+        registerSize,
+        insSize,
+        outsSize,
+        instructions,
+        tries,
+        handlers,
+        debugInfo,
+        factory.getHighestSortingString());
+  }
+
+  static void populateIndexTables(DexFileReader fileReader) {
+    // Populate structures that are already sorted upon read.
+    DexFileReader.populateStrings(fileReader);  // Depends on nothing.
+    DexFileReader.populateTypes(fileReader);  // Depends on Strings.
+    DexFileReader.populateFields(fileReader);  // Depends on Types, and Strings.
+    DexFileReader.populateProtos(fileReader);  // Depends on Types and Strings.
+    DexFileReader.populateMethods(fileReader);  // Depends on Protos, Types, and Strings.
+    DexFileReader.populateMethodHandles(fileReader); // Depends on Methods and Fields
+    DexFileReader.populateCallSites(fileReader); // Depends on MethodHandles
+  }
+
+  private static void populateStrings(DexFileReader reader) {
+    reader.indexedItems.initializeStrings(reader.stringIDs.length);
+    for (int i = 0; i < reader.stringIDs.length; i++) {
+      reader.indexedItems.setString(i, reader.stringAt(i));
+    }
+  }
+
+  private static void populateMethodHandles(DexFileReader reader) {
+    Segment segment = reader.lookupSegment(Constants.TYPE_METHOD_HANDLE_ITEM);
+    reader.indexedItems.initializeMethodHandles(segment.size);
+    for (int i = 0; i < segment.size; i++){
+      reader.indexedItems.setMethodHandle(i, reader.methodHandleAt(i));
+    }
+  }
+
+  private static void populateCallSites(DexFileReader reader) {
+    Segment segment = reader.lookupSegment(Constants.TYPE_CALL_SITE_ID_ITEM);
+    reader.indexedItems.initializeCallSites(segment.size);
+    for (int i = 0; i < segment.size; i++){
+      reader.indexedItems.setCallSites(i, reader.callSiteAt(i));
+    }
+  }
+
+  private static void populateTypes(DexFileReader reader) {
+    Segment segment = reader.lookupSegment(Constants.TYPE_TYPE_ID_ITEM);
+    reader.indexedItems.initializeTypes(segment.size);
+    for (int i = 0; i < segment.size; i++){
+      reader.indexedItems.setType(i, reader.typeAt(i));
+    }
+  }
+
+  private static void populateFields(DexFileReader reader) {
+    Segment segment = reader.lookupSegment(Constants.TYPE_FIELD_ID_ITEM);
+    reader.indexedItems.initializeFields(segment.size);
+    for (int i = 0; i < segment.size; i++){
+      reader.indexedItems.setField(i, reader.fieldAt(i));
+    }
+  }
+
+  private static void populateProtos(DexFileReader reader) {
+    Segment segment = reader.lookupSegment(Constants.TYPE_PROTO_ID_ITEM);
+    reader.indexedItems.initializeProtos(segment.size);
+    for (int i = 0; i < segment.size; i++){
+      reader.indexedItems.setProto(i, reader.protoAt(i));
+    }
+  }
+
+  private static void populateMethods(DexFileReader reader) {
+    Segment segment = reader.lookupSegment(Constants.TYPE_METHOD_ID_ITEM);
+    reader.indexedItems.initializeMethods(segment.size);
+    for (int i = 0; i < segment.size; i++){
+      reader.indexedItems.setMethod(i, reader.methodAt(i));
+    }
+  }
+
+  private DexString stringAt(int index) {
+    final int offset = stringIDs[index];
+    file.position(offset);
+    int size = file.getUleb128();
+    ByteArrayOutputStream os = new ByteArrayOutputStream();
+    byte read;
+    do {
+      read = file.get();
+      os.write(read);
+    } while (read != 0);
+    return dexItemFactory.createString(size, os.toByteArray());
+  }
+
+  private DexType typeAt(int index) {
+    Segment segment = lookupSegment(Constants.TYPE_TYPE_ID_ITEM);
+    if (index >= segment.size) {
+      return null;
+    }
+    int offset = segment.offset + (Constants.TYPE_TYPE_ID_ITEM_SIZE * index);
+    int stringIndex = file.getUint(offset);
+    return dexItemFactory.createType(indexedItems.getString(stringIndex));
+  }
+
+  private DexField fieldAt(int index) {
+    Segment segment = lookupSegment(Constants.TYPE_FIELD_ID_ITEM);
+    if (index >= segment.size) {
+      return null;
+    }
+    int offset = segment.offset + (Constants.TYPE_FIELD_ID_ITEM_SIZE * index);
+    file.position(offset);
+    int classIndex = file.getUshort();
+    int typeIndex = file.getUshort();
+    int nameIndex = file.getUint();
+    DexType clazz = indexedItems.getType(classIndex);
+    DexType type = indexedItems.getType(typeIndex);
+    DexString name = indexedItems.getString(nameIndex);
+    return dexItemFactory.createField(clazz, type, name);
+  }
+
+  private DexMethodHandle methodHandleAt(int index) {
+    Segment segment = lookupSegment(Constants.TYPE_METHOD_HANDLE_ITEM);
+    if (index >= segment.size) {
+      return null;
+    }
+    int offset = segment.offset + (Constants.TYPE_METHOD_HANDLE_ITEM_SIZE * index);
+    file.position(offset);
+    MethodHandleType type = MethodHandleType.getKind(file.getUshort());
+    file.getUshort(); // unused
+    int indexFieldOrMethod = file.getUshort();
+    Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod;
+    switch (type) {
+      case INSTANCE_GET:
+      case INSTANCE_PUT:
+      case STATIC_GET:
+      case STATIC_PUT: {
+        fieldOrMethod = indexedItems.getField(indexFieldOrMethod);
+        break;
+      }
+      case INVOKE_INSTANCE:
+      case INVOKE_STATIC: {
+        fieldOrMethod = indexedItems.getMethod(indexFieldOrMethod);
+        break;
+      }
+      default:
+        throw new AssertionError("Method handle type unsupported in a dex file.");
+    }
+    file.getUshort(); // unused
+
+    return dexItemFactory.createMethodHandle(type, fieldOrMethod);
+  }
+
+  private DexCallSite callSiteAt(int index) {
+    Segment segment = lookupSegment(Constants.TYPE_CALL_SITE_ID_ITEM);
+    if (index >= segment.size) {
+      return null;
+    }
+    int callSiteOffset =
+        file.getUint(segment.offset + (Constants.TYPE_CALL_SITE_ID_ITEM_SIZE * index));
+    DexEncodedArray callSiteEncodedArray = encodedArrayAt(callSiteOffset);
+    DexValue[] values = callSiteEncodedArray.values;
+    assert values[0] instanceof DexValueMethodHandle;
+    assert values[1] instanceof DexValueString;
+    assert values[2] instanceof DexValueMethodType;
+
+    return dexItemFactory.createCallSite(
+        ((DexValueString) values[1]).value,
+        ((DexValueMethodType) values[2]).value,
+        ((DexValueMethodHandle) values[0]).value,
+        // 3 means first extra args
+        Arrays.asList(Arrays.copyOfRange(values, 3, values.length)));
+  }
+
+  private DexProto protoAt(int index) {
+    Segment segment = lookupSegment(Constants.TYPE_PROTO_ID_ITEM);
+    if (index >= segment.size) {
+      return null;
+    }
+    int offset = segment.offset + (Constants.TYPE_PROTO_ID_ITEM_SIZE * index);
+    file.position(offset);
+    int shortyIndex = file.getUint();
+    int returnTypeIndex = file.getUint();
+    int parametersOffsetIndex = file.getUint();
+    DexString shorty = indexedItems.getString(shortyIndex);
+    DexType returnType = indexedItems.getType(returnTypeIndex);
+    DexTypeList parameters = typeListAt(parametersOffsetIndex);
+    return dexItemFactory.createProto(shorty, returnType, parameters);
+  }
+
+  private DexMethod methodAt(int index) {
+    Segment segment = lookupSegment(Constants.TYPE_METHOD_ID_ITEM);
+    if (index >= segment.size) {
+      return null;
+    }
+    int offset = segment.offset + (Constants.TYPE_METHOD_ID_ITEM_SIZE * index);
+    file.position(offset);
+    int classIndex = file.getUshort();
+    int protoIndex = file.getUshort();
+    int nameIndex = file.getUint();
+    return dexItemFactory.createMethod(
+        indexedItems.getType(classIndex),
+        indexedItems.getProto(protoIndex),
+        indexedItems.getString(nameIndex));
+  }
+
+  private static class AnnotationsDirectory {
+
+    private static final DexParameterAnnotation[] NO_PARAMETER_ANNOTATIONS =
+        new DexParameterAnnotation[0];
+
+    private static final DexFieldAnnotation[] NO_FIELD_ANNOTATIONS =
+        new DexFieldAnnotation[0];
+
+    private static final DexMethodAnnotation[] NO_METHOD_ANNOTATIONS =
+        new DexMethodAnnotation[0];
+
+    private static final AnnotationsDirectory THE_EMPTY_ANNOTATIONS_DIRECTORY =
+        new AnnotationsDirectory(DexAnnotationSet.empty(),
+            NO_FIELD_ANNOTATIONS, new DexMethodAnnotation[0],
+            NO_PARAMETER_ANNOTATIONS);
+
+    public final DexAnnotationSet clazz;
+    public final DexFieldAnnotation[] fields;
+    public final DexMethodAnnotation[] methods;
+    public final DexParameterAnnotation[] parameters;
+
+    AnnotationsDirectory(DexAnnotationSet clazz,
+        DexFieldAnnotation[] fields,
+        DexMethodAnnotation[] methods,
+        DexParameterAnnotation[] parameters) {
+      this.clazz = clazz == null ? DexAnnotationSet.empty() : clazz;
+      this.fields = fields == null ? NO_FIELD_ANNOTATIONS : fields;
+      this.methods = methods == null ? NO_METHOD_ANNOTATIONS : methods;
+      this.parameters = parameters == null ? NO_PARAMETER_ANNOTATIONS : parameters;
+    }
+
+    public static AnnotationsDirectory empty() {
+      return THE_EMPTY_ANNOTATIONS_DIRECTORY;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
new file mode 100644
index 0000000..fac5e53
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.EncodedValueUtils;
+import com.android.tools.r8.utils.LebUtils;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+
+/**
+ * Provides an abstraction around a {@link ByteBuffer} with write operations for
+ * additional DEX specific formats, like Leb128.
+ */
+public class DexOutputBuffer {
+  private static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+
+  private ByteBuffer byteBuffer;
+
+  public DexOutputBuffer() {
+    byteBuffer = allocate(DEFAULT_BUFFER_SIZE);
+  }
+
+  private void ensureSpaceFor(int bytes) {
+    if (byteBuffer.remaining() < bytes) {
+      int newSize = byteBuffer.capacity() + Math.max(byteBuffer.capacity(), bytes * 2);
+      ByteBuffer newBuffer = allocate(newSize);
+      System.arraycopy(byteBuffer.array(), 0, newBuffer.array(), 0, byteBuffer.position());
+      newBuffer.position(byteBuffer.position());
+      byteBuffer = newBuffer;
+    }
+  }
+
+  private ByteBuffer allocate(int size) {
+    ByteBuffer buffer = ByteBuffer.allocate(size);
+    buffer.order(ByteOrder.LITTLE_ENDIAN);
+    return buffer;
+  }
+
+  public void putUleb128(int value) {
+    LebUtils.putUleb128(this, value);
+  }
+
+  public void putSleb128(int value) {
+    LebUtils.putSleb128(this, value);
+  }
+
+  public int putSignedEncodedValue(long value, int expectedSize) {
+    return EncodedValueUtils.putSigned(this, value, expectedSize);
+  }
+
+  public int putUnsignedEncodedValue(long value, int expectedSize) {
+    return EncodedValueUtils.putUnsigned(this, value, expectedSize);
+  }
+
+  public void putInstructions(Instruction[] insns, ObjectToOffsetMapping mapping) {
+    int size = 0;
+    for (Instruction insn : insns) {
+      size += insn.getSize();
+    }
+    ensureSpaceFor(size * Short.BYTES);
+    assert byteBuffer.position() % 2 == 0;
+    ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
+    for (int i = 0; i < insns.length; i++) {
+      insns[i].write(shortBuffer, mapping);
+    }
+    byteBuffer.position(byteBuffer.position() + shortBuffer.position() * Short.BYTES);
+  }
+
+  public void putByte(byte aByte) {
+    ensureSpaceFor(Byte.BYTES);
+    byteBuffer.put(aByte);
+  }
+
+  public void putBytes(byte[] bytes) {
+    ensureSpaceFor(bytes.length);
+    byteBuffer.put(bytes);
+  }
+
+  public void putShort(short aShort) {
+    ensureSpaceFor(Short.BYTES);
+    byteBuffer.putShort(aShort);
+  }
+
+  public void putInt(int anInteger) {
+    ensureSpaceFor(Integer.BYTES);
+    byteBuffer.putInt(anInteger);
+  }
+
+  /**
+   * Moves the position in the bytebuffer forward until it is aligned.
+   *
+   * @param bytes  alignment requirement in bytes
+   * @return       the new position after alignment
+   */
+  public int align(int bytes) {
+    assert bytes > 0;
+    int mask = bytes - 1;
+    int newPosition = (byteBuffer.position() + mask) & ~mask;
+    ensureSpaceFor(newPosition - position());
+    byteBuffer.position(newPosition);
+    return newPosition;
+  }
+
+  public int position() {
+    return byteBuffer.position();
+  }
+
+  public void forward(int bytes) {
+    ensureSpaceFor(bytes);
+    byteBuffer.position(byteBuffer.position() + bytes);
+  }
+
+  public void rewind(int bytes) {
+    forward(-bytes);
+  }
+
+  public void moveTo(int position) {
+    ensureSpaceFor(position - byteBuffer.position());
+    byteBuffer.position(position);
+  }
+
+  public boolean isAligned(int bytes) {
+    return position() % bytes == 0;
+  }
+
+  public byte[] asArray() {
+    return byteBuffer.array();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
new file mode 100644
index 0000000..33cea6d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -0,0 +1,1345 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.utils.LebUtils.sizeAsUleb128;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramClassVisitor;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LebUtils;
+import com.android.tools.r8.utils.OrderedMergingIterator;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+import java.util.zip.Adler32;
+
+public class FileWriter {
+
+  private final ObjectToOffsetMapping mapping;
+  private final DexApplication application;
+  private final AppInfo appInfo;
+  private final InternalOptions options;
+  private final NamingLens namingLens;
+  private final DexOutputBuffer dest = new DexOutputBuffer();
+  private final MixedSectionOffsets mixedSectionOffsets;
+  private int numberOfAnnotationDirectories;
+
+  public FileWriter(
+      ObjectToOffsetMapping mapping,
+      DexApplication application,
+      AppInfo appinfo,
+      InternalOptions options,
+      NamingLens namingLens) {
+    this.mapping = mapping;
+    this.application = application;
+    this.appInfo = appinfo;
+    this.options = options;
+    this.namingLens = namingLens;
+    this.mixedSectionOffsets = new MixedSectionOffsets();
+  }
+
+  public static void writeEncodedAnnotation(DexEncodedAnnotation annotation, DexOutputBuffer dest,
+      ObjectToOffsetMapping mapping) {
+    if (Log.ENABLED) {
+      Log.verbose(FileWriter.class, "Writing encoded annotation @ %08x", dest.position());
+    }
+    dest.putUleb128(mapping.getOffsetFor(annotation.type));
+    dest.putUleb128(annotation.elements.length);
+    assert isSorted(annotation.elements, (element) -> element.name);
+    for (DexAnnotationElement element : annotation.elements) {
+      dest.putUleb128(mapping.getOffsetFor(element.name));
+      element.value.writeTo(dest, mapping);
+    }
+  }
+
+  private static <T extends PresortedComparable<T>> boolean isSorted(KeyedDexItem<T>[] items) {
+    return isSorted(items, KeyedDexItem::getKey);
+  }
+
+  private static <S, T extends Comparable<T>> boolean isSorted(S[] items, Function<S, T> getter) {
+    T current = null;
+    for (S item : items) {
+      T next = getter.apply(item);
+      if (current != null && current.compareTo(next) >= 0) {
+        return false;
+      }
+      current = next;
+    }
+    return true;
+  }
+
+  public FileWriter collect() {
+    // Use the class array from the mapping, as it has a deterministic iteration order.
+    new ProgramClassDependencyCollector(application, mapping.getClasses()).run(mapping.getClasses());
+
+    // Sort the class members.
+    // Needed before adding static-value arrays and writing annotation directories and classes.
+    sortClassData(mixedSectionOffsets.getClassesWithData());
+
+    // Add the static values for all fields now that we have committed to their sorting.
+    mixedSectionOffsets.getClassesWithData().forEach(this::addStaticFieldValues);
+
+    // String data is not tracked by the MixedSectionCollection.new AppInfo(application, null)
+    assert mixedSectionOffsets.stringData.size() == 0;
+    for (DexString string : mapping.getStrings()) {
+      mixedSectionOffsets.add(string);
+    }
+    // Neither are the typelists in protos...
+    for (DexProto proto : mapping.getProtos()) {
+      mixedSectionOffsets.add(proto.parameters);
+    }
+
+    DexItem.collectAll(mixedSectionOffsets, mapping.getCallSites());
+
+    return this;
+  }
+
+  private void rewriteCodeWithJumboStrings(IRConverter converter, DexEncodedMethod[] methods) {
+    for (int i = 0; i < methods.length; i++) {
+      DexEncodedMethod method = methods[i];
+      if (method.getCode() == null) {
+        continue;
+      }
+      DexCode code = method.getCode().asDexCode();
+      if (code.highestSortingString != null) {
+        if (mapping.getOffsetFor(code.highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) {
+          converter.processJumboStrings(method, mapping.getFirstJumboString());
+        }
+      }
+    }
+  }
+
+  public FileWriter rewriteCodeWithJumboStrings(List<DexProgramClass> classes) {
+    // If there are no strings with jumbo indices at all this is a no-op.
+    if (!mapping.hasJumboStrings()) {
+      return this;
+    }
+    // If the globally highest sorting string is not a jumbo string this is also a no-op.
+    if (application.highestSortingString != null &&
+        application.highestSortingString.slowCompareTo(mapping.getFirstJumboString()) < 0) {
+      return this;
+    }
+    // At least one method needs a jumbo string.
+    IRConverter converter = new IRConverter(application, appInfo, options, null, false);
+    for (DexProgramClass clazz : classes) {
+      rewriteCodeWithJumboStrings(converter, clazz.directMethods());
+      rewriteCodeWithJumboStrings(converter, clazz.virtualMethods());
+    }
+    return this;
+  }
+
+  public byte[] generate() {
+    // Check restrictions on interface methods.
+    checkInterfaceMethods();
+
+    Layout layout = Layout.from(mapping);
+    layout.setCodesOffset(layout.dataSectionOffset);
+
+    // Sort the codes first, as their order might impact size due to alignment constraints.
+    List<DexCode> codes = sortDexCodesByClassName(mixedSectionOffsets.getCodes(), application);
+
+    // Output the debug_info_items first, as they have no dependencies.
+    dest.moveTo(layout.getCodesOffset() + sizeOfCodeItems(codes));
+    writeItems(mixedSectionOffsets.getDebugInfos(), layout::setDebugInfosOffset,
+        this::writeDebugItem);
+
+    // Remember the typelist offset for later.
+    layout.setTypeListsOffset(dest.align(4));  // type_list are aligned.
+
+    // Now output the code.
+    dest.moveTo(layout.getCodesOffset());
+    assert dest.isAligned(4);
+    writeItems(codes, layout::alreadySetOffset, this::writeCodeItem, 4);
+    assert layout.getDebugInfosOffset() == 0 || dest.position() == layout.getDebugInfosOffset();
+
+    // Now the type lists and rest.
+    dest.moveTo(layout.getTypeListsOffset());
+    writeItems(mixedSectionOffsets.getTypeLists(), layout::alreadySetOffset, this::writeTypeList);
+    writeItems(mixedSectionOffsets.getStringData(), layout::setStringDataOffsets,
+        this::writeStringData);
+    writeItems(mixedSectionOffsets.getAnnotations(), layout::setAnnotationsOffset,
+        this::writeAnnotation);
+    writeItems(mixedSectionOffsets.getAnnotationSets(), layout::setAnnotationSetsOffset,
+        this::writeAnnotationSet, 4);
+    writeItems(mixedSectionOffsets.getAnnotationSetRefLists(),
+        layout::setAnnotationSetRefListsOffset, this::writeAnnotationSetRefList, 4);
+    // Write the annotation directories.
+    writeItems(Arrays.asList(mapping.getClasses()), layout::setAnnotationDirectoriesOffset,
+        this::writeAnnotationDirectoryForClass, 4);
+    // Write the rest.
+    writeItems(mixedSectionOffsets.getClassesWithData(), layout::setClassDataOffset,
+        this::writeClassData);
+    writeItems(mixedSectionOffsets.getEncodedArrays(), layout::setEncodedArrarysOffset,
+        this::writeEncodedArray);
+
+    // Add the map at the end
+    layout.setMapOffset(dest.align(4));
+    writeMap(layout);
+    layout.setEndOfFile(dest.position());
+
+    // Now that we have all mixedSectionOffsets, lets write the indexed items.
+    dest.moveTo(Constants.HEADER_SIZE);
+    writeFixedSectionItems(mapping.getStrings(), layout.stringIdsOffset, this::writeStringItem);
+    writeFixedSectionItems(mapping.getTypes(), layout.typeIdsOffset, this::writeTypeItem);
+    writeFixedSectionItems(mapping.getProtos(), layout.protoIdsOffset, this::writeProtoItem);
+    writeFixedSectionItems(mapping.getFields(), layout.fieldIdsOffset, this::writeFieldItem);
+    writeFixedSectionItems(mapping.getMethods(), layout.methodIdsOffset, this::writeMethodItem);
+    writeFixedSectionItems(mapping.getClasses(), layout.classDefsOffset, this::writeClassDefItem);
+    writeFixedSectionItems(mapping.getCallSites(), layout.callSiteIdsOffset, this::writeCallSite);
+    writeFixedSectionItems(
+        mapping.getMethodHandles(), layout.methodHandleIdsOffset, this::writeMethodHandle);
+
+    // Fill in the header information.
+    writeHeader(layout);
+    writeSignature(layout);
+    writeChecksum(layout);
+
+    // Turn into an array
+    return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
+  }
+
+  private void sortClassData(List<DexProgramClass> classesWithData) {
+    for (DexProgramClass clazz : classesWithData) {
+      sortEncodedFields(clazz.instanceFields);
+      sortEncodedFields(clazz.staticFields);
+      sortEncodedMethods(clazz.directMethods);
+      sortEncodedMethods(clazz.virtualMethods);
+    }
+  }
+
+  private void sortEncodedFields(DexEncodedField[] fields) {
+    Arrays.sort(fields, (DexEncodedField a, DexEncodedField b) -> a.field.compareTo(b.field));
+  }
+
+  private void sortEncodedMethods(DexEncodedMethod[] methods) {
+    Arrays.sort(methods, (DexEncodedMethod a, DexEncodedMethod b) -> a.method.compareTo(b.method));
+  }
+
+  private void checkInterfaceMethods() {
+    for (DexProgramClass clazz : mapping.getClasses()) {
+      if (clazz.isInterface()) {
+        checkInterfaceMethods(clazz.directMethods());
+        checkInterfaceMethods(clazz.virtualMethods());
+      }
+    }
+  }
+
+  // Ensures interface methods comply with requirements imposed by Android runtime:
+  //  -- in pre-N Android versions interfaces may only have class
+  //     initializer and public abstract methods.
+  //  -- starting with N interfaces may also have public or private
+  //     static methods, as well as public non-abstract (default)
+  //     and private instance methods.
+  private void checkInterfaceMethods(DexEncodedMethod[] methods) {
+    for (DexEncodedMethod method : methods) {
+      if (application.dexItemFactory.isClassConstructor(method.method)) {
+        continue; // Class constructor is always OK.
+      }
+      if (method.accessFlags.isStatic()) {
+        if (!options.canUseDefaultAndStaticInterfaceMethods()) {
+          throw new CompilationError("Static interface methods are only supported "
+              + "starting with Android N (--min-sdk-version " + Constants.ANDROID_N_API + "): "
+              + method.method.toSourceString());
+        }
+
+      } else {
+        if (method.accessFlags.isConstructor()) {
+          throw new CompilationError(
+              "Interface must not have constructors: " + method.method.toSourceString());
+        }
+        if (!method.accessFlags.isAbstract() && !method.accessFlags.isPrivate() &&
+            !options.canUseDefaultAndStaticInterfaceMethods()) {
+          throw new CompilationError("Default interface methods are only supported "
+              + "starting with Android N (--min-sdk-version " + Constants.ANDROID_N_API + "): "
+              + method.method.toSourceString());
+        }
+      }
+
+      if (method.accessFlags.isPrivate()) {
+        if (options.canUsePrivateInterfaceMethods()) {
+          continue;
+        }
+        throw new CompilationError("Private interface methods are only supported "
+            + "starting with Android N (--min-sdk-version " + Constants.ANDROID_N_API + "): "
+            + method.method.toSourceString());
+      }
+
+      if (!method.accessFlags.isPublic()) {
+        throw new CompilationError("Interface methods must not be "
+            + "protected or package private: " + method.method.toSourceString());
+      }
+    }
+  }
+
+  private List<DexCode> sortDexCodesByClassName(List<DexCode> codes, DexApplication application) {
+    Map<DexCode, String> codeToSignatureMap = new IdentityHashMap<>();
+    for (DexProgramClass clazz : mapping.getClasses()) {
+      addSignaturesFromMethods(clazz.directMethods(), codeToSignatureMap,
+          application.getProguardMap());
+      addSignaturesFromMethods(clazz.virtualMethods(), codeToSignatureMap,
+          application.getProguardMap());
+    }
+    DexCode[] codesArray = codes.toArray(new DexCode[codes.size()]);
+    Arrays.sort(codesArray, Comparator.comparing(codeToSignatureMap::get));
+    return Arrays.asList(codesArray);
+  }
+
+  private static void addSignaturesFromMethods(DexEncodedMethod[] methods,
+      Map<DexCode, String> codeToSignatureMap,
+      ClassNameMapper proguardMap) {
+    for (DexEncodedMethod method : methods) {
+      if (method.getCode() == null) {
+        assert method.accessFlags.isAbstract() || method.accessFlags.isNative();
+      } else {
+        Signature signature;
+        String originalClassName;
+        if (proguardMap != null) {
+          signature = proguardMap.originalSignatureOf(method.method);
+          originalClassName = proguardMap.originalNameOf(method.method.holder);
+        } else {
+          signature = MethodSignature.fromDexMethod(method.method);
+          originalClassName = method.method.holder.toSourceString();
+        }
+        codeToSignatureMap.put(method.getCode().asDexCode(), originalClassName + signature);
+      }
+    }
+  }
+
+  private <T extends DexItem> void writeFixedSectionItems(T[] items, int offset,
+      Consumer<T> writer) {
+    assert dest.position() == offset;
+    for (T item : items) {
+      writer.accept(item);
+    }
+  }
+
+  private <T extends DexItem> void writeItems(List<T> items, Consumer<Integer> offsetSetter,
+      Consumer<T> writer) {
+    writeItems(items, offsetSetter, writer, 1);
+  }
+
+  private <T extends DexItem> void writeItems(List<T> items, Consumer<Integer> offsetSetter,
+      Consumer<T> writer, int alignment) {
+    if (items.isEmpty()) {
+      offsetSetter.accept(0);
+    } else {
+      offsetSetter.accept(dest.align(alignment));
+      items.forEach(writer);
+    }
+  }
+
+  private int sizeOfCodeItems(Iterable<DexCode> codes) {
+    int size = 0;
+    for (DexCode code : codes) {
+      size = alignSize(4, size);
+      size += sizeOfCodeItem(code);
+    }
+    return size;
+  }
+
+  private int sizeOfCodeItem(DexCode code) {
+    int result = 16;
+    int insnSize = 0;
+    for (Instruction insn : code.instructions) {
+      insnSize += insn.getSize();
+    }
+    result += insnSize * 2;
+    result += code.tries.length * 8;
+    if ((code.handlers != null) && (code.handlers.length > 0)) {
+      result = alignSize(4, result);
+      result += LebUtils.sizeAsUleb128(code.handlers.length);
+      for (TryHandler handler : code.handlers) {
+        boolean hasCatchAll = handler.catchAllAddr != TryHandler.NO_HANDLER;
+        result += LebUtils
+            .sizeAsSleb128(hasCatchAll ? -handler.pairs.length : handler.pairs.length);
+        for (TypeAddrPair pair : handler.pairs) {
+
+          result += sizeAsUleb128(mapping.getOffsetFor(pair.type));
+          result += sizeAsUleb128(pair.addr);
+        }
+        if (hasCatchAll) {
+          result += sizeAsUleb128(handler.catchAllAddr);
+        }
+      }
+    }
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Computed size item %08d.", result);
+    }
+    return result;
+  }
+
+  private void writeStringItem(DexString string) {
+    dest.putInt(mixedSectionOffsets.getOffsetFor(string));
+  }
+
+  private void writeTypeItem(DexType type) {
+    DexString descriptor = namingLens.lookupDescriptor(type);
+    dest.putInt(mapping.getOffsetFor(descriptor));
+  }
+
+  private void writeProtoItem(DexProto proto) {
+    dest.putInt(mapping.getOffsetFor(proto.shorty));
+    dest.putInt(mapping.getOffsetFor(proto.returnType));
+    dest.putInt(mixedSectionOffsets.getOffsetFor(proto.parameters));
+  }
+
+  private void writeFieldItem(DexField field) {
+    int classIdx = mapping.getOffsetFor(field.clazz);
+    assert (short) classIdx == classIdx;
+    dest.putShort((short) classIdx);
+    int typeIdx = mapping.getOffsetFor(field.type);
+    assert (short) typeIdx == typeIdx;
+    dest.putShort((short) typeIdx);
+    DexString name = namingLens.lookupName(field);
+    dest.putInt(mapping.getOffsetFor(name));
+  }
+
+  private void writeMethodItem(DexMethod method) {
+    int classIdx = mapping.getOffsetFor(method.holder);
+    assert (short) classIdx == classIdx;
+    dest.putShort((short) classIdx);
+    int protoIdx = mapping.getOffsetFor(method.proto);
+    assert (short) protoIdx == protoIdx;
+    dest.putShort((short) protoIdx);
+    DexString name = namingLens.lookupName(method);
+    dest.putInt(mapping.getOffsetFor(name));
+  }
+
+  private void writeClassDefItem(DexProgramClass clazz) {
+    dest.putInt(mapping.getOffsetFor(clazz.type));
+    dest.putInt(clazz.accessFlags.get());
+    dest.putInt(
+        clazz.superType == null ? Constants.NO_INDEX : mapping.getOffsetFor(clazz.superType));
+    dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.interfaces));
+    dest.putInt(
+        clazz.sourceFile == null ? Constants.NO_INDEX : mapping.getOffsetFor(clazz.sourceFile));
+    dest.putInt(mixedSectionOffsets.getOffsetForAnnotationsDirectory(clazz));
+    dest.putInt(clazz.hasMethodsOrFields() ? mixedSectionOffsets.getOffsetFor(clazz) : Constants.NO_OFFSET);
+    dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.getStaticValues()));
+  }
+
+  private void writeDebugItem(DexDebugInfo debugInfo) {
+    mixedSectionOffsets.setOffsetFor(debugInfo, dest.position());
+    dest.putBytes(new DebugBytecodeWriter(debugInfo, mapping).generate());
+  }
+
+  private void writeCodeItem(DexCode code) {
+    mixedSectionOffsets.setOffsetFor(code, dest.align(4));
+    // Fixed size header information.
+    dest.putShort((short) code.registerSize);
+    dest.putShort((short) code.incomingRegisterSize);
+    dest.putShort((short) code.outgoingRegisterSize);
+    dest.putShort((short) code.tries.length);
+    dest.putInt(mixedSectionOffsets.getOffsetFor(code.getDebugInfo()));
+    // Jump over the size.
+    int insnSizeOffset = dest.position();
+    dest.forward(4);
+    // Write instruction stream.
+    dest.putInstructions(code.instructions, mapping);
+    // Compute size and do the backward/forward dance to write the size at the beginning.
+    int insnSize = dest.position() - insnSizeOffset - 4;
+    dest.rewind(insnSize + 4);
+    dest.putInt(insnSize / 2);
+    dest.forward(insnSize);
+    if (code.tries.length > 0) {
+      // The tries need to be 4 byte aligned.
+      int beginOfTriesOffset = dest.align(4);
+      // First write the handlers, so that we know their mixedSectionOffsets.
+      dest.forward(code.tries.length * 8);
+      int beginOfHandlersOffset = dest.position();
+      dest.putUleb128(code.handlers.length);
+      short[] offsets = new short[code.handlers.length];
+      int i = 0;
+      for (TryHandler handler : code.handlers) {
+        offsets[i++] = (short) (dest.position() - beginOfHandlersOffset);
+        boolean hasCatchAll = handler.catchAllAddr != TryHandler.NO_HANDLER;
+        dest.putSleb128(hasCatchAll ? -handler.pairs.length : handler.pairs.length);
+        for (TypeAddrPair pair : handler.pairs) {
+          dest.putUleb128(mapping.getOffsetFor(pair.type));
+          dest.putUleb128(pair.addr);
+        }
+        if (hasCatchAll) {
+          dest.putUleb128(handler.catchAllAddr);
+        }
+      }
+      int endOfCodeOffset = dest.position();
+      // Now write the tries.
+      dest.moveTo(beginOfTriesOffset);
+      for (Try aTry : code.tries) {
+        dest.putInt(aTry.startAddress);
+        dest.putShort((short) aTry.instructionCount);
+        dest.putShort(offsets[aTry.handlerIndex]);
+      }
+      // And move to the end.
+      dest.moveTo(endOfCodeOffset);
+    }
+  }
+
+  private void writeTypeList(DexTypeList list) {
+    assert !list.isEmpty();
+    mixedSectionOffsets.setOffsetFor(list, dest.align(4));
+    DexType[] values = list.values;
+    dest.putInt(values.length);
+    for (DexType type : values) {
+      dest.putShort((short) mapping.getOffsetFor(type));
+    }
+  }
+
+  private void writeStringData(DexString string) {
+    mixedSectionOffsets.setOffsetFor(string, dest.position());
+    dest.putUleb128(string.size);
+    dest.putBytes(string.content);
+  }
+
+  private void writeAnnotation(DexAnnotation annotation) {
+    mixedSectionOffsets.setOffsetFor(annotation, dest.position());
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Writing Annotation @ 0x%08x.", dest.position());
+    }
+    dest.putByte((byte) annotation.visibility);
+    writeEncodedAnnotation(annotation.annotation, dest, mapping);
+  }
+
+  private void writeAnnotationSet(DexAnnotationSet set) {
+    assert !set.isEmpty();
+    assert isSorted(set.annotations, (item) -> item.annotation.type);
+    mixedSectionOffsets.setOffsetFor(set, dest.align(4));
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Writing AnnotationSet @ 0x%08x.", dest.position());
+    }
+    dest.putInt(set.annotations.length);
+    for (DexAnnotation annotation : set.annotations) {
+      dest.putInt(mixedSectionOffsets.getOffsetFor(annotation));
+    }
+  }
+
+  private void writeAnnotationSetRefList(DexAnnotationSetRefList setRefList) {
+    assert !setRefList.isEmpty();
+    mixedSectionOffsets.setOffsetFor(setRefList, dest.align(4));
+    dest.putInt(setRefList.values.length);
+    for (DexAnnotationSet set : setRefList.values) {
+      dest.putInt(mixedSectionOffsets.getOffsetFor(set));
+    }
+  }
+
+  private <S extends Descriptor<T, S>, T extends KeyedDexItem<S>> void writeMemberAnnotations(
+      List<T> items, ToIntFunction<T> getter) {
+    for (T item : items) {
+      dest.putInt(item.getKey().getOffset(mapping));
+      dest.putInt(getter.applyAsInt(item));
+    }
+  }
+
+  private void writeAnnotationDirectoryForClass(DexProgramClass clazz) {
+    if (clazz.hasAnnotations()) {
+      mixedSectionOffsets.setOffsetForAnnotationsDirectory(clazz, dest.align(4));
+      numberOfAnnotationDirectories++;
+      assert isSorted(clazz.directMethods());
+      assert isSorted(clazz.virtualMethods());
+      OrderedMergingIterator<DexEncodedMethod, DexMethod> methods =
+          new OrderedMergingIterator<>(clazz.directMethods(), clazz.virtualMethods());
+      List<DexEncodedMethod> methodAnnotations = new ArrayList<>();
+      List<DexEncodedMethod> parameterAnnotations = new ArrayList<>();
+      while (methods.hasNext()) {
+        DexEncodedMethod method = methods.next();
+        if (!method.annotations.isEmpty()) {
+          methodAnnotations.add(method);
+        }
+        if (!method.parameterAnnotations.isEmpty()) {
+          parameterAnnotations.add(method);
+        }
+      }
+      assert isSorted(clazz.staticFields());
+      assert isSorted(clazz.instanceFields());
+      OrderedMergingIterator<DexEncodedField, DexField> fields =
+          new OrderedMergingIterator<>(clazz.staticFields(), clazz.instanceFields());
+      List<DexEncodedField> fieldAnnotations = new ArrayList<>();
+      while (fields.hasNext()) {
+        DexEncodedField field = fields.next();
+        if (!field.annotations.isEmpty()) {
+          fieldAnnotations.add(field);
+        }
+      }
+      dest.putInt(mixedSectionOffsets.getOffsetFor(clazz.annotations));
+      dest.putInt(fieldAnnotations.size());
+      dest.putInt(methodAnnotations.size());
+      dest.putInt(parameterAnnotations.size());
+      writeMemberAnnotations(fieldAnnotations,
+          item -> mixedSectionOffsets.getOffsetFor(item.annotations));
+      writeMemberAnnotations(methodAnnotations,
+          item -> mixedSectionOffsets.getOffsetFor(item.annotations));
+      writeMemberAnnotations(parameterAnnotations,
+          item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotations));
+    }
+  }
+
+  private void writeEncodedFields(DexEncodedField[] fields) {
+    assert isSorted(fields);
+    int currentOffset = 0;
+    for (DexEncodedField field : fields) {
+      int nextOffset = mapping.getOffsetFor(field.field);
+      assert nextOffset - currentOffset >= 0;
+      dest.putUleb128(nextOffset - currentOffset);
+      currentOffset = nextOffset;
+      dest.putUleb128(field.accessFlags.get());
+    }
+  }
+
+  private void writeEncodedMethods(DexEncodedMethod[] methods) {
+    assert isSorted(methods);
+    int currentOffset = 0;
+    for (DexEncodedMethod method : methods) {
+      int nextOffset = mapping.getOffsetFor(method.method);
+      assert nextOffset - currentOffset >= 0;
+      dest.putUleb128(nextOffset - currentOffset);
+      currentOffset = nextOffset;
+      dest.putUleb128(method.accessFlags.get());
+      if (method.getCode() == null) {
+        assert method.accessFlags.isAbstract() || method.accessFlags.isNative();
+        dest.putUleb128(0);
+      } else {
+        dest.putUleb128(mixedSectionOffsets.getOffsetFor(method.getCode().asDexCode()));
+        // Writing the methods starts to take up memory so we are going to flush the
+        // code objects since they are no longer necessary after this.
+        method.removeCode();
+      }
+    }
+  }
+
+  private void writeClassData(DexProgramClass clazz) {
+    assert clazz.hasMethodsOrFields();
+    mixedSectionOffsets.setOffsetFor(clazz, dest.position());
+    dest.putUleb128(clazz.staticFields().length);
+    dest.putUleb128(clazz.instanceFields().length);
+    dest.putUleb128(clazz.directMethods().length);
+    dest.putUleb128(clazz.virtualMethods().length);
+    writeEncodedFields(clazz.staticFields());
+    writeEncodedFields(clazz.instanceFields());
+    writeEncodedMethods(clazz.directMethods());
+    writeEncodedMethods(clazz.virtualMethods());
+  }
+
+  private void addStaticFieldValues(DexProgramClass clazz) {
+    DexEncodedField[] fields = clazz.staticFields();
+    int length = 0;
+    List<DexValue> values = new ArrayList<>(fields.length);
+    for (int i = 0; i < fields.length; i++) {
+      DexEncodedField field = fields[i];
+      assert field.staticValue != null;
+      values.add(field.staticValue);
+      if (!field.staticValue.isDefault(field.field.type, application.dexItemFactory)) {
+        length = i + 1;
+      }
+    }
+    if (length > 0) {
+      DexEncodedArray staticValues = new DexEncodedArray(
+          values.subList(0, length).toArray(new DexValue[length]));
+      clazz.setStaticValues(staticValues);
+      mixedSectionOffsets.add(staticValues);
+    }
+  }
+
+  private void writeMethodHandle(DexMethodHandle methodHandle) {
+    checkThatInvokeCustomIsAllowed();
+    MethodHandleType methodHandleDexType = methodHandle.type;
+    switch (methodHandleDexType) {
+      case INVOKE_CONSTRUCTOR:
+        throw new CompilationError("Constructor method handle type is not yet supported.");
+      case INVOKE_INTERFACE:
+      case INVOKE_SUPER:
+        methodHandleDexType = MethodHandleType.INVOKE_INSTANCE;
+        break;
+    }
+    assert dest.isAligned(4);
+    dest.putShort(methodHandleDexType.getValue());
+    dest.putShort((short) 0); // unused
+    int fieldOrMethodIdx;
+    if (methodHandle.isMethodHandle()) {
+      fieldOrMethodIdx = mapping.getOffsetFor(methodHandle.asMethod());
+    } else {
+      assert methodHandle.isFieldHandle();
+      fieldOrMethodIdx = mapping.getOffsetFor(methodHandle.asField());
+    }
+    assert (short) fieldOrMethodIdx == fieldOrMethodIdx;
+    dest.putShort((short) fieldOrMethodIdx);
+    dest.putShort((short) 0); // unused
+  }
+
+  private void writeCallSite(DexCallSite callSite) {
+    checkThatInvokeCustomIsAllowed();
+    assert dest.isAligned(4);
+    dest.putInt(mixedSectionOffsets.getOffsetFor(callSite.getEncodedArray()));
+  }
+
+  private void writeEncodedArray(DexEncodedArray array) {
+    mixedSectionOffsets.setOffsetFor(array, dest.position());
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Writing EncodedArray @ 0x%08x [%s].", dest.position(), array);
+    }
+    dest.putUleb128(array.values.length);
+    for (DexValue value : array.values) {
+      value.writeTo(dest, mapping);
+    }
+  }
+
+  private int writeMapItem(int type, int offset, int length) {
+    if (length == 0) {
+      return 0;
+    }
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Map entry 0x%04x @ 0x%08x # %08d.", type, offset, length);
+    }
+    dest.putShort((short) type);
+    dest.putShort((short) 0);
+    dest.putInt(length);
+    dest.putInt(offset);
+    return 1;
+  }
+
+  private void writeMap(Layout layout) {
+    int startOfMap = dest.align(4);
+    dest.forward(4); // Leave space for size;
+    int size = 0;
+    size += writeMapItem(Constants.TYPE_HEADER_ITEM, 0, 1);
+    size += writeMapItem(Constants.TYPE_STRING_ID_ITEM, layout.stringIdsOffset,
+        mapping.getStrings().length);
+    size += writeMapItem(Constants.TYPE_TYPE_ID_ITEM, layout.typeIdsOffset,
+        mapping.getTypes().length);
+    size += writeMapItem(Constants.TYPE_PROTO_ID_ITEM, layout.protoIdsOffset,
+        mapping.getProtos().length);
+    size += writeMapItem(Constants.TYPE_FIELD_ID_ITEM, layout.fieldIdsOffset,
+        mapping.getFields().length);
+    size += writeMapItem(Constants.TYPE_METHOD_ID_ITEM, layout.methodIdsOffset,
+        mapping.getMethods().length);
+    size += writeMapItem(Constants.TYPE_CLASS_DEF_ITEM, layout.classDefsOffset,
+        mapping.getClasses().length);
+    size += writeMapItem(Constants.TYPE_CALL_SITE_ID_ITEM, layout.callSiteIdsOffset,
+        mapping.getCallSites().length);
+    size += writeMapItem(Constants.TYPE_METHOD_HANDLE_ITEM, layout.methodHandleIdsOffset,
+        mapping.getMethodHandles().length);
+    size += writeMapItem(Constants.TYPE_CODE_ITEM, layout.getCodesOffset(),
+        mixedSectionOffsets.getCodes().size());
+    size += writeMapItem(Constants.TYPE_DEBUG_INFO_ITEM, layout.getDebugInfosOffset(),
+        mixedSectionOffsets.getDebugInfos().size());
+    size += writeMapItem(Constants.TYPE_TYPE_LIST, layout.getTypeListsOffset(),
+        mixedSectionOffsets.getTypeLists().size());
+    size += writeMapItem(Constants.TYPE_STRING_DATA_ITEM, layout.getStringDataOffsets(),
+        mixedSectionOffsets.getStringData().size());
+    size += writeMapItem(Constants.TYPE_ANNOTATION_ITEM, layout.getAnnotationsOffset(),
+        mixedSectionOffsets.getAnnotations().size());
+    size += writeMapItem(Constants.TYPE_ANNOTATION_SET_ITEM, layout.getAnnotationSetsOffset(),
+        mixedSectionOffsets.getAnnotationSets().size());
+    size += writeMapItem(Constants.TYPE_ANNOTATION_SET_REF_LIST,
+        layout.getAnnotationSetRefListsOffset(),
+        mixedSectionOffsets.getAnnotationSetRefLists().size());
+    size += writeMapItem(Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM,
+        layout.getAnnotationDirectoriesOffset(),
+        numberOfAnnotationDirectories);
+    size += writeMapItem(Constants.TYPE_CLASS_DATA_ITEM, layout.getClassDataOffset(),
+        mixedSectionOffsets.getClassesWithData().size());
+    size += writeMapItem(Constants.TYPE_ENCODED_ARRAY_ITEM, layout.getEncodedArrarysOffset(),
+        mixedSectionOffsets.getEncodedArrays().size());
+    size += writeMapItem(Constants.TYPE_MAP_LIST, layout.getMapOffset(), 1);
+    dest.moveTo(startOfMap);
+    dest.putInt(size);
+    dest.forward(size * Constants.TYPE_MAP_LIST_ITEM_SIZE);
+  }
+
+  private static byte[] convertApiLevelToDexVersion(int apiLevel) {
+    if (apiLevel >= Constants.ANDROID_O_API) {
+      return Constants.ANDROID_O_DEX_VERSION_BYTES;
+    }
+    if (apiLevel >= Constants.ANDROID_N_API) {
+      return Constants.ANDROID_N_DEX_VERSION_BYTES;
+    }
+    return Constants.ANDROID_PRE_N_DEX_VERSION_BYTES;
+  }
+
+  private void writeHeader(Layout layout) {
+    dest.moveTo(0);
+    dest.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
+    dest.putBytes(convertApiLevelToDexVersion(options.minApiLevel));
+    dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
+    // Leave out checksum and signature for now.
+    dest.moveTo(Constants.FILE_SIZE_OFFSET);
+    dest.putInt(layout.getEndOfFile());
+    dest.putInt(Constants.HEADER_SIZE);
+    dest.putInt(Constants.ENDIAN_CONSTANT);
+    dest.putInt(0);
+    dest.putInt(0);
+    dest.putInt(layout.getMapOffset());
+    int numberOfStrings = mapping.getStrings().length;
+    dest.putInt(numberOfStrings);
+    dest.putInt(numberOfStrings == 0 ? 0 : layout.stringIdsOffset);
+    int numberOfTypes = mapping.getTypes().length;
+    dest.putInt(numberOfTypes);
+    dest.putInt(numberOfTypes == 0 ? 0 : layout.typeIdsOffset);
+    int numberOfProtos = mapping.getProtos().length;
+    dest.putInt(numberOfProtos);
+    dest.putInt(numberOfProtos == 0 ? 0 : layout.protoIdsOffset);
+    int numberOfFields = mapping.getFields().length;
+    dest.putInt(numberOfFields);
+    dest.putInt(numberOfFields == 0 ? 0 : layout.fieldIdsOffset);
+    int numberOfMethods = mapping.getMethods().length;
+    dest.putInt(numberOfMethods);
+    dest.putInt(numberOfMethods == 0 ? 0 : layout.methodIdsOffset);
+    int numberOfClasses = mapping.getClasses().length;
+    dest.putInt(numberOfClasses);
+    dest.putInt(numberOfClasses == 0 ? 0 : layout.classDefsOffset);
+    dest.putInt(layout.getDataSectionSize());
+    dest.putInt(layout.dataSectionOffset);
+    assert dest.position() == layout.stringIdsOffset;
+  }
+
+  private void writeSignature(Layout layout) {
+    try {
+      MessageDigest md = MessageDigest.getInstance("SHA-1");
+      md.update(dest.asArray(), Constants.FILE_SIZE_OFFSET,
+          layout.getEndOfFile() - Constants.FIELD_IDS_OFF_OFFSET);
+      md.digest(dest.asArray(), Constants.SIGNATURE_OFFSET, 20);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private void writeChecksum(Layout layout) {
+    Adler32 adler = new Adler32();
+    adler.update(dest.asArray(), Constants.SIGNATURE_OFFSET,
+        layout.getEndOfFile() - Constants.SIGNATURE_OFFSET);
+    dest.moveTo(Constants.CHECKSUM_OFFSET);
+    dest.putInt((int) adler.getValue());
+  }
+
+  private int alignSize(int bytes, int value) {
+    int mask = bytes - 1;
+    return (value + mask) & ~mask;
+  }
+
+  private static class Layout {
+
+    private static final int NOT_SET = -1;
+
+    // Fixed size constant pool sections
+    final int stringIdsOffset;
+    final int typeIdsOffset;
+    final int protoIdsOffset;
+    final int fieldIdsOffset;
+    final int methodIdsOffset;
+    final int classDefsOffset;
+    final int callSiteIdsOffset;
+    final int methodHandleIdsOffset;
+    final int dataSectionOffset;
+
+    // Mixed size sections
+    private int codesOffset = NOT_SET; // aligned
+    private int debugInfosOffset = NOT_SET;
+
+    private int typeListsOffset = NOT_SET; // aligned
+    private int stringDataOffsets = NOT_SET;
+    private int annotationsOffset = NOT_SET;
+    private int annotationSetsOffset = NOT_SET; // aligned
+    private int annotationSetRefListsOffset = NOT_SET; // aligned
+    private int annotationDirectoriesOffset = NOT_SET; // aligned
+    private int classDataOffset = NOT_SET;
+    private int encodedArrarysOffset = NOT_SET;
+    private int mapOffset = NOT_SET;
+    private int endOfFile = NOT_SET;
+
+    private Layout(int stringIdsOffset, int typeIdsOffset, int protoIdsOffset, int fieldIdsOffset,
+        int methodIdsOffset, int classDefsOffset, int callSiteIdsOffset, int methodHandleIdsOffset,
+        int dataSectionOffset) {
+      this.stringIdsOffset = stringIdsOffset;
+      this.typeIdsOffset = typeIdsOffset;
+      this.protoIdsOffset = protoIdsOffset;
+      this.fieldIdsOffset = fieldIdsOffset;
+      this.methodIdsOffset = methodIdsOffset;
+      this.classDefsOffset = classDefsOffset;
+      this.callSiteIdsOffset = callSiteIdsOffset;
+      this.methodHandleIdsOffset = methodHandleIdsOffset;
+      this.dataSectionOffset = dataSectionOffset;
+      assert stringIdsOffset <= typeIdsOffset;
+      assert typeIdsOffset <= protoIdsOffset;
+      assert protoIdsOffset <= fieldIdsOffset;
+      assert fieldIdsOffset <= methodIdsOffset;
+      assert methodIdsOffset <= classDefsOffset;
+      assert classDefsOffset <= dataSectionOffset;
+      assert callSiteIdsOffset <= dataSectionOffset;
+      assert methodHandleIdsOffset <= dataSectionOffset;
+    }
+
+    static Layout from(ObjectToOffsetMapping mapping) {
+      int offset = 0;
+      return new Layout(
+          offset = Constants.HEADER_SIZE,
+          offset += mapping.getStrings().length * Constants.TYPE_STRING_ID_ITEM_SIZE,
+          offset += mapping.getTypes().length * Constants.TYPE_TYPE_ID_ITEM_SIZE,
+          offset += mapping.getProtos().length * Constants.TYPE_PROTO_ID_ITEM_SIZE,
+          offset += mapping.getFields().length * Constants.TYPE_FIELD_ID_ITEM_SIZE,
+          offset += mapping.getMethods().length * Constants.TYPE_METHOD_ID_ITEM_SIZE,
+          offset += mapping.getClasses().length * Constants.TYPE_CLASS_DEF_ITEM_SIZE,
+          offset += mapping.getCallSites().length * Constants.TYPE_CALL_SITE_ID_ITEM_SIZE,
+          offset += mapping.getMethodHandles().length * Constants.TYPE_METHOD_HANDLE_ITEM_SIZE);
+    }
+
+    int getDataSectionSize() {
+      int size = getEndOfFile() - dataSectionOffset;
+      assert size % 4 == 0;
+      return size;
+    }
+
+    private boolean isValidOffset(int value, boolean isAligned) {
+      return value != NOT_SET && (!isAligned || value % 4 == 0);
+    }
+
+    public int getCodesOffset() {
+      assert isValidOffset(codesOffset, true);
+      return codesOffset;
+    }
+
+    public void setCodesOffset(int codesOffset) {
+      assert this.codesOffset == NOT_SET;
+      this.codesOffset = codesOffset;
+    }
+
+    public int getDebugInfosOffset() {
+      assert isValidOffset(debugInfosOffset, false);
+      return debugInfosOffset;
+    }
+
+    public void setDebugInfosOffset(int debugInfosOffset) {
+      assert this.debugInfosOffset == NOT_SET;
+      this.debugInfosOffset = debugInfosOffset;
+    }
+
+    public int getTypeListsOffset() {
+      assert isValidOffset(typeListsOffset, true);
+      return typeListsOffset;
+    }
+
+    public void setTypeListsOffset(int typeListsOffset) {
+      assert this.typeListsOffset == NOT_SET;
+      this.typeListsOffset = typeListsOffset;
+    }
+
+    public int getStringDataOffsets() {
+      assert isValidOffset(stringDataOffsets, false);
+      return stringDataOffsets;
+    }
+
+    public void setStringDataOffsets(int stringDataOffsets) {
+      assert this.stringDataOffsets == NOT_SET;
+      this.stringDataOffsets = stringDataOffsets;
+    }
+
+    public int getAnnotationsOffset() {
+      assert isValidOffset(annotationsOffset, false);
+      return annotationsOffset;
+    }
+
+    public void setAnnotationsOffset(int annotationsOffset) {
+      assert this.annotationsOffset == NOT_SET;
+      this.annotationsOffset = annotationsOffset;
+    }
+
+    public int getAnnotationSetsOffset() {
+      assert isValidOffset(annotationSetsOffset, true);
+      return annotationSetsOffset;
+    }
+
+    public void alreadySetOffset(int ignored) {
+      // Intentionally empty.
+    }
+
+    public void setAnnotationSetsOffset(int annotationSetsOffset) {
+      assert this.annotationSetsOffset == NOT_SET;
+      this.annotationSetsOffset = annotationSetsOffset;
+    }
+
+    public int getAnnotationSetRefListsOffset() {
+      assert isValidOffset(annotationSetRefListsOffset, true);
+      return annotationSetRefListsOffset;
+    }
+
+    public void setAnnotationSetRefListsOffset(int annotationSetRefListsOffset) {
+      assert this.annotationSetRefListsOffset == NOT_SET;
+      this.annotationSetRefListsOffset = annotationSetRefListsOffset;
+    }
+
+    public int getAnnotationDirectoriesOffset() {
+      assert isValidOffset(annotationDirectoriesOffset, true);
+      return annotationDirectoriesOffset;
+    }
+
+    public void setAnnotationDirectoriesOffset(int annotationDirectoriesOffset) {
+      assert this.annotationDirectoriesOffset == NOT_SET;
+      this.annotationDirectoriesOffset = annotationDirectoriesOffset;
+    }
+
+    public int getClassDataOffset() {
+      assert isValidOffset(classDataOffset, false);
+      return classDataOffset;
+    }
+
+    public void setClassDataOffset(int classDataOffset) {
+      assert this.classDataOffset == NOT_SET;
+      this.classDataOffset = classDataOffset;
+    }
+
+    public int getEncodedArrarysOffset() {
+      assert isValidOffset(encodedArrarysOffset, false);
+      return encodedArrarysOffset;
+    }
+
+    public void setEncodedArrarysOffset(int encodedArrarysOffset) {
+      assert this.encodedArrarysOffset == NOT_SET;
+      this.encodedArrarysOffset = encodedArrarysOffset;
+    }
+
+    public int getMapOffset() {
+      return mapOffset;
+    }
+
+    public void setMapOffset(int mapOffset) {
+      this.mapOffset = mapOffset;
+    }
+
+    public int getEndOfFile() {
+      return endOfFile;
+    }
+
+    public void setEndOfFile(int endOfFile) {
+      this.endOfFile = endOfFile;
+    }
+  }
+
+  /**
+   * Encapsulates information on the offsets of items in the sections of the mixed data part of the
+   * DEX file.
+   * Initially, items are collected using the {@link MixedSectionCollection} traversal and all
+   * offsets are unset. When writing a section, the offsets of the written items are stored.
+   * These offsets are then used to resolve cross-references between items from different sections
+   * into a file offset.
+   */
+  private static class MixedSectionOffsets extends MixedSectionCollection {
+
+    private static final int NOT_SET = -1;
+
+    private final Map<DexCode, Integer> codes = Maps.newIdentityHashMap();
+    private final List<DexCode> codesList = new LinkedList<>();
+    private final Hashtable<DexDebugInfo, Integer> debugInfos = new Hashtable<>();
+    private final List<DexDebugInfo> debugInfosList = new LinkedList<>();
+    private final Hashtable<DexTypeList, Integer> typeLists = new Hashtable<>();
+    private final List<DexTypeList> typeListsList = new LinkedList<>();
+    private final Hashtable<DexString, Integer> stringData = new Hashtable<>();
+    private final List<DexString> stringDataList = new LinkedList<>();
+    private final Hashtable<DexAnnotation, Integer> annotations = new Hashtable<>();
+    private final List<DexAnnotation> annotationsList = new LinkedList<>();
+    private final Hashtable<DexAnnotationSet, Integer> annotationSets = new Hashtable<>();
+    private final List<DexAnnotationSet> annotationSetsList = new LinkedList<>();
+    private final Hashtable<DexAnnotationSetRefList, Integer> annotationSetRefLists
+        = new Hashtable<>();
+    private final List<DexAnnotationSetRefList> annotationSetRefListsList = new LinkedList<>();
+    private final Hashtable<DexProgramClass, Integer> annotationDirectories
+        = new Hashtable<>();
+    private final Hashtable<DexProgramClass, Integer> classesWithData = new Hashtable<>();
+    private final List<DexProgramClass> classesWithDataList = new LinkedList<>();
+    private final Hashtable<DexEncodedArray, Integer> encodedArrays = new Hashtable<>();
+    private final List<DexEncodedArray> encodedArraysList = new LinkedList<>();
+
+    private <T> boolean add(Map<T, Integer> map, List<T> list, T item) {
+      boolean notSeen = map.put(item, NOT_SET) == null;
+      if (notSeen) {
+        list.add(item);
+      }
+      return notSeen;
+    }
+
+    @Override
+    public boolean add(DexProgramClass aClassWithData) {
+      return add(classesWithData, classesWithDataList, aClassWithData);
+    }
+
+    @Override
+    public boolean add(DexEncodedArray encodedArray) {
+      return add(encodedArrays, encodedArraysList, encodedArray);
+    }
+
+    @Override
+    public boolean add(DexAnnotationSet annotationSet) {
+      if (annotationSet.isEmpty()) {
+        return false;
+      }
+      return add(annotationSets, annotationSetsList, annotationSet);
+    }
+
+    @Override
+    public boolean add(DexCode code) {
+      return add(codes, codesList, code);
+    }
+
+    @Override
+    public boolean add(DexDebugInfo debugInfo) {
+      return add(debugInfos, debugInfosList, debugInfo);
+    }
+
+    @Override
+    public boolean add(DexTypeList typeList) {
+      if (typeList.isEmpty()) {
+        return false;
+      }
+      return add(typeLists, typeListsList, typeList);
+    }
+
+    @Override
+    public boolean add(DexAnnotationSetRefList annotationSetRefList) {
+      if (annotationSetRefList.isEmpty()) {
+        return false;
+      }
+      return add(annotationSetRefLists, annotationSetRefListsList, annotationSetRefList);
+    }
+
+    @Override
+    public boolean add(DexAnnotation annotation) {
+      return add(annotations, annotationsList, annotation);
+    }
+
+    public boolean add(DexString string) {
+      return add(stringData, stringDataList, string);
+    }
+
+    public List<DexCode> getCodes() {
+      return Collections.unmodifiableList(codesList);
+    }
+
+    public List<DexDebugInfo> getDebugInfos() {
+      return Collections.unmodifiableList(debugInfosList);
+    }
+
+    public List<DexTypeList> getTypeLists() {
+      return Collections.unmodifiableList(typeListsList);
+    }
+
+    public List<DexString> getStringData() {
+      return Collections.unmodifiableList(stringDataList);
+    }
+
+    public List<DexAnnotation> getAnnotations() {
+      return Collections.unmodifiableList(annotationsList);
+    }
+
+    public List<DexAnnotationSet> getAnnotationSets() {
+      return Collections.unmodifiableList(annotationSetsList);
+    }
+
+    public List<DexAnnotationSetRefList> getAnnotationSetRefLists() {
+      return Collections.unmodifiableList(annotationSetRefListsList);
+    }
+
+    public List<DexProgramClass> getClassesWithData() {
+      return Collections.unmodifiableList(classesWithDataList);
+    }
+
+    public List<DexEncodedArray> getEncodedArrays() {
+      return Collections.unmodifiableList(encodedArraysList);
+    }
+
+    private <T> int lookup(T item, Map<T, Integer> table) {
+      if (item == null) {
+        return Constants.NO_OFFSET;
+      }
+      Integer offset = table.get(item);
+      assert offset != null;
+      assert offset != NOT_SET;
+      return offset;
+    }
+
+    public int getOffsetFor(DexString item) {
+      return lookup(item, stringData);
+    }
+
+    public int getOffsetFor(DexTypeList parameters) {
+      if (parameters.isEmpty()) {
+        return 0;
+      }
+      return lookup(parameters, typeLists);
+    }
+
+    public int getOffsetFor(DexProgramClass aClassWithData) {
+      return lookup(aClassWithData, classesWithData);
+    }
+
+    public int getOffsetFor(DexEncodedArray encodedArray) {
+      return lookup(encodedArray, encodedArrays);
+    }
+
+    public int getOffsetFor(DexDebugInfo debugInfo) {
+      return lookup(debugInfo, debugInfos);
+    }
+
+
+    public int getOffsetForAnnotationsDirectory(DexProgramClass clazz) {
+      Integer offset = annotationDirectories.get(clazz);
+      if (offset == null) {
+        return Constants.NO_OFFSET;
+      }
+      return offset;
+    }
+
+    public int getOffsetFor(DexAnnotation annotation) {
+      return lookup(annotation, annotations);
+    }
+
+    public int getOffsetFor(DexAnnotationSet annotationSet) {
+      if (annotationSet.isEmpty()) {
+        return 0;
+      }
+      return lookup(annotationSet, annotationSets);
+    }
+
+    public int getOffsetFor(DexAnnotationSetRefList annotationSetRefList) {
+      if (annotationSetRefList.isEmpty()) {
+        return 0;
+      }
+      return lookup(annotationSetRefList, annotationSetRefLists);
+    }
+
+    public int getOffsetFor(DexCode code) {
+      return lookup(code, codes);
+    }
+
+    private <T> void setOffsetFor(T item, int offset, Map<T, Integer> table) {
+      Integer old = table.put(item, offset);
+      assert old != null;
+      assert old <= NOT_SET;
+    }
+
+    void setOffsetFor(DexDebugInfo debugInfo, int offset) {
+      setOffsetFor(debugInfo, offset, debugInfos);
+    }
+
+    void setOffsetFor(DexCode code, int offset) {
+      setOffsetFor(code, offset, codes);
+    }
+
+    void setOffsetFor(DexTypeList typeList, int offset) {
+      assert offset != 0 && !typeLists.isEmpty();
+      setOffsetFor(typeList, offset, typeLists);
+    }
+
+    void setOffsetFor(DexString string, int offset) {
+      setOffsetFor(string, offset, stringData);
+    }
+
+    void setOffsetFor(DexAnnotation annotation, int offset) {
+      setOffsetFor(annotation, offset, annotations);
+    }
+
+    void setOffsetFor(DexAnnotationSet annotationSet, int offset) {
+      setOffsetFor(annotationSet, offset, annotationSets);
+    }
+
+    void setOffsetForAnnotationsDirectory(DexProgramClass clazz,
+        int offset) {
+      Integer previous = annotationDirectories.put(clazz, offset);
+      assert previous == null;
+    }
+
+    void setOffsetFor(DexProgramClass aClassWithData, int offset) {
+      setOffsetFor(aClassWithData, offset, classesWithData);
+    }
+
+    void setOffsetFor(DexEncodedArray encodedArray, int offset) {
+      setOffsetFor(encodedArray, offset, encodedArrays);
+    }
+
+    void setOffsetFor(DexAnnotationSetRefList annotationSetRefList, int offset) {
+      assert offset != 0 && !annotationSetRefList.isEmpty();
+      setOffsetFor(annotationSetRefList, offset, annotationSetRefLists);
+    }
+  }
+
+  private class ProgramClassDependencyCollector extends ProgramClassVisitor {
+
+    private final Set<DexClass> includedClasses = Sets.newIdentityHashSet();
+
+    ProgramClassDependencyCollector(DexApplication application, DexProgramClass[] includedClasses) {
+      super(application);
+      Collections.addAll(this.includedClasses, includedClasses);
+    }
+
+    @Override
+    public void visit(DexType type) {
+      // Intentionally left empty.
+    }
+
+    @Override
+    public void visit(DexClass clazz) {
+      // Only visit classes that are part of the current file.
+      if (!includedClasses.contains(clazz)) {
+        return;
+      }
+      clazz.addDependencies(mixedSectionOffsets);
+    }
+  }
+
+  private void checkThatInvokeCustomIsAllowed() {
+    if (!options.canUseInvokeCustom()) {
+      throw new CompilationError("Invoke-custom is unsupported before Android O (--min-sdk-version "
+          + Constants.ANDROID_O_API + ")");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
new file mode 100644
index 0000000..73d5d86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/IndexedItemCollection.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.IndexedDexItem;
+
+/**
+ * Common interface for constant pools.
+ *
+ * <p>This is semantically a wrapper around a number of sets for all subtypes of
+ * {@link IndexedDexItem}. <b>Items should not be added directly to this collection.</b> Instead see
+ * {@link DexItem#collectIndexedItems}.
+ *
+ * <p>Note that the various add methods of this class are not transitive, i.e., they do not add
+ * components of the {@link IndexedDexItem} itself. Use a call to
+ * {@link IndexedDexItem#collectIndexedItems} for this.
+ */
+public interface IndexedItemCollection {
+
+  /**
+   * Adds the given class to the collection.
+   *
+   * <p>Does not add the class' components.
+   *
+   * @return true if the class was not in the pool before.
+   */
+  boolean addClass(DexProgramClass dexProgramClass);
+
+  /**
+   * Adds the given field to the collection.
+   *
+   * <p>Does not add the field's components.
+   *
+   * @return true if the field was not in the pool before.
+   */
+  boolean addField(DexField field);
+
+  /**
+   * Adds the given method to the collection.
+   *
+   * <p>Does not add the method's components.
+   *
+   * @return true if the method was not in the pool before.
+   */
+  boolean addMethod(DexMethod method);
+
+  /**
+   * Adds the given class to the collection.
+   *
+   * <p>Does not add the classes components.
+   *
+   * @return true if the class was not in the pool before.
+   */
+  boolean addString(DexString string);
+
+  /**
+   * Adds the given proto to the collection.
+   *
+   * <p>Does not add the proto's components.
+   *
+   * @return true if the proto was not in the pool before.
+   */
+  boolean addProto(DexProto proto);
+
+  /**
+   * Adds the given type to the collection.
+   *
+   * <p>Does not add the type's components.
+   *
+   * @return true if the type was not in the pool before.
+   */
+  boolean addType(DexType type);
+
+  /**
+   * Adds the given call site to the collection.
+   *
+   * <p>Does not add the call site's components.
+   *
+   * @return true if the call site was not in the pool before.
+   */
+  boolean addCallSite(DexCallSite callSite);
+
+  /**
+   * Adds the given method handle to the collection.
+   *
+   * <p>Does not add the method handle site's components.
+   *
+   * @return true if the method handle was not in the pool before.
+   */
+  boolean addMethodHandle(DexMethodHandle methodHandle);
+
+  default DexString getRenamedName(DexMethod method) {
+    return method.name;
+  }
+
+  default DexString getRenamedName(DexField field) {
+    return field.name;
+  }
+
+  default DexString getRenamedDescriptor(DexType type) {
+    return type.descriptor;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
new file mode 100644
index 0000000..bc3fe98
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexTypeList;
+
+/**
+ * Collection of the various components of the mixed section of a dex file.
+ *
+ * <p>This semantically is just a wrapper around a bunch of collections. We do not expose the
+ * collections directly to allow for implementations that under the hood do not use collections.
+ *
+ * <p>See {@link DexItem#collectMixedSectionItems(MixedSectionCollection)} for
+ * information on how to fill a {@link MixedSectionCollection}.
+ */
+public abstract class MixedSectionCollection {
+
+  /**
+   * Adds the given class data to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexProgramClass dexClassData);
+
+  /**
+   * Adds the given encoded array to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexEncodedArray dexEncodedArray);
+
+  /**
+   * Adds the given annotation set to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexAnnotationSet dexAnnotationSet);
+
+  /**
+   * Adds the given code item to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexCode dexCode);
+
+  /**
+   * Adds the given debug info to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexDebugInfo dexDebugInfo);
+
+  /**
+   * Adds the given type list to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexTypeList dexTypeList);
+
+  /**
+   * Adds the given annotation-set reference list to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexAnnotationSetRefList annotationSetRefList);
+
+  /**
+   * Adds the given annotation to the collection.
+   *
+   * Does not add any dependencies.
+   *
+   * @return true if the item was not added before
+   */
+  public abstract boolean add(DexAnnotation annotation);
+}
diff --git a/src/main/java/com/android/tools/r8/dex/Segment.java b/src/main/java/com/android/tools/r8/dex/Segment.java
new file mode 100644
index 0000000..32b0c0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/Segment.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+public class Segment {
+
+  final int type;
+  final int size;
+  public final int offset;
+  int end;
+
+  public Segment(int type, int unused, int size, int offset) {
+    this.type = type;
+    assert unused == 0;
+    this.size = size;
+    this.offset = offset;
+    this.end = -1;
+  }
+
+  public int getType() {
+    return type;
+  }
+
+  public int getSize() {
+    return size;
+  }
+
+  void setEnd(int end) {
+    this.end = end;
+  }
+
+  String typeName() {
+    switch (type) {
+      case Constants.TYPE_HEADER_ITEM:
+        return "Header";
+      case Constants.TYPE_STRING_ID_ITEM:
+        return "Strings";
+      case Constants.TYPE_TYPE_ID_ITEM:
+        return "Types";
+      case Constants.TYPE_PROTO_ID_ITEM:
+        return "Protos";
+      case Constants.TYPE_FIELD_ID_ITEM:
+        return "Fields";
+      case Constants.TYPE_METHOD_ID_ITEM:
+        return "Methods";
+      case Constants.TYPE_CLASS_DEF_ITEM:
+        return "Class defs";
+      case Constants.TYPE_MAP_LIST:
+        return "Maps";
+      case Constants.TYPE_TYPE_LIST:
+        return "Type lists";
+      case Constants.TYPE_ANNOTATION_SET_REF_LIST:
+        return "Annotation set refs";
+      case Constants.TYPE_ANNOTATION_SET_ITEM:
+        return "Annotation sets";
+      case Constants.TYPE_CLASS_DATA_ITEM:
+        return "Class data";
+      case Constants.TYPE_CODE_ITEM:
+        return "Code";
+      case Constants.TYPE_STRING_DATA_ITEM:
+        return "String data";
+      case Constants.TYPE_DEBUG_INFO_ITEM:
+        return "Debug info";
+      case Constants.TYPE_ANNOTATION_ITEM:
+        return "Annotation";
+      case Constants.TYPE_ENCODED_ARRAY_ITEM:
+        return "Encoded arrays";
+      case Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+        return "Annotations directory";
+      default:
+        return "Unknown";
+    }
+  }
+
+  public String toString() {
+    return typeName() + " @" + offset + " " + size;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
new file mode 100644
index 0000000..e575cee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -0,0 +1,831 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PackageDistribution;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.Function;
+
+public class VirtualFile {
+
+  private static final int MAX_ENTRIES = (Short.MAX_VALUE << 1) + 1;
+  /**
+   * When distributing classes across files we aim to leave some space. The amount of space left is
+   * driven by this constant.
+   */
+  private static final int MAX_PREFILL_ENTRIES = MAX_ENTRIES - 5000;
+
+  private final int id;
+  private final VirtualFileIndexedItemCollection indexedItems;
+  private final IndexedItemTransaction transaction;
+
+  private VirtualFile(int id, NamingLens namingLens) {
+    this.id = id;
+    this.indexedItems = new VirtualFileIndexedItemCollection(id);
+    this.transaction = new IndexedItemTransaction(indexedItems, namingLens);
+  }
+
+  public static Map<Integer, VirtualFile> fileSetFrom(
+      ApplicationWriter writer,
+      PackageDistribution packageDistribution,
+      ExecutorService executorService)
+      throws ExecutionException, IOException {
+    // Strategy for distributing classes for write out:
+    // 1. Place all files in the package distribution file in the proposed files (if any).
+    // 2. Place the remaining files based on their packages in sorted order.
+    DexApplication application = writer.application;
+    InternalOptions options = writer.options;
+    Map<Integer, VirtualFile> nameToFileMap = new LinkedHashMap<>();
+    if (packageDistribution != null) {
+      int maxReferencedIndex = packageDistribution.maxReferencedIndex();
+      for (int index = 0; index <= maxReferencedIndex; index++) {
+        VirtualFile file = new VirtualFile(index, writer.namingLens);
+        nameToFileMap.put(index, file);
+      }
+    } else {
+      // If we had no map we default to 1 file, the package populator will add more if needed.
+      nameToFileMap.put(0, new VirtualFile(0, writer.namingLens));
+    }
+    Set<DexProgramClass> classes = Sets.newHashSet(application.classes());
+
+    // Compute the original names.
+    Map<DexProgramClass, String> originalNames = computeOriginalNameMapping(classes,
+        application.getProguardMap());
+
+    if (application.mainDexList != null) {
+      VirtualFile mainDexFile = nameToFileMap.get(0);
+      for (DexType type : application.mainDexList) {
+        DexClass clazz = application.definitionFor(type);
+        if (clazz != null && clazz.isProgramClass()) {
+          DexProgramClass programClass = (DexProgramClass) clazz;
+          mainDexFile.addClass(programClass);
+          if (mainDexFile.isFull()) {
+            throw new CompilationError("Cannot fit requested classes in main-dex file.");
+          }
+          classes.remove(programClass);
+        } else {
+          System.out.println(
+              "WARNING: Application does not contain `"
+                  + type.toDescriptorString()
+                  + "` as referenced in main-dex-list.");
+        }
+        mainDexFile.commitTransaction();
+      }
+    }
+
+    // Sort the classes based on the original names.
+    // This with make classes from the same package be adjacent.
+    classes = sortClassesByPackage(classes, originalNames);
+
+    Set<String> usedPrefixes = null;
+    if (packageDistribution != null) {
+      ArrayList<Future<List<DexProgramClass>>> futures = new ArrayList<>(nameToFileMap.size());
+      usedPrefixes = packageDistribution.getFiles();
+      for (VirtualFile file : nameToFileMap.values()) {
+        PackageMapPopulator populator =
+            new PackageMapPopulator(file, classes, packageDistribution, originalNames);
+        futures.add(executorService.submit(populator));
+      }
+      ThreadUtils.awaitFutures(futures).forEach(classes::removeAll);
+    }
+
+    // TODO(zerny): Add the package map to AndroidApp and refactor its generation.
+    Path newPackageMap = Paths.get("package.map");
+    Map<String, Integer> newAssignments;
+    if (classes.isEmpty()) {
+      newAssignments = Collections.emptyMap();
+    } else {
+      newAssignments =
+          new PackageSplitPopulator(
+              nameToFileMap, classes, originalNames, usedPrefixes, application.dexItemFactory,
+              options, writer.namingLens)
+              .call();
+      if (!newAssignments.isEmpty() && nameToFileMap.size() > 1) {
+        if (packageDistribution == null) {
+          System.out.println(" * Consider using a package map to improve patch sizes.");
+        } else {
+          System.err.println(" * The used package map is missing entries. The following default "
+              + "mappings have been used:");
+          Writer output = new OutputStreamWriter(System.err);
+          for (Entry<String, Integer> entry : newAssignments.entrySet()) {
+            output.write("    ");
+            PackageDistribution.formatEntry(entry, output);
+            output.write("\n");
+          }
+          output.flush();
+          System.err.println(" * Consider updating the map.");
+        }
+      }
+    }
+    if (packageDistribution != null || nameToFileMap.size() > 1) {
+      System.out.println(" - " + newPackageMap.toString());
+      PackageDistribution.writePackageToFileMap(newPackageMap, newAssignments, packageDistribution);
+    }
+    return nameToFileMap;
+  }
+
+  public static String deriveCommonPrefixAndSanityCheck(List<String> fileNames) {
+    Iterator<String> nameIterator = fileNames.iterator();
+    String first = nameIterator.next();
+    if (!first.toLowerCase().endsWith(FileUtils.DEX_EXTENSION)) {
+      throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
+    }
+    String prefix = first.substring(0, first.length() - FileUtils.DEX_EXTENSION.length());
+    int index = 2;
+    while (nameIterator.hasNext()) {
+      String next = nameIterator.next();
+      if (!next.toLowerCase().endsWith(FileUtils.DEX_EXTENSION)) {
+        throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
+      }
+      if (!next.startsWith(prefix)) {
+        throw new RuntimeException("Input filenames lack common prefix.");
+      }
+      String numberPart = next.substring(prefix.length(), next.length() - FileUtils.DEX_EXTENSION.length());
+      if (Integer.parseInt(numberPart) != index++) {
+        throw new RuntimeException("DEX files are not numbered consecutively.");
+      }
+    }
+    return prefix;
+  }
+
+  private static Map<DexProgramClass, String> computeOriginalNameMapping(
+      Collection<DexProgramClass> classes,
+      ClassNameMapper proguardMap) {
+    Map<DexProgramClass, String> originalNames = new HashMap<>();
+    classes.forEach((DexProgramClass c) ->
+        originalNames.put(c,
+            DescriptorUtils.descriptorToJavaType(c.type.toDescriptorString(), proguardMap)));
+    return originalNames;
+  }
+
+  private static TreeSet<DexProgramClass> sortClassesByPackage(Set<DexProgramClass> classes,
+      Map<DexProgramClass, String> originalNames) {
+    TreeSet<DexProgramClass> sortedClasses = new TreeSet<>(
+        (DexProgramClass a, DexProgramClass b) -> {
+      String originalA = originalNames.get(a);
+      String originalB = originalNames.get(b);
+      int indexA = originalA.lastIndexOf('.');
+      int indexB = originalB.lastIndexOf('.');
+      if (indexA == -1 && indexB == -1) {
+        // Empty package, compare the class names.
+        return originalA.compareTo(originalB);
+      }
+      if (indexA == -1) {
+        // Empty package name comes first.
+        return -1;
+      }
+      if (indexB == -1) {
+        // Empty package name comes first.
+        return 1;
+      }
+      String prefixA = originalA.substring(0, indexA);
+      String prefixB = originalB.substring(0, indexB);
+      int result = prefixA.compareTo(prefixB);
+      if (result != 0) {
+        return result;
+      }
+      return originalA.compareTo(originalB);
+    });
+    sortedClasses.addAll(classes);
+    return sortedClasses;
+  }
+
+  private static String extractPrefixToken(int prefixLength, String className, boolean addStar) {
+    int index = 0;
+    int lastIndex = 0;
+    int segmentCount = 0;
+    while (lastIndex != -1 && segmentCount++ < prefixLength) {
+      index = lastIndex;
+      lastIndex = className.indexOf('.', index + 1);
+    }
+    String prefix = className.substring(0, index);
+    if (addStar && segmentCount >= prefixLength) {
+      // Full match, add a * to also match sub-packages.
+      prefix += ".*";
+    }
+    return prefix;
+  }
+
+  public ObjectToOffsetMapping computeMapping(DexApplication application) {
+    assert transaction.isEmpty();
+    return new ObjectToOffsetMapping(
+        id,
+        application,
+        indexedItems.classes.toArray(new DexProgramClass[indexedItems.classes.size()]),
+        indexedItems.protos.toArray(new DexProto[indexedItems.protos.size()]),
+        indexedItems.types.toArray(new DexType[indexedItems.types.size()]),
+        indexedItems.methods.toArray(new DexMethod[indexedItems.methods.size()]),
+        indexedItems.fields.toArray(new DexField[indexedItems.fields.size()]),
+        indexedItems.strings.toArray(new DexString[indexedItems.strings.size()]),
+        indexedItems.callSites.toArray(new DexCallSite[indexedItems.callSites.size()]),
+        indexedItems.methodHandles.toArray(new DexMethodHandle[indexedItems.methodHandles.size()]));
+  }
+
+  private void addClass(DexProgramClass clazz) {
+    transaction.addClassAndDependencies(clazz);
+  }
+
+  private static boolean isFull(int numberOfMethods, int numberOfFields, int maximum) {
+    return (numberOfMethods > maximum) || (numberOfFields > maximum);
+  }
+
+  private boolean isFull() {
+    return isFull(transaction.getNumberOfMethods(), transaction.getNumberOfFields(), MAX_ENTRIES);
+  }
+
+  private boolean isFilledEnough(InternalOptions options) {
+    return isFull(
+        transaction.getNumberOfMethods(),
+        transaction.getNumberOfFields(),
+        options.fillDexFiles ? MAX_ENTRIES : MAX_PREFILL_ENTRIES);
+  }
+
+  public void abortTransaction() {
+    transaction.abort();
+  }
+
+  public void commitTransaction() {
+    transaction.commit();
+  }
+
+  public boolean isEmpty() {
+    return indexedItems.classes.isEmpty();
+  }
+
+  public List<DexProgramClass> classes() {
+    return indexedItems.classes;
+  }
+
+  private static class VirtualFileIndexedItemCollection implements IndexedItemCollection {
+
+    final int id;
+
+    private final List<DexProgramClass> classes = new ArrayList<>();
+    private final List<DexProto> protos = new ArrayList<>();
+    private final List<DexType> types = new ArrayList<>();
+    private final List<DexMethod> methods = new ArrayList<>();
+    private final List<DexField> fields = new ArrayList<>();
+    private final List<DexString> strings = new ArrayList<>();
+    private final List<DexCallSite> callSites = new ArrayList<>();
+    private final List<DexMethodHandle> methodHandles = new ArrayList<>();
+
+    private final Set<DexClass> seenClasses = Sets.newIdentityHashSet();
+
+    private VirtualFileIndexedItemCollection(int id) {
+      this.id = id;
+    }
+
+    private <T extends IndexedDexItem> boolean addItem(T item, List<T> itemList) {
+      assert item != null;
+      if (item.assignToVirtualFile(id)) {
+        itemList.add(item);
+        return true;
+      }
+      return false;
+    }
+
+    @Override
+    public boolean addClass(DexProgramClass clazz) {
+      if (seenClasses.add(clazz)) {
+        classes.add(clazz);
+        return true;
+      }
+      return false;
+    }
+
+    @Override
+    public boolean addField(DexField field) {
+      return addItem(field, fields);
+    }
+
+    @Override
+    public boolean addMethod(DexMethod method) {
+      return addItem(method, methods);
+    }
+
+    @Override
+    public boolean addString(DexString string) {
+      return addItem(string, strings);
+    }
+
+    @Override
+    public boolean addProto(DexProto proto) {
+      return addItem(proto, protos);
+    }
+
+    @Override
+    public boolean addCallSite(DexCallSite callSite) {
+      return addItem(callSite, callSites);
+    }
+
+    @Override
+    public boolean addMethodHandle(DexMethodHandle methodHandle) {
+      return addItem(methodHandle, methodHandles);
+    }
+
+    @Override
+    public boolean addType(DexType type) {
+      return addItem(type, types);
+    }
+
+    public int getNumberOfMethods() {
+      return methods.size();
+    }
+
+    public int getNumberOfFields() {
+      return fields.size();
+    }
+
+    public int getNumberOfStrings() {
+      return strings.size();
+    }
+  }
+
+  private static class IndexedItemTransaction implements IndexedItemCollection {
+
+    private final VirtualFileIndexedItemCollection base;
+    private final NamingLens namingLens;
+
+    private final Set<DexProgramClass> classes = new LinkedHashSet<>();
+    private final Set<DexField> fields = new LinkedHashSet<>();
+    private final Set<DexMethod> methods = new LinkedHashSet<>();
+    private final Set<DexType> types = new LinkedHashSet<>();
+    private final Set<DexProto> protos = new LinkedHashSet<>();
+    private final Set<DexString> strings = new LinkedHashSet<>();
+    private final Set<DexCallSite> callSites = new LinkedHashSet<>();
+    private final Set<DexMethodHandle> methodHandles = new LinkedHashSet<>();
+
+    private IndexedItemTransaction(VirtualFileIndexedItemCollection base,
+        NamingLens namingLens) {
+      this.base = base;
+      this.namingLens = namingLens;
+    }
+
+    private <T extends IndexedDexItem> boolean maybeInsert(T item, Set<T> set) {
+      if (item.hasVirtualFileData(base.id) || set.contains(item)) {
+        return false;
+      }
+      set.add(item);
+      return true;
+    }
+
+    void addClassAndDependencies(DexProgramClass clazz) {
+      clazz.collectIndexedItems(this);
+    }
+
+    @Override
+    public boolean addClass(DexProgramClass dexProgramClass) {
+      if (base.seenClasses.contains(dexProgramClass) || classes.contains(dexProgramClass)) {
+        return false;
+      }
+      classes.add(dexProgramClass);
+      return true;
+    }
+
+    @Override
+    public boolean addField(DexField field) {
+      return maybeInsert(field, fields);
+    }
+
+    @Override
+    public boolean addMethod(DexMethod method) {
+      return maybeInsert(method, methods);
+    }
+
+    @Override
+    public boolean addString(DexString string) {
+      return maybeInsert(string, strings);
+    }
+
+    @Override
+    public boolean addProto(DexProto proto) {
+      return maybeInsert(proto, protos);
+    }
+
+    @Override
+    public boolean addType(DexType type) {
+      return maybeInsert(type, types);
+    }
+
+    @Override
+    public boolean addCallSite(DexCallSite callSite) {
+      return maybeInsert(callSite, callSites);
+    }
+
+    @Override
+    public boolean addMethodHandle(DexMethodHandle methodHandle) {
+      return maybeInsert(methodHandle, methodHandles);
+    }
+
+    @Override
+    public DexString getRenamedDescriptor(DexType type) {
+      return namingLens.lookupDescriptor(type);
+    }
+
+    @Override
+    public DexString getRenamedName(DexMethod method) {
+      assert namingLens.checkTargetCanBeTranslated(method);
+      return namingLens.lookupName(method);
+    }
+
+    @Override
+    public DexString getRenamedName(DexField field) {
+      return namingLens.lookupName(field);
+    }
+
+    int getNumberOfMethods() {
+      return methods.size() + base.getNumberOfMethods();
+    }
+
+    int getNumberOfFields() {
+      return fields.size() + base.getNumberOfFields();
+    }
+
+    private <T extends DexItem> void commitItemsIn(Set<T> set, Function<T, Boolean> hook) {
+      set.forEach((item) -> {
+        boolean newlyAdded = hook.apply(item);
+        assert newlyAdded;
+      });
+      set.clear();
+    }
+
+    void commit() {
+      commitItemsIn(classes, base::addClass);
+      commitItemsIn(fields, base::addField);
+      commitItemsIn(methods, base::addMethod);
+      commitItemsIn(protos, base::addProto);
+      commitItemsIn(types, base::addType);
+      commitItemsIn(strings, base::addString);
+      commitItemsIn(callSites, base::addCallSite);
+      commitItemsIn(methodHandles, base::addMethodHandle);
+    }
+
+    void abort() {
+      classes.clear();
+      fields.clear();
+      methods.clear();
+      protos.clear();
+      types.clear();
+      strings.clear();
+    }
+
+    public boolean isEmpty() {
+      return classes.isEmpty() && fields.isEmpty() && methods.isEmpty() && protos.isEmpty()
+          && types.isEmpty() && strings.isEmpty();
+    }
+
+    int getNumberOfStrings() {
+      return strings.size() + base.getNumberOfStrings();
+    }
+
+    int getNumberOfClasses() {
+      return classes.size() + base.classes.size();
+    }
+  }
+
+  /**
+   * Adds all classes from the given set that are covered by a corresponding package map
+   * specification to the given file.
+   */
+  private static class PackageMapPopulator implements Callable<List<DexProgramClass>> {
+
+    private final VirtualFile file;
+    private final Collection<DexProgramClass> classes;
+    private final PackageDistribution packageDistribution;
+    private final Map<DexProgramClass, String> originalNames;
+
+    PackageMapPopulator(
+        VirtualFile file,
+        Collection<DexProgramClass> classes,
+        PackageDistribution packageDistribution,
+        Map<DexProgramClass, String> originalNames) {
+      this.file = file;
+      this.classes = classes;
+      this.packageDistribution = packageDistribution;
+      this.originalNames = originalNames;
+    }
+
+    public List<DexProgramClass> call() {
+      String currentPrefix = null;
+      int currentFileId = -1;
+      List<DexProgramClass> inserted = new ArrayList<>();
+      for (DexProgramClass clazz : classes) {
+        String originalName = originalNames.get(clazz);
+        assert originalName != null;
+        if (!coveredByPrefix(originalName, currentPrefix)) {
+          if (currentPrefix != null) {
+            file.commitTransaction();
+          }
+          currentPrefix = lookupPrefixFor(originalName);
+          if (currentPrefix == null) {
+            currentFileId = -1;
+          } else {
+            currentFileId = packageDistribution.get(currentPrefix);
+          }
+        }
+        if (currentFileId == file.id) {
+          file.addClass(clazz);
+          inserted.add(clazz);
+        }
+        if (file.isFull()) {
+          throw new RuntimeException(
+              "Cannot fit package " + currentPrefix
+                  + " in requested dex file, consider to remove mapping.");
+        }
+      }
+      file.commitTransaction();
+      return inserted;
+    }
+
+    private String lookupPrefixFor(String originalName) {
+      // First, check whether we have a match on the full package name.
+      int lastIndexOfDot = originalName.lastIndexOf('.');
+      if (lastIndexOfDot < 0) {
+        return null;
+      }
+      String prefix = originalName.substring(0, lastIndexOfDot);
+      if (packageDistribution.containsFile(prefix)) {
+        return prefix;
+      }
+      // Second, look for .* qualified entries.
+      int index;
+      prefix = originalName;
+      while ((index = prefix.lastIndexOf('.')) != -1) {
+        prefix = prefix.substring(0, index);
+        if (packageDistribution.containsFile(prefix + ".*")) {
+          return prefix + ".*";
+        }
+      }
+      return null;
+    }
+
+    static boolean coveredByPrefix(String originalName, String currentPrefix) {
+      if (currentPrefix == null) {
+        return false;
+      }
+      if (currentPrefix.endsWith(".*")) {
+        return originalName.startsWith(currentPrefix.substring(0, currentPrefix.length() - 2));
+      } else {
+        return originalName.startsWith(currentPrefix)
+            && originalName.lastIndexOf('.') == currentPrefix.length();
+      }
+    }
+  }
+
+  /**
+   * Distributes the given classes over the files in package order.
+   *
+   * <p>The populator avoids package splits. Big packages are split into subpackages if their size
+   * exceeds 20% of the dex file. This populator also avoids filling files completely to cater for
+   * future growth.
+   *
+   * <p>The populator cycles through the files until all classes have been successfully placed and
+   * adds new files to the passed in map if it can't fit in the existing files.
+   */
+  private static class PackageSplitPopulator implements Callable<Map<String, Integer>> {
+
+    /**
+     * Android suggests com.company.product for package names, so the components will be at level 4
+     */
+    private static final int MINIMUM_PREFIX_LENGTH = 4;
+    private static final int MAXIMUM_PREFIX_LENGTH = 7;
+    /**
+     * We allow 1/MIN_FILL_FACTOR of a file to remain empty when moving to the next file, i.e., a
+     * rollback with less than 1/MAX_FILL_FACTOR of the total classes in a file will move to the
+     * next file.
+     */
+    private static final int MIN_FILL_FACTOR = 5;
+
+    private final Map<Integer, VirtualFile> files;
+    private final List<DexProgramClass> classes;
+    private final Map<DexProgramClass, String> originalNames;
+    private final Set<String> previousPrefixes;
+    private final DexItemFactory dexItemFactory;
+    private final InternalOptions options;
+    private final NamingLens namingLens;
+
+    PackageSplitPopulator(
+        Map<Integer, VirtualFile> files,
+        Set<DexProgramClass> classes,
+        Map<DexProgramClass, String> originalNames,
+        Set<String> previousPrefixes,
+        DexItemFactory dexItemFactory,
+        InternalOptions options,
+        NamingLens namingLens) {
+      this.files = files;
+      this.classes = new ArrayList<>(classes);
+      this.originalNames = originalNames;
+      this.previousPrefixes = previousPrefixes;
+      this.dexItemFactory = dexItemFactory;
+      this.options = options;
+      this.namingLens = namingLens;
+    }
+
+    private String getOriginalName(DexProgramClass clazz) {
+      return originalNames != null ? originalNames.get(clazz) : clazz.toString();
+    }
+
+    public Map<String, Integer> call() throws IOException {
+      Iterator<VirtualFile> allFilesCyclic = Iterators.cycle(files.values());
+      Iterator<VirtualFile> activeFiles = Iterators.limit(allFilesCyclic, files.size());
+      int prefixLength = MINIMUM_PREFIX_LENGTH;
+      int transactionStartIndex = 0;
+      int fileStartIndex = 0;
+      String currentPrefix = null;
+      Map<String, Integer> newPackageAssignments = new LinkedHashMap<>();
+      VirtualFile current = activeFiles.next();
+      List<DexProgramClass> nonPackageClasses = new ArrayList<>();
+      for (int classIndex = 0; classIndex < classes.size(); classIndex++) {
+        DexProgramClass clazz = classes.get(classIndex);
+        String originalName = getOriginalName(clazz);
+        if (!PackageMapPopulator.coveredByPrefix(originalName, currentPrefix)) {
+          if (currentPrefix != null) {
+            current.commitTransaction();
+            // Reset the iterator to again iterate over all files, starting with the current one.
+            activeFiles = Iterators.limit(allFilesCyclic, files.size());
+            assert !newPackageAssignments.containsKey(currentPrefix);
+            newPackageAssignments.put(currentPrefix, current.id);
+            // Try to reduce the prefix length if possible. Only do this on a successful commit.
+            prefixLength = MINIMUM_PREFIX_LENGTH - 1;
+          }
+          String newPrefix;
+          // Also, we need to avoid new prefixes that are a prefix of previously used prefixes, as
+          // otherwise we might generate an overlap that will trigger problems when reusing the
+          // package mapping generated here. For example, if an existing map contained
+          //   com.android.foo.*
+          // but we now try to place some new subpackage
+          //   com.android.bar.*,
+          // we locally could use
+          //   com.android.*.
+          // However, when writing out the final package map, we get overlapping patterns
+          // com.android.* and com.android.foo.*.
+          do {
+            newPrefix = extractPrefixToken(++prefixLength, originalName, false);
+          } while (currentPrefix != null &&
+              (currentPrefix.startsWith(newPrefix)
+              || conflictsWithPreviousPrefix(newPrefix, originalName)));
+          // Don't set the current prefix if we did not extract one.
+          if (!newPrefix.equals("")) {
+            currentPrefix = extractPrefixToken(prefixLength, originalName, true);
+          }
+          transactionStartIndex = classIndex;
+        }
+        if (currentPrefix != null) {
+          assert clazz.superType != null || clazz.type == dexItemFactory.objectType;
+          current.addClass(clazz);
+        } else {
+          assert clazz.superType != null;
+          // We don't have a package, add this to a list of classes that we will add last.
+          assert current.transaction.isEmpty();
+          nonPackageClasses.add(clazz);
+          continue;
+        }
+        if (current.isFilledEnough(options) || current.isFull()) {
+          current.abortTransaction();
+          // We allow for a final rollback that has at most 20% of classes in it.
+          // This is a somewhat random number that was empirically chosen.
+          if (classIndex - transactionStartIndex > (classIndex - fileStartIndex) / MIN_FILL_FACTOR
+              && prefixLength < MAXIMUM_PREFIX_LENGTH) {
+            prefixLength++;
+          } else {
+            // Reset the state to after the last commit and cycle through files.
+            // The idea is that we do not increase the number of files, so it has to fit
+            // somewhere.
+            fileStartIndex = transactionStartIndex;
+            if (!activeFiles.hasNext()) {
+              // Special case where we simply will never be able to fit the current package into
+              // one dex file. This is currently the case for Strings in jumbo tests, see:
+              // b/33227518
+              if (current.transaction.getNumberOfClasses() == 0) {
+                for (int j = transactionStartIndex; j <= classIndex; j++) {
+                  nonPackageClasses.add(classes.get(j));
+                }
+                transactionStartIndex = classIndex + 1;
+              }
+              // All files are filled up to the 20% mark.
+              files.put(files.size(), new VirtualFile(files.size(), namingLens));
+              allFilesCyclic = Iterators.cycle(files.values());
+              activeFiles = Iterators.limit(allFilesCyclic, files.size());
+            }
+            current = activeFiles.next();
+          }
+          currentPrefix = null;
+          // Go back to previous start index.
+          classIndex = transactionStartIndex - 1;
+          assert current != null;
+        }
+      }
+      current.commitTransaction();
+      assert !newPackageAssignments.containsKey(currentPrefix);
+      if (currentPrefix != null) {
+        newPackageAssignments.put(currentPrefix, current.id);
+      }
+      if (nonPackageClasses.size() > 0) {
+        addNonPackageClasses(Iterators.limit(allFilesCyclic, files.size()), nonPackageClasses);
+      }
+      return newPackageAssignments;
+    }
+
+    private void addNonPackageClasses(Iterator<VirtualFile> activeFiles,
+        List<DexProgramClass> nonPackageClasses) throws IOException {
+      VirtualFile current;
+      current = activeFiles.next();
+      for (DexProgramClass clazz : nonPackageClasses) {
+        if (current.isFilledEnough(options)) {
+          current = getVirtualFile(activeFiles);
+        }
+        current.addClass(clazz);
+        while (current.isFull()) {
+          // This only happens if we have a huge class, that takes up more than 20% of a dex file.
+          current.abortTransaction();
+          current = getVirtualFile(activeFiles);
+          boolean wasEmpty = current.isEmpty();
+          current.addClass(clazz);
+          if (wasEmpty && current.isFull()) {
+            throw new InternalCompilerError(
+                "Class " + clazz.toString() + " does not fit into a single dex file.");
+          }
+        }
+        current.commitTransaction();
+      }
+    }
+
+    private VirtualFile getVirtualFile(Iterator<VirtualFile> activeFiles) throws IOException {
+      VirtualFile current = null;
+      while (activeFiles.hasNext() && (current = activeFiles.next()).isFilledEnough(options)) {}
+      if (current == null || current.isFilledEnough(options)) {
+        current = new VirtualFile(files.size(), namingLens);
+        files.put(files.size(), current);
+      }
+      return current;
+    }
+
+    private boolean conflictsWithPreviousPrefix(String newPrefix, String originalName) {
+      if (previousPrefixes == null) {
+        return false;
+      }
+      for (String previous : previousPrefixes) {
+        // Check whether a previous prefix starts with this new prefix and, if so,
+        // whether the new prefix already is maximal. So for example a new prefix of
+        //   foo.bar
+        // would match
+        //   foo.bar.goo.*
+        // However, if the original class is
+        //   foo.bar.X
+        // then this prefix is the best we can do, and will not turn into a .* prefix and
+        // thus does not conflict.
+        if (previous.startsWith(newPrefix)
+            && (originalName.lastIndexOf('.') > newPrefix.length())) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/CompilationError.java b/src/main/java/com/android/tools/r8/errors/CompilationError.java
new file mode 100644
index 0000000..060699e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/CompilationError.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal an compilation error.
+ *
+ * This is always an expected error and considered a user input issue.
+ * A user-understandable message must be provided.
+ */
+public class CompilationError extends RuntimeException {
+
+  public CompilationError(String message) {
+    super(message);
+  }
+
+  public CompilationError(String message, Throwable cause) {
+    super(message, cause);
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
new file mode 100644
index 0000000..b51c7ec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/InternalCompilerError.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal an unexpected state internally to the compiler.
+ *
+ * Exceptions of this type always represent an bug in the compiler.
+ * For expected errors, such as invalid input, the compiler should generate a CompilationError.
+  */
+public class InternalCompilerError extends IllegalStateException {
+
+  public InternalCompilerError() {
+  }
+
+  public InternalCompilerError(String s) {
+    super(s);
+  }
+
+  public InternalCompilerError(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public InternalCompilerError(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/Unimplemented.java b/src/main/java/com/android/tools/r8/errors/Unimplemented.java
new file mode 100644
index 0000000..f0acc65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/Unimplemented.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal that a not-yet-implemented code path has been hit.
+ */
+public class Unimplemented extends RuntimeException {
+
+  public Unimplemented() {
+  }
+
+  public Unimplemented(String message) {
+    super(message);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/Unreachable.java b/src/main/java/com/android/tools/r8/errors/Unreachable.java
new file mode 100644
index 0000000..4b17eba
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/Unreachable.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.errors;
+
+/**
+ * Exception to signal an expected unreachable code path.
+ */
+public class Unreachable extends InternalCompilerError {
+
+  public Unreachable() {
+  }
+
+  public Unreachable(String s) {
+    super(s);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
new file mode 100644
index 0000000..f5f22e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -0,0 +1,337 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiFunction;
+
+public class AppInfo {
+
+  public final DexItemFactory dexItemFactory;
+  final ImmutableMap<DexType, DexClassPromise> classMap;
+  private final ConcurrentHashMap<DexType, Map<Descriptor, KeyedDexItem>> definitions =
+      new ConcurrentHashMap<>();
+
+  public AppInfo(DexApplication application) {
+    this.dexItemFactory = application.dexItemFactory;
+    this.classMap = application.classMap;
+  }
+
+  protected AppInfo(AppInfo previous) {
+    this.dexItemFactory = previous.dexItemFactory;
+    this.classMap = previous.classMap;
+    this.definitions.putAll(previous.definitions);
+  }
+
+  protected AppInfo(AppInfo previous, GraphLense lense) {
+    // Do not rewrite basic structure, as the type information in the lense is about applied uses
+    // and not definitions.
+    this(previous);
+  }
+
+  private Map<Descriptor, KeyedDexItem> computeDefinitions(DexType type) {
+    Builder<Descriptor, KeyedDexItem> builder = ImmutableMap.builder();
+    DexClassPromise promise = classMap.get(type);
+    if (promise != null) {
+      DexClass clazz = promise.get();
+      registerDefinitions(builder, clazz.directMethods());
+      registerDefinitions(builder, clazz.virtualMethods());
+      registerDefinitions(builder, clazz.instanceFields());
+      registerDefinitions(builder, clazz.staticFields());
+    }
+    return builder.build();
+  }
+
+  private void registerDefinitions(Builder<Descriptor, KeyedDexItem> builder,
+      KeyedDexItem<? extends Descriptor>[] items) {
+    for (KeyedDexItem<? extends Descriptor> item : items) {
+      builder.put(item.getKey(), item);
+    }
+  }
+
+  public Iterable<DexProgramClass> classes() {
+    List<DexProgramClass> result = new ArrayList<>();
+    for (DexClassPromise promise : classMap.values()) {
+      if (promise.isProgramClass()) {
+        result.add(promise.get().asProgramClass());
+      }
+    }
+    return result;
+  }
+
+  public Iterable<DexLibraryClass> libraryClasses() {
+    List<DexLibraryClass> result = new ArrayList<>();
+    for (DexClassPromise promise : classMap.values()) {
+      if (promise.isLibraryClass()) {
+        result.add(promise.get().asLibraryClass());
+      }
+    }
+    return result;
+  }
+
+  public DexClass definitionFor(DexType type) {
+    DexClassPromise promise = classMap.get(type);
+    return promise == null ? null : promise.get();
+  }
+
+  public DexEncodedMethod definitionFor(DexMethod method) {
+    return (DexEncodedMethod) getDefinitions(method.getHolder()).get(method);
+  }
+
+  public DexEncodedField definitionFor(DexField field) {
+    return (DexEncodedField) getDefinitions(field.getHolder()).get(field);
+  }
+
+  private Map<Descriptor, KeyedDexItem> getDefinitions(DexType type) {
+    Map<Descriptor, KeyedDexItem> typeDefinitions = definitions.get(type);
+    if (typeDefinitions != null) {
+      return typeDefinitions;
+    }
+
+    typeDefinitions = computeDefinitions(type);
+    Map<Descriptor, KeyedDexItem> existing = definitions.putIfAbsent(type, typeDefinitions);
+    return existing != null ? existing : typeDefinitions;
+  }
+
+  private DexEncodedMethod lookupDirectStaticOrConstructorTarget(DexMethod method) {
+    assert method.holder.isClassType();
+    return lookupTargetAlongSuperChain(method.holder, method, DexClass::findDirectTarget);
+  }
+
+  /**
+   * Lookup static method following the super chain from the holder of {@code method}.
+   * <p>
+   * This method will lookup only static methods.
+   *
+   * @param method the method to lookup
+   * @return The actual target for {@code method} or {@code null} if none found.
+   */
+  public DexEncodedMethod lookupStaticTarget(DexMethod method) {
+    DexEncodedMethod target = lookupDirectStaticOrConstructorTarget(method);
+    return target == null || target.accessFlags.isStatic() ? target : null;
+  }
+
+  /**
+   * Lookup direct method following the super chain from the holder of {@code method}.
+   * <p>
+   * This method will lookup private and constructor methods.
+   *
+   * @param method the method to lookup
+   * @return The actual target for {@code method} or {@code null} if none found.
+   */
+  public DexEncodedMethod lookupDirectTarget(DexMethod method) {
+    DexEncodedMethod target = lookupDirectStaticOrConstructorTarget(method);
+    return target == null || !target.accessFlags.isStatic() ? target : null;
+  }
+
+  /**
+   * Lookup virtual method starting in type and following the super chain.
+   * <p>
+   * If the target cannot be found along the super-chain, look for a default implementation in one
+   * of the interfaces.
+   */
+  public DexEncodedMethod lookupVirtualTarget(DexType type, DexMethod method) {
+    assert type.isClassType();
+    DexEncodedMethod result
+        = lookupTargetAlongSuperChain(type, method, DexClass::findVirtualTarget);
+    if (result != null) {
+      return result;
+    }
+    return lookupTargetAlongInterfaceChain(type, method,
+        (dexClass, dexMethod) -> {
+          DexEncodedMethod virtualTarget = dexClass.findVirtualTarget(dexMethod);
+          return virtualTarget != null && virtualTarget.getCode() != null
+              ? virtualTarget
+              : null;
+        });
+  }
+
+  /**
+   * Lookup virtual method starting in type and following the super chain.
+   * <p>
+   * If the target cannot be found along the super-chain, look for a definition in one of
+   * the interfaces.
+   */
+  public DexEncodedMethod lookupVirtualDefinition(DexType type, DexMethod method) {
+    assert type.isClassType();
+    DexEncodedMethod result
+        = lookupTargetAlongSuperChain(type, method, DexClass::findVirtualTarget);
+    if (result != null) {
+      return result;
+    }
+    return lookupTargetAlongInterfaceChain(type, method, DexClass::findVirtualTarget);
+  }
+
+  /**
+   * Lookup instance field starting in type and following the super chain.
+   */
+  public DexEncodedField lookupInstanceTarget(DexType type, DexField field) {
+    assert type.isClassType();
+    return lookupTargetAlongSuperChain(type, field, DexClass::findInstanceTarget);
+  }
+
+  /**
+   * Lookup static field starting in type and following the super chain.
+   */
+  public DexEncodedField lookupStaticTarget(DexType type, DexField field) {
+    assert type.isClassType();
+    DexEncodedField target = lookupTargetAlongSuperChain(type, field, DexClass::findStaticTarget);
+    if (target == null) {
+      target = lookupTargetAlongInterfaceChain(type, field, DexClass::findStaticTarget);
+    }
+    return target;
+  }
+
+  /**
+   * Traverse along the super chain until lookup returns non-null value.
+   */
+  private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongSuperChain(
+      DexType type,
+      T desc,
+      BiFunction<DexClass, T, S> lookup) {
+    assert type != null;
+    DexClass holder = definitionFor(type);
+    while (holder != null) {
+      S result = lookup.apply(holder, desc);
+      if (result != null) {
+        return result;
+      }
+      if (holder.superType == null) {
+        return null;
+      }
+      holder = definitionFor(holder.superType);
+    }
+    return null;
+  }
+
+  /**
+   * Traverse along the super chain and the interface chains until lookup returns non-null value.
+   */
+  private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongSuperAndInterfaceChain(
+      DexType type,
+      T desc,
+      BiFunction<DexClass, T, S> lookup) {
+    DexClass holder = definitionFor(type);
+    if (holder == null) {
+      return null;
+    }
+    S result = lookup.apply(holder, desc);
+    if (result != null) {
+      return result;
+    }
+    if (holder.superType != null) {
+      result = lookupTargetAlongSuperAndInterfaceChain(holder.superType, desc, lookup);
+      if (result != null) {
+        return result;
+      }
+    }
+    for (DexType iface : holder.interfaces.values) {
+      result = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup);
+      if (result != null) {
+        return result;
+      }
+    }
+    return null;
+  }
+
+  private boolean isDefaultMethod(DexItem dexItem) {
+    return dexItem != null && dexItem instanceof DexEncodedMethod &&
+        !((DexEncodedMethod) dexItem).accessFlags.isStatic() &&
+        ((DexEncodedMethod) dexItem).getCode() != null;
+  }
+
+  private void checkIfMethodIsAmbiguous(DexItem previousResult, DexItem newResult) {
+    if (previousResult != null
+        && previousResult != newResult
+        && isDefaultMethod(previousResult)
+        && isDefaultMethod(newResult)) {
+      throw new CompilationError("Duplicate default methods named "
+          + previousResult.toSourceString()
+          + " are inherited from the types "
+          + ((DexEncodedMethod) previousResult).method.holder.getName()
+          + " and "
+          + ((DexEncodedMethod) newResult).method.holder.getName());
+    }
+  }
+
+  /**
+   * Traverse along the interface chains until lookup returns non-null value.
+   */
+  private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongInterfaceChain(
+      DexType type,
+      T desc,
+      BiFunction<DexClass, T, S> lookup) {
+    DexClass holder = definitionFor(type);
+    if (holder == null) {
+      // TODO(herhut): The subtype hierarchy is broken. Handle this case.
+      return null;
+    }
+    S result = null;
+    for (DexType iface : holder.interfaces.values) {
+      S localResult = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup);
+      if (localResult != null) {
+        checkIfMethodIsAmbiguous(result, localResult);
+        // Return the first item found, we only continue to detect ambiguous method call.
+        if (result == null) {
+          result = localResult;
+        }
+      }
+    }
+    if (holder.superType != null) {
+      S localResult = lookupTargetAlongInterfaceChain(holder.superType, desc, lookup);
+      if (localResult != null) {
+        checkIfMethodIsAmbiguous(result, localResult);
+        // Return the first item found, we only continue to detect ambiguous method call.
+        if (result == null) {
+          result = localResult;
+        }
+      }
+    }
+    return result;
+  }
+
+  public DexEncodedMethod lookup(Type type, DexMethod target) {
+    DexEncodedMethod definition;
+    DexType holder = target.getHolder();
+    if (!holder.isClassType()) {
+      return null;
+    }
+    if (type == Type.VIRTUAL || type == Type.INTERFACE) {
+      definition = lookupVirtualDefinition(holder, target);
+    } else if (type == Type.DIRECT) {
+      definition = lookupDirectTarget(target);
+    } else if (type == Type.STATIC) {
+      definition = lookupStaticTarget(target);
+    } else if (type == Type.SUPER) {
+      definition = lookupVirtualTarget(holder, target);
+    } else {
+      return null;
+    }
+    return definition;
+  }
+
+  public boolean hasSubtyping() {
+    return false;
+  }
+
+  public AppInfoWithSubtyping withSubtyping() {
+    return null;
+  }
+
+  public boolean hasLiveness() {
+    return false;
+  }
+
+  public AppInfoWithLiveness withLiveness() {
+    return null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
new file mode 100644
index 0000000..17754cc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -0,0 +1,240 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+public class AppInfoWithSubtyping extends AppInfo {
+
+  // Set of missing classes, discovered during subtypeMap computation.
+  private Set<DexType> missingClasses = Sets.newIdentityHashSet();
+  // Map from types to their subtypes.
+  private final Hashtable<DexType, ImmutableSet<DexType>> subtypeMap = new Hashtable<>();
+
+  public AppInfoWithSubtyping(DexApplication application) {
+    super(application);
+    populateSubtypeMap(application.classMap, application.dexItemFactory);
+  }
+
+  protected AppInfoWithSubtyping(AppInfoWithSubtyping previous) {
+    super(previous);
+    missingClasses.addAll(previous.missingClasses);
+    subtypeMap.putAll(previous.subtypeMap);
+  }
+
+  protected AppInfoWithSubtyping(AppInfoWithSubtyping previous, GraphLense lense) {
+    super(previous, lense);
+    // Recompute subtype map if we have modified the graph.
+    populateSubtypeMap(previous.classMap, dexItemFactory);
+  }
+
+  public Set<DexType> getMissingClasses() {
+    return Collections.unmodifiableSet(missingClasses);
+  }
+
+  public ImmutableSet<DexType> subtypes(DexType type) {
+    assert type.isClassType();
+    ImmutableSet<DexType> subtypes = subtypeMap.get(type);
+    return subtypes == null ? ImmutableSet.of() : subtypes;
+  }
+
+  private void populateSuperType(Hashtable<DexType, Set<DexType>> map, DexType superType,
+      DexClass baseClass, Function<DexType, DexClass> definitions) {
+    if (superType != null) {
+      Set<DexType> set = map.computeIfAbsent(superType, ignore -> new HashSet<>());
+      if (set.add(baseClass.type)) {
+        // Only continue recursion if type has been added to set.
+        populateAllSuperTypes(map, superType, baseClass, definitions);
+      }
+    }
+  }
+
+  private void populateAllSuperTypes(Hashtable<DexType, Set<DexType>> map, DexType holder,
+      DexClass baseClass, Function<DexType, DexClass> definitions) {
+    DexClass holderClass = definitions.apply(holder);
+    // Skip if no corresponding class is found.
+    if (holderClass != null) {
+      populateSuperType(map, holderClass.superType, baseClass, definitions);
+      if (holderClass.superType != null) {
+        holderClass.superType.addDirectSubtype(holder);
+      } else {
+        // We found java.lang.Object
+        assert dexItemFactory.objectType == holder;
+      }
+      for (DexType inter : holderClass.interfaces.values) {
+        populateSuperType(map, inter, baseClass, definitions);
+        inter.addInterfaceSubtype(holder);
+      }
+    } else {
+      if (!baseClass.isLibraryClass()) {
+        missingClasses.add(holder);
+      }
+      // The subtype chain is broken, at least make this type a subtype of Object.
+      if (holder != dexItemFactory.objectType) {
+        dexItemFactory.objectType.addDirectSubtype(holder);
+      }
+    }
+  }
+
+  private void populateSubtypeMap(
+      Map<DexType, DexClassPromise> classes, DexItemFactory dexItemFactory) {
+    dexItemFactory.clearSubtypeInformation();
+    dexItemFactory.objectType.tagAsSubtypeRoot();
+    Hashtable<DexType, Set<DexType>> map = new Hashtable<>();
+    Function<DexType, DexClass> typeToClass = type -> {
+      DexClassPromise promise = classes.get(type);
+      return promise == null ? null : promise.get();
+    };
+    for (Map.Entry<DexType, DexClassPromise> entry : classes.entrySet()) {
+      populateAllSuperTypes(map, entry.getKey(), entry.getValue().get(), typeToClass);
+    }
+    for (Map.Entry<DexType, Set<DexType>> entry : map.entrySet()) {
+      subtypeMap.put(entry.getKey(), ImmutableSet.copyOf(entry.getValue()));
+    }
+    assert DexType.validateLevelsAreCorrect(typeToClass, dexItemFactory);
+  }
+
+  // For mapping invoke virtual instruction to target methods.
+  public Set<DexEncodedMethod> lookupVirtualTargets(DexMethod method) {
+    Set<DexEncodedMethod> result = new HashSet<>();
+    // First add the target for receiver type method.type.
+    DexClass root = definitionFor(method.holder);
+    if (root == null) {
+      // type specified in method does not have a materialized class.
+      return null;
+    }
+    DexEncodedMethod topMethod = lookupVirtualTarget(method.holder, method);
+    // The top method might be absent if this is an abstract class.
+    if (topMethod != null) {
+      result.add(topMethod);
+    } else {
+      if (!holderIsAbstract(method)) {
+        // This method is missing and the program we have is invalid.
+        // TODO(herhut): Find a better way to handle missing targets.
+        return Collections.emptySet();
+      }
+    }
+    // Add all matching targets from the subclass hierarchy.
+    Set<DexType> set = subtypes(method.holder);
+    if (set != null) {
+      for (DexType type : set) {
+        DexClass clazz = definitionFor(type);
+        if (!clazz.isInterface()) {
+          DexEncodedMethod t = clazz.findVirtualTarget(method);
+          if (t != null) {
+            result.add(t);
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * For mapping invoke virtual instruction to single target method.
+   */
+  public DexEncodedMethod lookupSingleVirtualTarget(DexMethod method) {
+    assert method != null;
+    if (method.isSingleVirtualMethodCached()) {
+      return method.getSingleVirtualMethodCache();
+    }
+    DexEncodedMethod result = null;
+    // First add the target for receiver type method.type.
+    DexClass root = definitionFor(method.holder);
+    if (root == null) {
+      // type specified in method does not have a materialized class.
+      return null;
+    }
+    DexEncodedMethod topMethod = lookupVirtualTarget(method.holder, method);
+    // The top method might be absent if this is an abstract class.
+    if (topMethod != null) {
+      result = topMethod;
+    } else {
+      if (!holderIsAbstract(method)) {
+        return null;
+      }
+    }
+    // Search for matching target in subtype hierarchy.
+    Set<DexType> set = subtypes(method.holder);
+    if (set != null) {
+      for (DexType type : set) {
+        DexClass clazz = definitionFor(type);
+        if (!clazz.isInterface()) {
+          DexEncodedMethod t = clazz.findVirtualTarget(method);
+          if (t != null) {
+            if (result != null) {
+              return null;  // We have more than one target method.
+            } else {
+              result = t;
+            }
+          }
+        }
+      }
+    }
+    method.setSingleVirtualMethodCache(result);
+    return result;
+  }
+
+  private boolean holderIsAbstract(Descriptor desc) {
+    DexClass holder = definitionFor(desc.getHolder());
+    return holder.accessFlags.isAbstract();
+  }
+
+  // For mapping invoke interface instruction to target methods.
+  public Set<DexEncodedMethod> lookupInterfaceTargets(DexMethod method) {
+    Set<DexEncodedMethod> result = new HashSet<>();
+    Set<DexType> set = subtypes(method.holder);
+    if (set != null) {
+      for (DexType type : set) {
+        DexClass clazz = definitionFor(type);
+        if (!clazz.isInterface()) {
+          DexEncodedMethod targetMethod = lookupVirtualTarget(type, method);
+          if (targetMethod != null) {
+            result.add(targetMethod);
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  public DexEncodedMethod lookupSingleInterfaceTarget(DexMethod method) {
+    assert method != null;
+    DexEncodedMethod result = null;
+    Set<DexType> set = subtypes(method.holder);
+    if (set != null) {
+      for (DexType type : set) {
+        DexClass clazz = definitionFor(type);
+        if (!clazz.isInterface()) {
+          DexEncodedMethod t = lookupVirtualTarget(type, method);
+          if (t != null) {
+            if (result != null) {
+              return null;
+            } else {
+              result = t;
+            }
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public boolean hasSubtyping() {
+    return true;
+  }
+
+  @Override
+  public AppInfoWithSubtyping withSubtyping() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java b/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java
new file mode 100644
index 0000000..7872801
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/CanonicalizedDexItem.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+/**
+ * DexItems of this kind have to be canonicalized for the whole application.
+ */
+public abstract class CanonicalizedDexItem extends DexItem {
+
+  private static final int NOT_COMPUTED_HASH_VALUE = -1;
+  private static final int SENTINEL_HASH_VALUE = 0;
+  private volatile int hash = NOT_COMPUTED_HASH_VALUE;
+
+  protected abstract int computeHashCode();
+
+  protected abstract boolean computeEquals(Object other);
+
+  @Override
+  public final int hashCode() {
+    int cache = hash;
+    if (cache == NOT_COMPUTED_HASH_VALUE) {
+      cache = computeHashCode();
+      if (cache == NOT_COMPUTED_HASH_VALUE) {
+        cache = SENTINEL_HASH_VALUE;
+      }
+      hash = cache;
+    }
+    return cache;
+  }
+
+  @Override
+  public void flushCachedValues() {
+    super.flushCachedValues();
+    hash = NOT_COMPUTED_HASH_VALUE;
+  }
+
+  @Override
+  public final boolean equals(Object other) {
+    return this == other || computeEquals(other);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
new file mode 100644
index 0000000..e2e972c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ClassAndMemberPublicizer.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class ClassAndMemberPublicizer {
+
+  private static void publicizeAllMethods(DexEncodedMethod[] methods) {
+    for (DexEncodedMethod method : methods) {
+      method.accessFlags.promoteToPublic();
+    }
+  }
+
+  private static void publicizeAllFields(DexEncodedField[] fields) {
+    for (DexEncodedField field : fields) {
+      field.accessFlags.promoteToPublic();
+    }
+  }
+
+  /**
+   * Marks all package private and protected methods and fields as public.
+   * <p>
+   * This will destructively update the DexApplication passed in as argument.
+   */
+  public static DexApplication run(DexApplication application) {
+    for (DexClass clazz : application.classes()) {
+      clazz.accessFlags.promoteToPublic();
+      publicizeAllFields(clazz.staticFields());
+      publicizeAllFields(clazz.instanceFields());
+      publicizeAllMethods(clazz.directMethods());
+      publicizeAllMethods(clazz.virtualMethods());
+    }
+    return application;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
new file mode 100644
index 0000000..04c01be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.optimize.Outliner.OutlineCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+
+public abstract class Code extends CanonicalizedDexItem {
+
+  public abstract IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options);
+
+  public abstract void registerReachableDefinitions(UseRegistry registry);
+
+  public abstract String toString();
+
+  public abstract String toString(ClassNameMapper naming);
+
+  public boolean isDexCode() {
+    return false;
+  }
+
+  public boolean isJarCode() {
+    return false;
+  }
+
+  public boolean isOutlineCode() {
+    return false;
+  }
+
+  public DexCode asDexCode() {
+    throw new Unreachable();
+  }
+
+  public JarCode asJarCode() {
+    throw new Unreachable();
+  }
+
+  public OutlineCode asOutlineCode() {
+    throw new Unreachable();
+  }
+
+  @Override
+  void collectIndexedItems(IndexedItemCollection collection) {
+    throw new Unreachable();
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection collection) {
+    throw new Unreachable();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
new file mode 100644
index 0000000..4d7d120
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public class DebugLocalInfo {
+  public final DexString name;
+  public final DexType type;
+  public final DexString signature;
+
+  public DebugLocalInfo(DexString name, DexType type, DexString signature) {
+    this.name = name;
+    this.type = type;
+    this.signature = signature;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (!(other instanceof DebugLocalInfo)) {
+      return false;
+    }
+    DebugLocalInfo o = (DebugLocalInfo) other;
+    return name == o.name && type == o.type && signature == o.signature;
+  }
+
+  @Override
+  public int hashCode() {
+    int hash = 7 * name.hashCode() + 13 * type.hashCode();
+    if (signature != null) {
+      hash += 31 * signature.hashCode();
+    }
+    return hash;
+  }
+
+  @Override
+  public String toString() {
+    return name + ":" + type + (signature == null ? "" : signature);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Descriptor.java b/src/main/java/com/android/tools/r8/graph/Descriptor.java
new file mode 100644
index 0000000..9065839
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Descriptor.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class Descriptor<T extends DexItem, S extends Descriptor>
+    extends IndexedDexItem implements PresortedComparable<S> {
+
+  public abstract boolean match(T entry);
+
+  public abstract DexType getHolder();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java b/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
new file mode 100644
index 0000000..e145158
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAccessFlags.java
@@ -0,0 +1,289 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+
+public class DexAccessFlags {
+
+  private static final String[] ACC_NAMES = {
+      "public",
+      "private",
+      "protected",
+      "static",
+      "final",
+      "synchronized",
+      "volatile(bridge)",
+      "transient(varargs)",
+      "native",
+      "interface",
+      "abstract",
+      "strictfp",
+      "synthetic",
+      "annotation",
+      "enum",
+      "<unused>",
+      "<init>",
+      "synchronized",
+  };
+
+  private int flags;
+
+  public DexAccessFlags(int flags) {
+    this.flags = flags;
+  }
+
+  public DexAccessFlags(int... flags) {
+    this(combineFlags(flags));
+  }
+
+  private static int combineFlags(int[] flags) {
+    int combined = 0;
+    for (int flag : flags) {
+      combined |= flag;
+    }
+    return combined;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other instanceof DexAccessFlags) {
+      return flags == ((DexAccessFlags) other).flags;
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return get();
+  }
+
+  public int get() {
+    return flags;
+  }
+
+  public boolean containsAllOf(DexAccessFlags other) {
+    return (flags & other.get()) == other.get();
+  }
+
+  public boolean containsNoneOf(DexAccessFlags other) {
+    return (flags & other.get()) == 0;
+  }
+
+  public boolean isPublic() {
+    return isSet(Constants.ACC_PUBLIC);
+  }
+
+  public void setPublic() {
+    set(Constants.ACC_PUBLIC);
+  }
+
+  public boolean isPrivate() {
+    return isSet(Constants.ACC_PRIVATE);
+  }
+
+  public void setPrivate() {
+    set(Constants.ACC_PRIVATE);
+  }
+
+  public void unsetPrivate() {
+    unset(Constants.ACC_PRIVATE);
+  }
+
+  public boolean isProtected() {
+    return isSet(Constants.ACC_PROTECTED);
+  }
+
+  public void setProtected() {
+    set(Constants.ACC_PROTECTED);
+  }
+
+  public boolean isStatic() {
+    return isSet(Constants.ACC_STATIC);
+  }
+
+  public void setStatic() {
+    set(Constants.ACC_STATIC);
+  }
+
+  public boolean isFinal() {
+    return isSet(Constants.ACC_FINAL);
+  }
+
+  public void setFinal() {
+    set(Constants.ACC_FINAL);
+  }
+
+  public boolean isSynchronized() {
+    return isSet(Constants.ACC_SYNCHRONIZED);
+  }
+
+  public void setSynchronized() {
+    set(Constants.ACC_SYNCHRONIZED);
+  }
+
+  public void unsetSynchronized() {
+    unset(Constants.ACC_SYNCHRONIZED);
+  }
+
+  public boolean isVolatile() {
+    return isSet(Constants.ACC_VOLATILE);
+  }
+
+  public void setVolatile() {
+    set(Constants.ACC_VOLATILE);
+  }
+
+  public boolean isBridge() {
+    return isSet(Constants.ACC_BRIDGE);
+  }
+
+  public void setBridge() {
+    set(Constants.ACC_BRIDGE);
+  }
+
+  public void unsetBridge() {
+    unset(Constants.ACC_BRIDGE);
+  }
+
+  public boolean isTransient() {
+    return isSet(Constants.ACC_TRANSIENT);
+  }
+
+  public void setTransient() {
+    set(Constants.ACC_TRANSIENT);
+  }
+
+  public boolean isVarargs() {
+    return isSet(Constants.ACC_VARARGS);
+  }
+
+  public void setVarargs() {
+    set(Constants.ACC_VARARGS);
+  }
+
+  public boolean isNative() {
+    return isSet(Constants.ACC_NATIVE);
+  }
+
+  public void setNative() {
+    set(Constants.ACC_NATIVE);
+  }
+
+  public boolean isInterface() {
+    return isSet(Constants.ACC_INTERFACE);
+  }
+
+  public void setInterface() {
+    set(Constants.ACC_INTERFACE);
+  }
+
+  public void unsetInterface() {
+    unset(Constants.ACC_INTERFACE);
+  }
+
+  public boolean isAbstract() {
+    return isSet(Constants.ACC_ABSTRACT);
+  }
+
+  public void setAbstract() {
+    set(Constants.ACC_ABSTRACT);
+  }
+
+  public void unsetAbstract() {
+    unset(Constants.ACC_ABSTRACT);
+  }
+
+  public boolean isStrict() {
+    return isSet(Constants.ACC_STRICT);
+  }
+
+  public void setStrict() {
+    set(Constants.ACC_STRICT);
+  }
+
+  public boolean isSynthetic() {
+    return isSet(Constants.ACC_SYNTHETIC);
+  }
+
+  public void setSynthetic() {
+    set(Constants.ACC_SYNTHETIC);
+  }
+
+  public void unsetSynthetic() {
+    unset(Constants.ACC_SYNTHETIC);
+  }
+
+  public boolean isAnnotation() {
+    return isSet(Constants.ACC_ANNOTATION);
+  }
+
+  public void setAnnotation() {
+    set(Constants.ACC_ANNOTATION);
+  }
+
+  public boolean isEnum() {
+    return isSet(Constants.ACC_ENUM);
+  }
+
+  public void setEnum() {
+    set(Constants.ACC_ENUM);
+  }
+
+  public boolean isConstructor() {
+    return isSet(Constants.ACC_CONSTRUCTOR);
+  }
+
+  public void setConstructor() {
+    set(Constants.ACC_CONSTRUCTOR);
+  }
+
+  public boolean isDeclaredSynchronized() {
+    return isSet(Constants.ACC_DECLARED_SYNCHRONIZED);
+  }
+
+  public void setDeclaredSynchronized() {
+    set(Constants.ACC_DECLARED_SYNCHRONIZED);
+  }
+
+  public void promoteToPublic() {
+    if (!isPrivate()) {
+      flags &= ~Constants.ACC_PROTECTED;
+      flags |= Constants.ACC_PUBLIC;
+    }
+  }
+
+  private boolean isSet(int flag) {
+    return (flags & flag) != 0;
+  }
+
+  private void set(int flag) {
+    flags |= flag;
+  }
+
+  private void unset(int flag) {
+    flags &= ~flag;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    int flags = this.flags;
+    flags &= ~Constants.ACC_CONSTRUCTOR;  // Don't include the constructor flag in the string.
+    for (int i = 0; i < ACC_NAMES.length && flags != 0; i++, flags >>= 1) {
+      if ((flags & 0x1) != 0) {
+        if (builder.length() > 0) {
+          builder.append(' ');
+        }
+        builder.append(ACC_NAMES[i]);
+      }
+    }
+    assert flags == 0;
+    return builder.toString();
+  }
+
+  public String toSmaliString() {
+    return toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
new file mode 100644
index 0000000..d355ffd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -0,0 +1,262 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueMethod;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DexAnnotation extends DexItem {
+  // Dex system annotations.
+  // See https://source.android.com/devices/tech/dalvik/dex-format.html#system-annotation
+  private static final String ANNOTATION_DEFAULT_DESCRIPTOR =
+      "Ldalvik/annotation/AnnotationDefault;";
+  private static final String ENCLOSING_CLASS_DESCRIPTOR = "Ldalvik/annotation/EnclosingClass;";
+  private static final String ENCLOSING_METHOD_DESCRIPTOR = "Ldalvik/annotation/EnclosingMethod;";
+  private static final String INNER_CLASS_DESCRIPTOR = "Ldalvik/annotation/InnerClass;";
+  private static final String MEMBER_CLASSES_DESCRIPTOR = "Ldalvik/annotation/MemberClasses;";
+  private static final String METHOD_PARAMETERS_DESCRIPTOR = "Ldalvik/annotation/MethodParameters;";
+  private static final String SIGNATURE_DESCRIPTOR = "Ldalvik/annotation/Signature;";
+  private static final String SOURCE_DEBUG_EXTENSION = "Ldalvik/annotation/SourceDebugExtension;";
+  private static final String THROWS_DESCRIPTOR = "Ldalvik/annotation/Throws;";
+
+  public static final int VISIBILITY_BUILD = 0x00;
+  public static final int VISIBILITY_RUNTIME = 0x01;
+  public static final int VISIBILITY_SYSTEM = 0x02;
+  public final int visibility;
+  public final DexEncodedAnnotation annotation;
+
+  public DexAnnotation(int visibility, DexEncodedAnnotation annotation) {
+    this.visibility = visibility;
+    this.annotation = annotation;
+  }
+
+  @Override
+  public int hashCode() {
+    return visibility + annotation.hashCode() * 3;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof DexAnnotation) {
+      DexAnnotation o = (DexAnnotation) other;
+      return (visibility == o.visibility) && annotation.equals(o.annotation);
+    }
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    return visibility + " " + annotation;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    annotation.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    mixedItems.add(this);
+  }
+
+  public static DexAnnotation createEnclosingClassAnnotation(DexType enclosingClass,
+      DexItemFactory factory) {
+    return createSystemValueAnnotation(ENCLOSING_CLASS_DESCRIPTOR, factory,
+        new DexValueType(enclosingClass));
+  }
+
+  public static DexAnnotation createEnclosingMethodAnnotation(DexMethod enclosingMethod,
+      DexItemFactory factory) {
+    return createSystemValueAnnotation(ENCLOSING_METHOD_DESCRIPTOR, factory,
+        new DexValueMethod(enclosingMethod));
+  }
+
+  public static boolean isEnclosingClassAnnotation(DexAnnotation annotation) {
+    return annotation.annotation.type.toDescriptorString().equals(ENCLOSING_CLASS_DESCRIPTOR);
+  }
+
+  public static boolean isEnclosingMethodAnnotation(DexAnnotation annotation) {
+    return annotation.annotation.type.toDescriptorString().equals(ENCLOSING_METHOD_DESCRIPTOR);
+  }
+
+  public static boolean isEnclosingAnnotation(DexAnnotation annotation) {
+    return isEnclosingClassAnnotation(annotation) || isEnclosingMethodAnnotation(annotation);
+  }
+
+  public static boolean isInnerClassesAnnotation(DexAnnotation annotation) {
+    return annotation.annotation.type.toDescriptorString().equals(MEMBER_CLASSES_DESCRIPTOR)
+        || annotation.annotation.type.toDescriptorString().equals(INNER_CLASS_DESCRIPTOR);
+  }
+
+  public static DexAnnotation createInnerClassAnnotation(String clazz, int access,
+      DexItemFactory factory) {
+    return new DexAnnotation(VISIBILITY_SYSTEM,
+        new DexEncodedAnnotation(factory.createType(INNER_CLASS_DESCRIPTOR),
+            new DexAnnotationElement[]{
+                new DexAnnotationElement(
+                    factory.createString("accessFlags"),
+                    DexValueInt.create(access)),
+                new DexAnnotationElement(
+                    factory.createString("name"),
+                    (clazz == null)
+                        ? DexValue.NULL
+                        : new DexValueString(factory.createString(clazz)))
+            }));
+  }
+
+  public static DexAnnotation createMemberClassesAnnotation(List<DexType> classes,
+      DexItemFactory factory) {
+    DexValue[] values = new DexValue[classes.size()];
+    for (int i = 0; i < classes.size(); i++) {
+      values[i] = new DexValueType(classes.get(i));
+    }
+    return createSystemValueAnnotation(MEMBER_CLASSES_DESCRIPTOR, factory,
+        new DexValueArray(values));
+  }
+
+  public static DexAnnotation createSourceDebugExtensionAnnotation(DexValue value,
+      DexItemFactory factory) {
+    return new DexAnnotation(VISIBILITY_SYSTEM,
+        new DexEncodedAnnotation(factory.createType(SOURCE_DEBUG_EXTENSION),
+            new DexAnnotationElement[] {
+              new DexAnnotationElement(factory.createString("value"), value)
+            }));
+  }
+
+  public static DexAnnotation createMethodParametersAnnotation(DexValue[] names,
+      DexValue[] accessFlags, DexItemFactory factory) {
+    assert names.length == accessFlags.length;
+    return new DexAnnotation(VISIBILITY_SYSTEM,
+        new DexEncodedAnnotation(factory.createType(METHOD_PARAMETERS_DESCRIPTOR),
+            new DexAnnotationElement[]{
+                new DexAnnotationElement(
+                    factory.createString("names"),
+                    new DexValueArray(names)),
+                new DexAnnotationElement(
+                    factory.createString("accessFlags"),
+                    new DexValueArray(accessFlags))
+            }));
+  }
+
+  public static DexAnnotation createAnnotationDefaultAnnotation(DexType type,
+      List<DexAnnotationElement> defaults, DexItemFactory factory) {
+    return createSystemValueAnnotation(ANNOTATION_DEFAULT_DESCRIPTOR, factory,
+        new DexValueAnnotation(
+            new DexEncodedAnnotation(type,
+                defaults.toArray(new DexAnnotationElement[defaults.size()])))
+    );
+  }
+
+  public static DexAnnotation createSignatureAnnotation(String signature, DexItemFactory factory) {
+    return createSystemValueAnnotation(SIGNATURE_DESCRIPTOR, factory,
+        compressSignature(signature, factory));
+  }
+
+  public static DexAnnotation createThrowsAnnotation(DexValue[] exceptions,
+      DexItemFactory factory) {
+    return createSystemValueAnnotation(THROWS_DESCRIPTOR, factory, new DexValueArray(exceptions));
+  }
+
+  private static DexAnnotation createSystemValueAnnotation(String desc, DexItemFactory factory,
+      DexValue value) {
+    return new DexAnnotation(VISIBILITY_SYSTEM,
+        new DexEncodedAnnotation(factory.createType(desc), new DexAnnotationElement[] {
+            new DexAnnotationElement(factory.createString("value"), value)
+        }));
+  }
+
+  public static boolean isThrowingAnnotation(DexAnnotation annotation) {
+    return annotation.annotation.type.toDescriptorString().equals(THROWS_DESCRIPTOR);
+  }
+
+  public static boolean isSignatureAnnotation(DexAnnotation annotation) {
+    return annotation.annotation.type.toDescriptorString().equals(SIGNATURE_DESCRIPTOR);
+  }
+
+
+  public static boolean isSourceDebugExtension(DexAnnotation annotation) {
+    return annotation.annotation.type.toDescriptorString().equals(SOURCE_DEBUG_EXTENSION);
+  }
+
+  /**
+   * As a simple heuristic for compressing a signature, we locate all starting points of qualified
+   * names and of inner classes (started by an L or $) and make them individual parts, potentially
+   * splitting the qualified name into its package prefix and class name components. All other
+   * parts of the signature are simply grouped and separate the names.
+   * For examples, "()Ljava/lang/List<Lfoo/bar/Baz;>;" splits into:
+   * <pre>
+   *   ["()", "Ljava/lang/List<", "Lfoo/bar/", "Baz;", ">;"]
+   * </pre>
+   * We don't split classes in java and android since they are very frequent and the added string
+   * payload is outweighed by the reduced number of parts.
+   */
+  private static DexValue compressSignature(String signature, DexItemFactory factory) {
+    final int length = signature.length();
+    List<DexValue> parts = new ArrayList<>();
+    int previous = 0;
+    int index = 0;
+    while (index < length) {
+      char c = signature.charAt(index);
+      if (c == 'L' || c == '$') {
+        if (previous < index) {
+          parts.add(toDexValue(signature.substring(previous, index), factory));
+          previous = index;
+        }
+        int startOfClassName = index;
+        ++index;
+        while (index < length && isAlphaNumericPath(index, signature)) {
+          ++index;
+          // Record the last separator.
+          if (signature.charAt(index - 1) == '/') {
+            startOfClassName = index;
+          }
+        }
+        // Include the termination char in the part (this will typically be duplicated).
+        int lastChar = signature.charAt(index);
+        if (lastChar == ';' || lastChar == '<') {
+          ++index;
+        }
+        if (splitQualifiedName(previous, index, startOfClassName, signature)) {
+          parts.add(toDexValue(signature.substring(previous, startOfClassName), factory));
+          parts.add(toDexValue(signature.substring(startOfClassName, index), factory));
+        } else {
+          parts.add(toDexValue(signature.substring(previous, index), factory));
+        }
+        previous = index;
+      } else {
+        ++index;
+      }
+    }
+    if (previous < index) {
+      parts.add(toDexValue(signature.substring(previous, index), factory));
+    }
+    return new DexValueArray(parts.toArray(new DexValue[parts.size()]));
+  }
+
+  private static boolean isAlphaNumericPath(int position, String data) {
+    char c = data.charAt(position);
+    return c == '/' || Character.isLetterOrDigit(c);
+  }
+
+  private static boolean splitQualifiedName(int start, int end, int classStart, String signature) {
+    return start < classStart && classStart < end - 1
+        && !signature.startsWith("Landroid/", start)
+        && !signature.startsWith("Ljava/", start);
+  }
+
+  private static DexValue toDexValue(String string, DexItemFactory factory) {
+    return new DexValueString(factory.createString(string));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
new file mode 100644
index 0000000..c55ea05
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationElement.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+
+public class DexAnnotationElement extends DexItem {
+
+  public final DexString name;
+  public final DexValue value;
+
+  public DexAnnotationElement(DexString name, DexValue value) {
+    this.name = name;
+    this.value = value;
+  }
+
+  @Override
+  public int hashCode() {
+    return name.hashCode() + value.hashCode() * 3;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof DexAnnotationElement) {
+      DexAnnotationElement o = (DexAnnotationElement) other;
+      return name.equals(o.name) && value.equals(o.value);
+    }
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    return name + "=" + value;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    name.collectIndexedItems(indexedItems);
+    value.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Should never be visited.
+    assert false;
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
new file mode 100644
index 0000000..04e94d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexAnnotationSet extends DexItem {
+
+  private static final int UNSORTED = 0;
+  private static final DexAnnotationSet THE_EMPTY_ANNOTATIONS_SET =
+      new DexAnnotationSet(new DexAnnotation[0]);
+
+  public final DexAnnotation[] annotations;
+  private int sorted = UNSORTED;
+
+  public DexAnnotationSet(DexAnnotation[] annotations) {
+    this.annotations = annotations;
+  }
+
+  public static DexAnnotationSet empty() {
+    return THE_EMPTY_ANNOTATIONS_SET;
+  }
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(annotations);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof DexAnnotationSet) {
+      DexAnnotationSet o = (DexAnnotationSet) other;
+      return Arrays.equals(annotations, o.annotations);
+    }
+    return false;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    collectAll(indexedItems, annotations);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    mixedItems.add(this);
+    collectAll(mixedItems, annotations);
+  }
+
+  public boolean isEmpty() {
+    return annotations.length == 0;
+  }
+
+  public void sort() {
+    if (sorted != UNSORTED) {
+      assert sorted == sortedHashCode();
+      return;
+    }
+    Arrays.sort(annotations, (a, b) -> a.annotation.type.compareTo(b.annotation.type));
+    for (DexAnnotation annotation : annotations) {
+      annotation.annotation.sort();
+    }
+    sorted = hashCode();
+  }
+
+  private int sortedHashCode() {
+    int hashCode = hashCode();
+    return hashCode == UNSORTED ? 1 : hashCode;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
new file mode 100644
index 0000000..336eac5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexAnnotationSetRefList extends DexItem {
+
+  private static final DexAnnotationSetRefList theEmptyTypeList = new DexAnnotationSetRefList();
+
+  public final DexAnnotationSet[] values;
+
+  public static DexAnnotationSetRefList empty() {
+    return theEmptyTypeList;
+  }
+
+  private DexAnnotationSetRefList() {
+    this.values = new DexAnnotationSet[0];
+  }
+
+  public DexAnnotationSetRefList(DexAnnotationSet[] values) {
+    assert values != null && values.length > 0;
+    this.values = values;
+  }
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(values);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof DexAnnotationSetRefList) {
+      return Arrays.equals(values, ((DexAnnotationSetRefList) other).values);
+    }
+    return false;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    collectAll(indexedItems, values);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Collect values first so that the annotation sets have sorted themselves before adding this.
+    collectAll(mixedItems, values);
+    mixedItems.add(this);
+  }
+
+  public boolean isEmpty() {
+    return values.length == 0;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
new file mode 100644
index 0000000..b155cbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -0,0 +1,382 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DexApplication {
+
+  // Maps type into class promise, may be used concurrently.
+  final ImmutableMap<DexType, DexClassPromise> classMap;
+
+  public final ImmutableSet<DexType> mainDexList;
+
+  private final ClassNameMapper proguardMap;
+
+  public final Timing timing;
+
+  public final DexItemFactory dexItemFactory;
+
+  // Information on the lexicographically largest string referenced from code.
+  public final DexString highestSortingString;
+
+  /** Constructor should only be invoked by the DexApplication.Builder. */
+  private DexApplication(
+      ClassNameMapper proguardMap,
+      ImmutableMap<DexType, DexClassPromise> classMap,
+      ImmutableSet<DexType> mainDexList,
+      DexItemFactory dexItemFactory,
+      DexString highestSortingString,
+      Timing timing) {
+    this.proguardMap = proguardMap;
+    this.mainDexList = mainDexList;
+    this.classMap = classMap;
+    this.dexItemFactory = dexItemFactory;
+    this.highestSortingString = highestSortingString;
+    this.timing = timing;
+  }
+
+  public Iterable<DexProgramClass> classes() {
+    List<DexProgramClass> result = new ArrayList<>();
+    for (DexClassPromise promise : classMap.values()) {
+      if (promise.isProgramClass()) {
+        result.add(promise.get().asProgramClass());
+      }
+    }
+    return result;
+  }
+
+  public Iterable<DexLibraryClass> libraryClasses() {
+    List<DexLibraryClass> result = new ArrayList<>();
+    for (DexClassPromise promise : classMap.values()) {
+      if (promise.isLibraryClass()) {
+        result.add(promise.get().asLibraryClass());
+      }
+    }
+    return result;
+  }
+
+  public DexClass definitionFor(DexType type) {
+    DexClassPromise promise = classMap.get(type);
+    return promise == null ? null : promise.get();
+  }
+
+  public DexProgramClass programDefinitionFor(DexType type) {
+    DexClassPromise promise = classMap.get(type);
+    return (promise == null || !promise.isProgramClass()) ? null : promise.get().asProgramClass();
+  }
+
+  public String toString() {
+    return "Application (classes #" + classMap.size() + ")";
+  }
+
+  public ClassNameMapper getProguardMap() {
+    return proguardMap;
+  }
+
+  private void disassemble(DexEncodedMethod method, ClassNameMapper naming, Path outputDir) {
+    if (method.getCode() != null) {
+      PrintStream ps = System.out;
+      try {
+        String clazzName;
+        String methodName;
+        if (naming != null) {
+          clazzName = naming.originalNameOf(method.method.holder);
+          methodName = naming.originalSignatureOf(method.method).toString();
+        } else {
+          clazzName = method.method.holder.toSourceString();
+          methodName = method.method.name.toString();
+        }
+        if (outputDir != null) {
+          Path directory = outputDir.resolve(clazzName.replace('.', '/'));
+          String name = methodName + ".dump";
+          if (name.length() > 200) {
+            name = StringUtils.computeMD5Hash(name);
+          }
+          Files.createDirectories(directory);
+          ps = new PrintStream(Files.newOutputStream(directory.resolve(name)));
+        }
+        ps.println("Bytecode for");
+        ps.println("Class: '" + clazzName + "'");
+        ps.println("Method: '" + methodName + "':");
+        ps.println(method.getCode().toString(naming));
+      } catch (IOException e) {
+        e.printStackTrace();
+      } finally {
+        if (outputDir != null) {
+          ps.flush();
+          ps.close();
+        }
+      }
+    }
+  }
+
+  /**
+   * Write disassembly for the application code in the provided directory.
+   *
+   * <p>If no directory is provided everything is written to System.out.
+   */
+  public void disassemble(Path outputDir, InternalOptions options) {
+    for (DexClass clazz : classes()) {
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        if (options.methodMatchesFilter(method)) {
+          disassemble(method, getProguardMap(), outputDir);
+        }
+      }
+      for (DexEncodedMethod method : clazz.directMethods()) {
+        if (options.methodMatchesFilter(method)) {
+          disassemble(method, getProguardMap(), outputDir);
+        }
+      }
+    }
+  }
+
+  /** Return smali source for the application code. */
+  public String smali(InternalOptions options) {
+    ByteArrayOutputStream os = new ByteArrayOutputStream();
+    PrintStream ps = new PrintStream(os);
+    smali(options, ps);
+    return new String(os.toByteArray(), StandardCharsets.UTF_8);
+  }
+
+  private void writeClassHeader(DexClass clazz, PrintStream ps) {
+    StringBuilder builder = new StringBuilder();
+    builder.append(".class ");
+    builder.append(clazz.accessFlags.toSmaliString());
+    builder.append(" ");
+    builder.append(clazz.type.toSmaliString());
+    builder.append("\n\n");
+    if (clazz.type != dexItemFactory.objectType) {
+      builder.append(".super ");
+      builder.append(clazz.superType.toSmaliString());
+      // TODO(sgjesse): Add implemented interfaces
+      builder.append("\n");
+    }
+    ps.append(builder.toString());
+  }
+
+  /** Write smali source for the application code on the provided PrintStream. */
+  public void smali(InternalOptions options, PrintStream ps) {
+    for (DexClass clazz : classes()) {
+      boolean classHeaderWritten = false;
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        if (options.methodMatchesFilter(method)) {
+          if (!classHeaderWritten) {
+            writeClassHeader(clazz, ps);
+            classHeaderWritten = true;
+          }
+          ps.append("\n");
+          ps.append(method.toSmaliString(getProguardMap()));
+        }
+      }
+      for (DexEncodedMethod method : clazz.directMethods()) {
+        if (options.methodMatchesFilter(method)) {
+          if (!classHeaderWritten) {
+            writeClassHeader(clazz, ps);
+            classHeaderWritten = true;
+          }
+          ps.append("\n");
+          ps.append(method.toSmaliString(getProguardMap()));
+        }
+      }
+    }
+  }
+
+  public static class Builder {
+
+    public final Hashtable<DexType, DexClassPromise> classMap = new Hashtable<>();
+    public final Hashtable<DexCode, DexCode> codeItems = new Hashtable<>();
+
+    public final DexItemFactory dexItemFactory;
+    public ClassNameMapper proguardMap;
+    private final Timing timing;
+
+    public DexString highestSortingString;
+    private final Set<DexType> mainDexList = Sets.newIdentityHashSet();
+
+    public Builder(DexItemFactory dexItemFactory, Timing timing) {
+      this.dexItemFactory = dexItemFactory;
+      this.timing = timing;
+    }
+
+    public Builder(DexApplication application) {
+      this(application, application.classMap);
+    }
+
+    public Builder(DexApplication application, Map<DexType, DexClassPromise> classMap) {
+      this.classMap.putAll(classMap);
+      proguardMap = application.proguardMap;
+      timing = application.timing;
+      highestSortingString = application.highestSortingString;
+      dexItemFactory = application.dexItemFactory;
+      mainDexList.addAll(application.mainDexList);
+    }
+
+    public synchronized void setProguardMap(ClassNameMapper proguardMap) {
+      assert this.proguardMap == null;
+      this.proguardMap = proguardMap;
+    }
+
+    public synchronized void setHighestSortingString(DexString value) {
+      highestSortingString = value;
+    }
+
+    // Callback from FileReader when parsing a DexCode (multi-threaded).
+    public synchronized DexCode canonicalizeCode(DexCode code) {
+      DexCode result = codeItems.get(code);
+      if (result != null) {
+        return result;
+      }
+      codeItems.put(code, code);
+      return code;
+    }
+
+    public void addClassPromise(DexClassPromise promise) {
+      addClassPromise(promise, false);
+    }
+
+    public void addClassIgnoringLibraryDuplicates(DexClass clazz) {
+      addClass(clazz, true);
+    }
+
+    public void addSynthesizedClass(DexProgramClass synthesizedClass, boolean addToMainDexList) {
+      addClassPromise(synthesizedClass);
+      if (addToMainDexList && !mainDexList.isEmpty()) {
+        mainDexList.add(synthesizedClass.type);
+      }
+    }
+
+    // Callback from FileReader when parsing a DexProgramClass (multi-threaded).
+    private void addClass(DexClass clazz, boolean skipLibDups) {
+      addClassPromise(clazz, skipLibDups);
+    }
+
+    public synchronized void addClassPromise(DexClassPromise promise, boolean skipLibDups) {
+      assert promise != null;
+      DexType type = promise.getType();
+      DexClassPromise oldPromise = classMap.get(type);
+      if (oldPromise != null) {
+        promise = chooseClass(promise, oldPromise, skipLibDups);
+      }
+      if (oldPromise != promise) {
+        classMap.put(type, promise);
+      }
+    }
+
+    private DexClassPromise chooseClass(DexClassPromise a, DexClassPromise b, boolean skipLibDups) {
+      // NOTE: We assume that there should not be any conflicting names in user defined
+      // classes and/or linked jars. If we ever want to allow 'keep first'-like policy
+      // to resolve this kind of conflict between program and/or classpath classes, we'll
+      // need to make sure we choose the class we keep deterministically.
+      if (a.isProgramClass() && b.isProgramClass()) {
+        if (allowProgramClassConflict(a, b)) {
+          return a;
+        }
+        throw new CompilationError(
+            "Program type already present: " + a.getType().toSourceString());
+      }
+      if (a.isProgramClass()) {
+        return chooseBetweenProgramAndOtherClass(a, b);
+      }
+      if (b.isProgramClass()) {
+        return chooseBetweenProgramAndOtherClass(b, a);
+      }
+
+      if (a.isClasspathClass() && b.isClasspathClass()) {
+        throw new CompilationError(
+            "Classpath type already present: " + a.getType().toSourceString());
+      }
+      if (a.isClasspathClass()) {
+        return chooseBetweenClasspathAndLibraryClass(a, b);
+      }
+      if (b.isClasspathClass()) {
+        return chooseBetweenClasspathAndLibraryClass(b, a);
+      }
+
+      return chooseBetweenLibraryClasses(b, a, skipLibDups);
+    }
+
+    private boolean allowProgramClassConflict(DexClassPromise a, DexClassPromise b) {
+      // Currently only allow collapsing synthetic lambda classes.
+      return a.getOrigin() == DexClass.Origin.Dex
+          && b.getOrigin() == DexClass.Origin.Dex
+          && a.get().accessFlags.isSynthetic()
+          && b.get().accessFlags.isSynthetic()
+          && LambdaRewriter.hasLambdaClassPrefix(a.getType())
+          && LambdaRewriter.hasLambdaClassPrefix(b.getType());
+    }
+
+    private DexClassPromise chooseBetweenProgramAndOtherClass(
+        DexClassPromise selected, DexClassPromise ignored) {
+      assert selected.isProgramClass() && !ignored.isProgramClass();
+      if (ignored.isLibraryClass()) {
+        logIgnoredClass(ignored, "Class `%s` was specified as library and program type.");
+      }
+      // We don't log program/classpath class conflict since it is expected case.
+      return selected;
+    }
+
+    private DexClassPromise chooseBetweenClasspathAndLibraryClass(
+        DexClassPromise selected, DexClassPromise ignored) {
+      assert selected.isClasspathClass() && ignored.isLibraryClass();
+      logIgnoredClass(ignored, "Class `%s` was specified as library and classpath type.");
+      return selected;
+    }
+
+    private DexClassPromise chooseBetweenLibraryClasses(
+        DexClassPromise selected, DexClassPromise ignored, boolean skipDups) {
+      assert selected.isLibraryClass() && ignored.isLibraryClass();
+      if (!skipDups) {
+        throw new CompilationError(
+            "Library type already present: " + selected.getType().toSourceString());
+      }
+      logIgnoredClass(ignored, "Class `%s` was specified twice as a library type.");
+      return selected;
+    }
+
+    private void logIgnoredClass(DexClassPromise ignored, String message) {
+      if (Log.ENABLED) {
+        Log.warn(getClass(), message, ignored.getType().toSourceString());
+      }
+    }
+
+    public void addToMainDexList(Collection<DexType> mainDexList) {
+      this.mainDexList.addAll(mainDexList);
+    }
+
+    public DexApplication build() {
+      return new DexApplication(
+          proguardMap,
+          ImmutableMap.copyOf(classMap),
+          ImmutableSet.copyOf(mainDexList),
+          dexItemFactory,
+          highestSortingString,
+          timing);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
new file mode 100644
index 0000000..58bf764
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -0,0 +1,241 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.google.common.io.BaseEncoding;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+public final class DexCallSite extends IndexedDexItem {
+
+  public final DexString methodName;
+  public final DexProto methodProto;
+
+  public final DexMethodHandle bootstrapMethod;
+  public final List<DexValue> bootstrapArgs;
+
+  private DexEncodedArray encodedArray = null;
+
+  DexCallSite(DexString methodName, DexProto methodProto,
+      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+    assert methodName != null;
+    assert methodProto != null;
+    assert bootstrapMethod != null;
+    assert bootstrapArgs != null;
+
+    this.methodName = methodName;
+    this.methodProto = methodProto;
+    this.bootstrapMethod = bootstrapMethod;
+    this.bootstrapArgs = bootstrapArgs;
+  }
+
+  public int computeHashCode() {
+    return methodName.hashCode()
+        + methodProto.hashCode() * 7
+        + bootstrapMethod.hashCode() * 31
+        + bootstrapArgs.hashCode() * 101;
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexCallSite) {
+      DexCallSite o = (DexCallSite) other;
+      return methodName.equals(o.methodName)
+          && methodProto.equals(o.methodProto)
+          && bootstrapMethod.equals(o.bootstrapMethod)
+          && bootstrapArgs.equals(o.bootstrapArgs);
+    }
+    return false;
+  }
+
+  public String toString() {
+    StringBuilder builder =
+        new StringBuilder("CallSite: { Name: ").append(methodName.toSourceString())
+            .append(", Proto: ").append(methodProto.toSourceString())
+            .append(", ").append(bootstrapMethod.toSourceString());
+    String sep = ", Args: ";
+    for (DexItem arg : bootstrapArgs) {
+      builder.append(sep).append(arg.toSourceString());
+      sep = ", ";
+    }
+    builder.append('}');
+    return builder.toString();
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (indexedItems.addCallSite(this)) {
+      methodName.collectIndexedItems(indexedItems);
+      methodProto.collectIndexedItems(indexedItems);
+      bootstrapMethod.collectIndexedItems(indexedItems);
+      for (DexValue arg : bootstrapArgs) {
+        arg.collectIndexedItems(indexedItems);
+      }
+    }
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    mixedItems.add(getEncodedArray());
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  // TODO(mikaelpeltier): Adapt syntax when invoke-custom will be available into smali.
+  public String toSmaliString() {
+    return toString();
+  }
+
+  public String getHash() {
+    return new HashBuilder().build();
+  }
+
+  private final class HashBuilder {
+    private ByteArrayOutputStream bytes;
+    private ObjectOutputStream out;
+
+    private void write(DexString string) throws IOException {
+      out.writeInt(string.size); // To avoid same-prefix problem
+      out.write(string.content);
+    }
+
+    private void write(DexType type) throws IOException {
+      write(type.descriptor);
+    }
+
+    private void write(DexMethodHandle methodHandle) throws IOException {
+      out.writeShort(methodHandle.type.getValue());
+      if (methodHandle.isFieldHandle()) {
+        write(methodHandle.asField());
+      } else {
+        write(methodHandle.asMethod());
+      }
+    }
+
+    private void write(DexProto proto) throws IOException {
+      write(proto.shorty);
+      write(proto.returnType);
+      DexType[] params = proto.parameters.values;
+      out.writeInt(params.length);
+      for (DexType param : params) {
+        write(param);
+      }
+    }
+
+    private void write(DexMethod method) throws IOException {
+      write(method.holder);
+      write(method.proto);
+      write(method.name);
+    }
+
+    private void write(DexField field) throws IOException {
+      write(field.clazz);
+      write(field.type);
+      write(field.name);
+    }
+
+    private void write(List<DexValue> args) throws IOException {
+      out.writeInt(args.size());
+      for (DexValue arg : args) {
+        // String, Class, Integer, Long, Float, Double, MethodHandle, MethodType
+        if (arg instanceof DexValue.DexValueString) {
+          out.writeByte(0);
+          write(((DexValue.DexValueString) arg).value);
+          continue;
+        }
+
+        if (arg instanceof DexValue.DexValueType) {
+          out.writeByte(1);
+          write(((DexValue.DexValueType) arg).value);
+          continue;
+        }
+
+        if (arg instanceof DexValue.DexValueInt) {
+          out.writeByte(2);
+          out.writeInt(((DexValue.DexValueInt) arg).value);
+          continue;
+        }
+
+        if (arg instanceof DexValue.DexValueLong) {
+          out.writeByte(3);
+          out.writeLong(((DexValue.DexValueLong) arg).value);
+          continue;
+        }
+
+        if (arg instanceof DexValue.DexValueFloat) {
+          out.writeByte(4);
+          out.writeFloat(((DexValue.DexValueFloat) arg).value);
+          continue;
+        }
+
+        if (arg instanceof DexValue.DexValueDouble) {
+          out.writeByte(5);
+          out.writeDouble(((DexValue.DexValueDouble) arg).value);
+          continue;
+        }
+
+        if (arg instanceof DexValue.DexValueMethodHandle) {
+          out.writeByte(6);
+          write(((DexValue.DexValueMethodHandle) arg).value);
+          continue;
+        }
+
+        assert arg instanceof DexValue.DexValueMethodType;
+        out.writeByte(7);
+        write(((DexValue.DexValueMethodType) arg).value);
+      }
+    }
+
+    String build() {
+      try {
+        bytes = new ByteArrayOutputStream();
+        out = new ObjectOutputStream(bytes);
+
+        // We will generate SHA-1 hash of the call site information based on call site
+        // attributes used in equality comparison, such that if the two call sites are
+        // different their hashes should also be different.
+        write(methodName);
+        write(methodProto);
+        write(bootstrapMethod);
+        write(bootstrapArgs);
+        out.close();
+
+        MessageDigest digest = MessageDigest.getInstance("SHA-1");
+        digest.update(bytes.toByteArray());
+        return BaseEncoding.base64Url().omitPadding().encode(digest.digest());
+      } catch (NoSuchAlgorithmException | IOException ex) {
+        throw new Unreachable("Cannot get SHA-1 message digest");
+      }
+    }
+  }
+
+  public DexEncodedArray getEncodedArray() {
+    if (encodedArray == null) {
+      // 3 is the fixed size of the call site
+      DexValue[] callSitesValues = new DexValue[3 + bootstrapArgs.size()];
+      int valuesIndex = 0;
+      callSitesValues[valuesIndex++] = new DexValueMethodHandle(bootstrapMethod);
+      callSitesValues[valuesIndex++] = new DexValueString(methodName);
+      callSitesValues[valuesIndex++] = new DexValueMethodType(methodProto);
+      for (DexValue extraArgValue : bootstrapArgs) {
+        callSitesValues[valuesIndex++] = extraArgValue;
+      }
+      encodedArray = new DexEncodedArray(callSitesValues);
+    }
+
+    return encodedArray;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
new file mode 100644
index 0000000..bff12e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -0,0 +1,194 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.base.MoreObjects;
+
+public abstract class DexClass extends DexItem implements DexClassPromise {
+  public interface Factory {
+    DexClass create(DexType type, Origin origin, DexAccessFlags accessFlags, DexType superType,
+        DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+        DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+        DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods);
+  }
+
+  public enum Origin {
+    Dex, ClassFile, Synthetic
+  }
+
+  private static final DexEncodedMethod[] NO_METHODS = {};
+  private static final DexEncodedField[] NO_FIELDS = {};
+
+  public final Origin origin;
+  public final DexType type;
+  public final DexAccessFlags accessFlags;
+  public final DexType superType;
+  public final DexTypeList interfaces;
+  public final DexString sourceFile;
+  public DexEncodedField[] staticFields;
+  public DexEncodedField[] instanceFields;
+  public DexEncodedMethod[] directMethods;
+  public DexEncodedMethod[] virtualMethods;
+  public DexAnnotationSet annotations;
+
+  public DexClass(
+      DexString sourceFile, DexTypeList interfaces, DexAccessFlags accessFlags, DexType superType,
+      DexType type, DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods,
+      DexAnnotationSet annotations, Origin origin) {
+    this.origin = origin;
+    this.sourceFile = sourceFile;
+    this.interfaces = interfaces;
+    this.accessFlags = accessFlags;
+    this.superType = superType;
+    this.type = type;
+    this.staticFields = staticFields;
+    this.instanceFields = instanceFields;
+    this.directMethods = directMethods;
+    this.virtualMethods = virtualMethods;
+    this.annotations = annotations;
+    if (type == superType) {
+      throw new CompilationError("Class " + type.toString() + " cannot extend itself");
+    }
+    for (DexType interfaceType : interfaces.values) {
+      if (type == interfaceType) {
+        throw new CompilationError("Interface " + type.toString() + " cannot implement itself");
+      }
+    }
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    throw new Unreachable();
+  }
+
+  public DexEncodedMethod[] directMethods() {
+    return MoreObjects.firstNonNull(directMethods, NO_METHODS);
+  }
+
+  public DexEncodedMethod[] virtualMethods() {
+    return MoreObjects.firstNonNull(virtualMethods, NO_METHODS);
+  }
+
+
+  public DexEncodedField[] staticFields() {
+    return MoreObjects.firstNonNull(staticFields, NO_FIELDS);
+  }
+
+  public DexEncodedField[] instanceFields() {
+    return MoreObjects.firstNonNull(instanceFields, NO_FIELDS);
+  }
+
+  /**
+   * Find direct method in this class matching method
+   */
+  public DexEncodedMethod findDirectTarget(DexMethod method) {
+    return findTarget(directMethods(), method);
+  }
+
+  /**
+   * Find static field in this class matching field
+   */
+  public DexEncodedField findStaticTarget(DexField field) {
+    return findTarget(staticFields(), field);
+  }
+
+  /**
+   * Find virtual method in this class matching method
+   */
+  public DexEncodedMethod findVirtualTarget(DexMethod method) {
+    return findTarget(virtualMethods(), method);
+  }
+
+  /**
+   * Find instance field in this class matching field
+   */
+  public DexEncodedField findInstanceTarget(DexField field) {
+    return findTarget(instanceFields(), field);
+  }
+
+  private <T extends DexItem, S extends Descriptor<T, S>> T findTarget(T[] items, S descriptor) {
+    for (T entry : items) {
+      if (descriptor.match(entry)) {
+        return entry;
+      }
+    }
+    return null;
+  }
+
+  // Tells whether this is an interface.
+  public boolean isInterface() {
+    return accessFlags.isInterface();
+  }
+
+  public abstract void addDependencies(MixedSectionCollection collector);
+
+  @Override
+  public boolean isProgramClass() {
+    return false;
+  }
+
+  public DexProgramClass asProgramClass() {
+    return null;
+  }
+
+  @Override
+  public boolean isClasspathClass() {
+    return false;
+  }
+
+  public DexClasspathClass asClasspathClass() {
+    return null;
+  }
+
+  @Override
+  public boolean isLibraryClass() {
+    return false;
+  }
+
+  public DexLibraryClass asLibraryClass() {
+    return null;
+  }
+
+  public DexEncodedMethod getClassInitializer(DexItemFactory factory) {
+    for (DexEncodedMethod method : directMethods()) {
+      if (factory.isClassConstructor(method.method)) {
+        return method;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return this.origin;
+  }
+
+  @Override
+  public DexClass get() {
+    return this;
+  }
+
+  @Override
+  public DexType getType() {
+    return type;
+  }
+
+  /** Get a class factory for a particular resource kind */
+  public static Factory factoryForResourceKind(InternalResource.Kind kind) {
+    switch (kind) {
+      case PROGRAM:
+        return DexProgramClass::new;
+      case CLASSPATH:
+        return DexClasspathClass::new;
+      case LIBRARY:
+        return DexLibraryClass::new;
+    }
+    throw new Unreachable();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassPromise.java b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
new file mode 100644
index 0000000..243eadf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClassPromise.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+/**
+ * Provides a way for delayed DexClass discovery.
+ *
+ * Provides minimal class details of the promised class and
+ * provides the class when asked by calling method get().
+ *
+ * Note that DexClass also implements this interface, since it
+ * represents a 'materialized' promise for a class.
+ */
+public interface DexClassPromise {
+  DexType getType();
+
+  DexClass.Origin getOrigin();
+
+  boolean isProgramClass();
+
+  boolean isClasspathClass();
+
+  boolean isLibraryClass();
+
+  DexClass get();
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
new file mode 100644
index 0000000..0ec8cf7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+
+public class DexClasspathClass extends DexClass {
+
+  public DexClasspathClass(DexType type, Origin origin, DexAccessFlags accessFlags,
+      DexType superType, DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+      DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
+    super(sourceFile, interfaces, accessFlags, superType, type,
+        staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public String toString() {
+    return type.toString() + "(classpath class)";
+  }
+
+  @Override
+  public void addDependencies(MixedSectionCollection collector) {
+    // Should never happen but does not harm.
+    assert false;
+  }
+
+  @Override
+  public boolean isClasspathClass() {
+    return true;
+  }
+
+  @Override
+  public DexClasspathClass asClasspathClass() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
new file mode 100644
index 0000000..ebfd388
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -0,0 +1,448 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.SwitchPayload;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.DexSourceCode;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+// DexCode corresponds to code item in dalvik/dex-format.html
+public class DexCode extends Code {
+
+  public final int registerSize;
+  public final int incomingRegisterSize;
+  public final int outgoingRegisterSize;
+  public final Try[] tries;
+  public final TryHandler[] handlers;
+  public final Instruction[] instructions;
+
+  public final DexString highestSortingString;
+  private DexDebugInfo debugInfo;
+
+  public DexCode(
+      int registerSize,
+      int insSize,
+      int outsSize,
+      Instruction[] instructions,
+      Try[] tries,
+      TryHandler[] handlers,
+      DexDebugInfo debugInfo,
+      DexString highestSortingString) {
+    this.incomingRegisterSize = insSize;
+    this.registerSize = registerSize;
+    this.outgoingRegisterSize = outsSize;
+    this.instructions = instructions;
+    this.tries = tries;
+    this.handlers = handlers;
+    this.debugInfo = debugInfo;
+    this.highestSortingString = highestSortingString;
+    hashCode();  // Cache the hash code eagerly.
+  }
+
+  @Override
+  public boolean isDexCode() {
+    return true;
+  }
+
+  @Override
+  public DexCode asDexCode() {
+    return this;
+  }
+
+  public DexDebugInfo getDebugInfo() {
+    return debugInfo;
+  }
+
+  public void setDebugInfo(DexDebugInfo debugInfo) {
+    this.debugInfo = debugInfo;
+  }
+
+  public DexDebugInfo debugInfoWithAdditionalFirstParameter(DexString name) {
+    if (debugInfo == null) {
+      return null;
+    }
+    DexString[] parameters = debugInfo.parameters;
+    DexString[] newParameters = new DexString[parameters.length + 1];
+    newParameters[0] = name;
+    System.arraycopy(parameters, 0, newParameters, 1, parameters.length);
+    return new DexDebugInfo(debugInfo.startLine, newParameters, debugInfo.events);
+  }
+
+  public int codeSizeInBytes() {
+    Instruction last = instructions[instructions.length - 1];
+    return last.getOffset() + last.getSize();
+  }
+
+  @Override
+  public int computeHashCode() {
+    return incomingRegisterSize * 2
+        + registerSize * 3
+        + outgoingRegisterSize * 5
+        + Arrays.hashCode(instructions) * 7
+        + ((debugInfo == null) ? 0 : debugInfo.hashCode()) * 11
+        + Arrays.hashCode(tries) * 13
+        + Arrays.hashCode(handlers) * 17;
+  }
+
+  @Override
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexCode) {
+      DexCode o = (DexCode) other;
+      if (incomingRegisterSize != o.incomingRegisterSize) {
+        return false;
+      }
+      if (registerSize != o.registerSize) {
+        return false;
+      }
+      if (outgoingRegisterSize != o.outgoingRegisterSize) {
+        return false;
+      }
+      if (debugInfo == null) {
+        if (o.debugInfo != null) {
+          return false;
+        }
+      } else {
+        if (!debugInfo.equals(o.debugInfo)) {
+          return false;
+        }
+      }
+      if (!Arrays.equals(tries, o.tries)) {
+        return false;
+      }
+      if (!Arrays.equals(handlers, o.handlers)) {
+        return false;
+      }
+      // Save the most expensive operation to last.
+      return Arrays.equals(instructions, o.instructions);
+    }
+    return false;
+  }
+
+  @Override
+  public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+    DexSourceCode source = new DexSourceCode(this, encodedMethod);
+    IRBuilder builder = new IRBuilder(encodedMethod, source, options);
+    return builder.build();
+  }
+
+  public IRCode buildIR(
+      DexEncodedMethod encodedMethod,
+      ValueNumberGenerator valueNumberGenerator,
+      InternalOptions options) {
+    DexSourceCode source = new DexSourceCode(this, encodedMethod);
+    IRBuilder builder = new IRBuilder(encodedMethod, source, valueNumberGenerator, options);
+    return builder.build();
+  }
+
+  @Override
+  public void registerReachableDefinitions(UseRegistry registry) {
+    for (Instruction insn : instructions) {
+      insn.registerUse(registry);
+    }
+  }
+
+  public String toString() {
+    return toString(null);
+  }
+
+  public String toString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    builder.append("registers: ").append(registerSize);
+    builder.append(", inputs: ").append(incomingRegisterSize);
+    builder.append(", outputs: ").append(outgoingRegisterSize).append("\n");
+    builder.append("------------------------------------------------------------\n");
+    builder.append("inst#  offset  instruction         arguments\n");
+    builder.append("------------------------------------------------------------\n");
+    int instructionNumber = 0;
+    for (Instruction insn : instructions) {
+      StringUtils.appendLeftPadded(builder, Integer.toString(instructionNumber++), 5);
+      builder.append(": ")
+          .append(insn.toString(naming))
+          .append('\n');
+    }
+    if (tries.length > 0) {
+      builder.append("Tries (numbers are offsets)\n");
+      for (Try atry : tries) {
+        builder.append("  ");
+        builder.append(atry.toString());
+        builder.append('\n');
+      }
+      if (handlers != null) {
+        builder.append("Handlers (numbers are offsets)\n");
+        for (int handlerIndex = 0; handlerIndex < handlers.length; handlerIndex++) {
+          TryHandler handler = handlers[handlerIndex];
+          builder.append("  ").append(handlerIndex).append(": ");
+          builder.append(handler.toString());
+          builder.append('\n');
+        }
+      }
+    }
+    return builder.toString();
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    // Find labeled targets.
+    Map<Integer, Instruction> payloadUsers = new HashMap<>();
+    Set<Integer> labledTargets = new HashSet<>();
+    // Collect payload users and labeled targets for non-payload instructions.
+    for (Instruction dex : instructions) {
+      int[] targets = dex.getTargets();
+      if (targets != Instruction.NO_TARGETS && targets != Instruction.EXIT_TARGET) {
+        assert targets.length <= 2;
+        // For if instructions the second target is the fallthrough, for which no label is needed.
+        labledTargets.add(dex.getOffset() + targets[0]);
+      } else if (dex.hasPayload()) {
+        labledTargets.add(dex.getOffset() + dex.getPayloadOffset());
+        payloadUsers.put(dex.getOffset() + dex.getPayloadOffset(), dex);
+      }
+    }
+    // Collect labeled targets for payload instructions.
+    for (Instruction dex : instructions) {
+      if (dex.isSwitchPayload()) {
+        Instruction payloadUser = payloadUsers.get(dex.getOffset());
+        if (dex instanceof SwitchPayload) {
+          SwitchPayload payload = (SwitchPayload) dex;
+          for (int target : payload.switchTargetOffsets()) {
+            labledTargets.add(payloadUser.getOffset() + target);
+          }
+        }
+      }
+    }
+    // Generate smali for all instructions.
+    for (Instruction dex : instructions) {
+      if (labledTargets.contains(dex.getOffset())) {
+        builder.append("  :label_");
+        builder.append(dex.getOffset());
+        builder.append("\n");
+      }
+      if (dex.isSwitchPayload()) {
+        Instruction payloadUser = payloadUsers.get(dex.getOffset());
+        builder.append(dex.toSmaliString(payloadUser)).append('\n');
+      } else {
+        builder.append(dex.toSmaliString(naming)).append('\n');
+      }
+    }
+    if (tries.length > 0) {
+      builder.append("Tries (numbers are offsets)\n");
+      for (Try atry : tries) {
+        builder.append("  ");
+        builder.append(atry.toString());
+        builder.append('\n');
+      }
+      if (handlers != null) {
+        builder.append("Handlers (numbers are offsets)\n");
+        for (TryHandler handler : handlers) {
+          builder.append(handler.toString());
+          builder.append('\n');
+        }
+      }
+    }
+    return builder.toString();
+  }
+
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    for (Instruction insn : instructions) {
+      insn.collectIndexedItems(indexedItems);
+    }
+    if (debugInfo != null) {
+      debugInfo.collectIndexedItems(indexedItems);
+    }
+    if (handlers != null) {
+      for (TryHandler handler : handlers) {
+        handler.collectIndexedItems(indexedItems);
+      }
+    }
+  }
+
+  public boolean usesExceptionHandling() {
+    return tries.length != 0;
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    if (mixedItems.add(this)) {
+      if (debugInfo != null) {
+        debugInfo.collectMixedSectionItems(mixedItems);
+      }
+    }
+  }
+
+  public static class Try extends DexItem {
+
+    public static final int NO_INDEX = -1;
+
+    private final int handlerOffset;
+    public /* offset */ int startAddress;
+    public /* offset */ int instructionCount;
+    public int handlerIndex;
+
+    public Try(int startAddress, int instructionCount, int handlerOffset) {
+      this.startAddress = startAddress;
+      this.instructionCount = instructionCount;
+      this.handlerOffset = handlerOffset;
+      this.handlerIndex = NO_INDEX;
+    }
+
+    public void setHandlerIndex(Hashtable<Integer, Integer> map) {
+      handlerIndex = map.get(handlerOffset);
+    }
+
+    public int hashCode() {
+      return startAddress * 2 + instructionCount * 3 + handlerIndex * 5;
+    }
+
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (other instanceof Try) {
+        Try o = (Try) other;
+        if (startAddress != o.startAddress) {
+          return false;
+        }
+        if (instructionCount != o.instructionCount) {
+          return false;
+        }
+        return handlerIndex == o.handlerIndex;
+      }
+      return false;
+    }
+
+    public String toString() {
+      return "["
+          + startAddress
+          + " .. "
+          + (startAddress + instructionCount - 1)
+          + "] -> "
+          + handlerIndex;
+    }
+
+    @Override
+    void collectIndexedItems(IndexedItemCollection indexedItems) {
+      // Intentionally left empty.
+    }
+
+    @Override
+    void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+      // Should never be visited.
+      assert false;
+    }
+
+  }
+
+  public static class TryHandler extends DexItem {
+
+    public static final int NO_HANDLER = -1;
+
+    public final TypeAddrPair[] pairs;
+    public /* offset */ int catchAllAddr;
+
+    public TryHandler(TypeAddrPair[] pairs, int catchAllAddr) {
+      this.pairs = pairs;
+      this.catchAllAddr = catchAllAddr;
+    }
+
+    public int hashCode() {
+      return catchAllAddr + Arrays.hashCode(pairs) * 7;
+    }
+
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (other instanceof TryHandler) {
+        TryHandler o = (TryHandler) other;
+        if (catchAllAddr != o.catchAllAddr) {
+          return false;
+        }
+        return Arrays.equals(pairs, o.pairs);
+      }
+      return false;
+    }
+
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      collectAll(indexedItems, pairs);
+    }
+
+    @Override
+    void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+      // Should never be visited.
+      assert false;
+    }
+
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append("[\n");
+      for (TypeAddrPair pair : pairs) {
+        builder.append("       ");
+        builder.append(pair.type);
+        builder.append(" -> ");
+        builder.append(pair.addr);
+        builder.append("\n");
+      }
+      if (catchAllAddr != NO_HANDLER) {
+        builder.append("       default -> ");
+        builder.append(catchAllAddr);
+        builder.append("\n");
+      }
+      builder.append("     ]");
+      return builder.toString();
+    }
+
+    public static class TypeAddrPair extends DexItem {
+
+      public final DexType type;
+      public final /* offset */ int addr;
+      public final /* offset to the start of an encoded_catch_handler. */ int offset;
+
+      public TypeAddrPair(DexType type, int addr, int offset) {
+        this.type = type;
+        this.addr = addr;
+        this.offset = offset;
+      }
+
+      public void collectIndexedItems(IndexedItemCollection indexedItems) {
+        type.collectIndexedItems(indexedItems);
+      }
+
+      @Override
+      void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+        // Should never be visited.
+        assert false;
+      }
+
+      @Override
+      public int hashCode() {
+        return type.hashCode() * 7 + addr;
+      }
+
+      @Override
+      public boolean equals(Object other) {
+        if (this == other) {
+          return true;
+        }
+        if (other instanceof TypeAddrPair) {
+          TypeAddrPair o = (TypeAddrPair) other;
+          return type.equals(o.type) && addr == o.addr;
+        }
+        return false;
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
new file mode 100644
index 0000000..de7b544
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class DexDebugEntry {
+
+  public final int address;
+  public final int line;
+  public final DexString sourceFile;
+  public final boolean prologueEnd;
+  public final boolean epilogueBegin;
+  public final ImmutableMap<Integer, DebugLocalInfo> locals;
+
+  public DexDebugEntry(int address,
+      int line,
+      DexString sourceFile,
+      boolean prologueEnd,
+      boolean epilogueBegin,
+      ImmutableMap<Integer, DebugLocalInfo> locals) {
+    this.address = address;
+    this.line = line;
+    this.sourceFile = sourceFile;
+    this.prologueEnd = prologueEnd;
+    this.epilogueBegin = epilogueBegin;
+    this.locals = locals;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("pc 0x").append(StringUtils.hexString(address, 2));
+    builder.append(", line ").append(line);
+    if (sourceFile != null) {
+      builder.append(", file ").append(sourceFile);
+    }
+    if (prologueEnd) {
+      builder.append(", prologue_end = true");
+    }
+    if (epilogueBegin) {
+      builder.append(", epilogue_begin = true");
+    }
+    if (!locals.isEmpty()) {
+      builder.append(", locals: [");
+      SortedSet<Integer> keys = new TreeSet<>(locals.keySet());
+      boolean first = true;
+      for (Integer register : keys) {
+        if (first) {
+          first = false;
+        } else {
+          builder.append(", ");
+        }
+        builder.append(register).append(" -> ").append(locals.get(register));
+      }
+      builder.append("]");
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
new file mode 100644
index 0000000..f6d8fb4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Builder to construct a "per position" representation of the debug information.
+ *
+ * This builder is relatively relaxed about the stream of build operations and should accept
+ * any stream from any input file we expect to process correctly.
+ */
+public class DexDebugEntryBuilder {
+
+  private static class LocalEntry {
+    DebugLocalInfo current;
+    DebugLocalInfo last;
+
+    void set(DebugLocalInfo value) {
+      current = value;
+      last = value;
+    }
+
+    void unset() {
+      current = null;
+    }
+
+    void reset() {
+      current = last;
+    }
+  }
+
+  // The variables of the state machine.
+  private int currentPc = 0;
+  private int currentLine;
+  private DexString currentFile = null;
+  private boolean prologueEnd = false;
+  private boolean epilogueBegin = false;
+  private final Map<Integer, LocalEntry> locals = new HashMap<>();
+
+  // Delayed construction of an entry. Is finalized once locals information has been collected.
+  private DexDebugEntry pending = null;
+
+  // Canonicalization of locals (the IR/Dex builders assume identity of locals).
+  private final Map<DebugLocalInfo, DebugLocalInfo> canonicalizedLocals = new HashMap<>();
+
+  // Resulting debug entries.
+  private List<DexDebugEntry> entries = new ArrayList<>();
+
+  public DexDebugEntryBuilder(int startLine) {
+    currentLine = startLine;
+  }
+
+  public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) {
+    DexCode code = method.getCode().asDexCode();
+    DexDebugInfo info = code.getDebugInfo();
+    int argumentRegister = code.registerSize - code.incomingRegisterSize;
+    int argumentCount = code.incomingRegisterSize;
+    if (!method.accessFlags.isStatic()) {
+      --argumentCount;
+      DexString name = factory.thisName;
+      DexType type = method.method.getHolder();
+      startLocal(argumentRegister++, name, type, null);
+    }
+    DexType[] types = method.method.proto.parameters.values;
+    DexString[] names = info.parameters;
+    for (int i = 0; i < argumentCount; i++) {
+      // If null, the parameter has a parameterized type and the local is introduced in the stream.
+      if (names[i] != null) {
+        startLocal(argumentRegister++, names[i], types[i], null);
+      }
+    }
+    currentLine = info.startLine;
+    for (DexDebugEvent event : info.events) {
+      event.addToBuilder(this);
+    }
+  }
+
+  public void setFile(DexString file) {
+    currentFile = file;
+  }
+
+  public void advancePC(int pcDelta) {
+    assert pcDelta >= 0;
+    currentPc += pcDelta;
+  }
+
+  public void advanceLine(int line) {
+    currentLine += line;
+  }
+
+  public void endPrologue() {
+    prologueEnd = true;
+  }
+
+  public void beginEpilogue() {
+    epilogueBegin = true;
+  }
+
+  public void startLocal(int register, DexString name, DexType type, DexString signature) {
+    getEntry(register).set(canonicalize(name, type, signature));
+  }
+
+  public void endLocal(int register) {
+    getEntry(register).unset();
+  }
+
+  public void restartLocal(int register) {
+    getEntry(register).reset();
+  }
+
+  public void setPosition(int pcDelta, int lineDelta) {
+    assert pcDelta >= 0;
+    if (pending != null) {
+      // Local changes contribute to the pending position entry.
+      entries.add(new DexDebugEntry(
+          pending.address, pending.line, pending.sourceFile,
+          pending.prologueEnd, pending.epilogueBegin,
+          getLocals()));
+    }
+    currentPc += pcDelta;
+    currentLine += lineDelta;
+    pending = new DexDebugEntry(
+        currentPc, currentLine, currentFile, prologueEnd, epilogueBegin, null);
+    prologueEnd = false;
+    epilogueBegin = false;
+  }
+
+  public List<DexDebugEntry> build() {
+    // Flush any pending entry.
+    if (pending != null) {
+      setPosition(0, 0);
+      pending = null;
+    }
+    List<DexDebugEntry> result = entries;
+    entries = null;
+    return result;
+  }
+
+  private DebugLocalInfo canonicalize(DexString name, DexType type, DexString signature) {
+    DebugLocalInfo local = new DebugLocalInfo(name, type, signature);
+    DebugLocalInfo canonical = canonicalizedLocals.putIfAbsent(local, local);
+    return canonical != null ? canonical : local;
+  }
+
+  private LocalEntry getEntry(int register) {
+    LocalEntry entry = locals.get(register);
+    if (entry == null) {
+      entry = new LocalEntry();
+      locals.put(register, entry);
+    }
+    return entry;
+  }
+
+  private ImmutableMap<Integer, DebugLocalInfo> getLocals() {
+    ImmutableMap.Builder<Integer, DebugLocalInfo> builder = ImmutableMap.builder();
+    for (Integer register : locals.keySet()) {
+      LocalEntry entry = locals.get(register);
+      if (entry.current != null) {
+        builder.put(register, entry.current);
+      }
+    }
+    return builder.build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
new file mode 100644
index 0000000..b8bab7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -0,0 +1,367 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.DebugBytecodeWriter;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+
+abstract public class DexDebugEvent extends DexItem {
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection collection) {
+    // Empty by default.
+  }
+
+  @Override
+  public void collectMixedSectionItems(MixedSectionCollection collection) {
+    // Empty by default.
+  }
+
+  // Make sure all concrete subclasses implements toString, hashCode, and equals.
+  abstract public String toString();
+
+  abstract public int hashCode();
+
+  abstract public boolean equals(Object other);
+
+  public abstract void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping);
+
+  public abstract void addToBuilder(DexDebugEntryBuilder builder);
+
+
+  public static class AdvancePC extends DexDebugEvent {
+
+    final int delta;
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_ADVANCE_PC);
+      writer.putUleb128(delta);
+    }
+
+    public AdvancePC(int delta) {
+      this.delta = delta;
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      assert delta >= 0;
+      builder.advancePC(delta);
+    }
+
+
+    public String toString() {
+      return "ADVANCE_PC " + delta;
+    }
+
+    public int hashCode() {
+      return Constants.DBG_ADVANCE_PC
+          + delta * 7;
+    }
+
+    public boolean equals(Object other) {
+      return (other instanceof AdvancePC)
+          && (delta == ((AdvancePC) other).delta);
+    }
+  }
+
+  public static class SetPrologueEnd extends DexDebugEvent {
+
+    public SetPrologueEnd() {
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_SET_PROLOGUE_END);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.endPrologue();
+    }
+
+    public String toString() {
+      return "SET_PROLOGUE_END";
+    }
+
+
+    public int hashCode() {
+      return Constants.DBG_SET_PROLOGUE_END;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof SetPrologueEnd;
+    }
+  }
+
+
+  public static class SetEpilogueBegin extends DexDebugEvent {
+
+    public SetEpilogueBegin() {
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_SET_EPILOGUE_BEGIN);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.beginEpilogue();
+    }
+
+    public String toString() {
+      return "SET_EPILOGUE_BEGIN";
+    }
+
+    public int hashCode() {
+      return Constants.DBG_SET_EPILOGUE_BEGIN;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof SetEpilogueBegin;
+    }
+  }
+
+  public static class AdvanceLine extends DexDebugEvent {
+
+    final int delta;
+
+    public AdvanceLine(int delta) {
+      this.delta = delta;
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_ADVANCE_LINE);
+      writer.putSleb128(delta);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.advanceLine(delta);
+    }
+
+    public String toString() {
+      return "ADVANCE_LINE " + delta;
+    }
+
+    public int hashCode() {
+      return Constants.DBG_ADVANCE_LINE
+          + delta * 7;
+    }
+
+    public boolean equals(Object other) {
+      return (other instanceof AdvanceLine)
+          && (delta == ((AdvanceLine) other).delta);
+    }
+  }
+
+  static public class StartLocal extends DexDebugEvent {
+
+    final int registerNum;
+    final DexString name;
+    final DexType type;
+    final DexString signature;
+
+    public StartLocal(
+        int registerNum,
+        DexString name,
+        DexType type,
+        DexString signature) {
+      this.registerNum = registerNum;
+      this.name = name;
+      this.type = type;
+      this.signature = signature;
+    }
+
+    public StartLocal(int registerNum, DebugLocalInfo local) {
+      this(registerNum, local.name, local.type, local.signature);
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(signature == null
+          ? Constants.DBG_START_LOCAL
+          : Constants.DBG_START_LOCAL_EXTENDED);
+      writer.putUleb128(registerNum);
+      writer.putString(name);
+      writer.putType(type);
+      if (signature != null) {
+        writer.putString(signature);
+      }
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection collection) {
+      name.collectIndexedItems(collection);
+      type.collectIndexedItems(collection);
+      if (signature != null) {
+        signature.collectIndexedItems(collection);
+      }
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.startLocal(registerNum, name, type, signature);
+    }
+
+    public String toString() {
+      return "START_LOCAL " + registerNum;
+    }
+
+    public int hashCode() {
+      return Constants.DBG_START_LOCAL
+          + registerNum * 7
+          + name.hashCode() * 13
+          + type.hashCode() * 17
+          + (signature == null ? 0 : signature.hashCode()) * 19;
+    }
+
+    public boolean equals(Object other) {
+      if (!(other instanceof StartLocal)) {
+        return false;
+      }
+      StartLocal o = (StartLocal) other;
+      if (registerNum != o.registerNum) {
+        return false;
+      }
+      if (!name.equals(o.name)) {
+        return false;
+      }
+      if (!type.equals(o.type)) {
+        return false;
+      }
+      return (signature == o.signature || signature.equals(o.signature));
+    }
+  }
+
+  public static class EndLocal extends DexDebugEvent {
+
+    final int registerNum;
+
+    public EndLocal(int registerNum) {
+      this.registerNum = registerNum;
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_END_LOCAL);
+      writer.putUleb128(registerNum);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.endLocal(registerNum);
+    }
+
+    public String toString() {
+      return "END_LOCAL " + registerNum;
+    }
+
+    public int hashCode() {
+      return Constants.DBG_END_LOCAL
+          + registerNum * 7;
+    }
+
+    public boolean equals(Object other) {
+      return (other instanceof EndLocal)
+          && (registerNum == ((EndLocal) other).registerNum);
+    }
+  }
+
+  public static class RestartLocal extends DexDebugEvent {
+
+    final int registerNum;
+
+    public RestartLocal(int registerNum) {
+      this.registerNum = registerNum;
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_RESTART_LOCAL);
+      writer.putUleb128(registerNum);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.restartLocal(registerNum);
+    }
+
+    public String toString() {
+      return "RESTART_LOCAL " + registerNum;
+    }
+
+    public int hashCode() {
+      return Constants.DBG_RESTART_LOCAL
+          + registerNum * 7;
+    }
+
+    public boolean equals(Object other) {
+      return (other instanceof RestartLocal)
+          && (registerNum == ((RestartLocal) other).registerNum);
+    }
+  }
+
+  public static class SetFile extends DexDebugEvent {
+
+    final DexString fileName;
+
+    public SetFile(DexString fileName) {
+      this.fileName = fileName;
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(Constants.DBG_SET_FILE);
+      writer.putString(fileName);
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection collection) {
+      fileName.collectIndexedItems(collection);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      builder.setFile(fileName);
+    }
+
+    public String toString() {
+      return "SET_FILE " + fileName.toString();
+    }
+
+    public int hashCode() {
+      return Constants.DBG_SET_FILE
+          + fileName.hashCode() * 7;
+    }
+
+    public boolean equals(Object other) {
+      return (other instanceof SetFile)
+          && fileName.equals(((SetFile) other).fileName);
+    }
+  }
+
+  public static class Default extends DexDebugEvent {
+
+    final int value;
+
+    public Default(int value) {
+      assert (value >= Constants.DBG_FIRST_SPECIAL) && (value <= Constants.DBG_LAST_SPECIAL);
+      this.value = value;
+    }
+
+    public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
+      writer.putByte(value);
+    }
+
+    public void addToBuilder(DexDebugEntryBuilder builder) {
+      int adjustedOpcode = value - Constants.DBG_FIRST_SPECIAL;
+      int line = Constants.DBG_LINE_BASE + (adjustedOpcode % Constants.DBG_LINE_RANGE);
+      int address = adjustedOpcode / Constants.DBG_LINE_RANGE;
+      builder.setPosition(address, line);
+    }
+
+    public String toString() {
+      return "DEFAULT " + value;
+    }
+
+    public int hashCode() {
+      return Constants.DBG_FIRST_SPECIAL
+          + value * 7;
+    }
+
+    public boolean equals(Object other) {
+      return (other instanceof Default)
+          && (value == ((Default) other).value);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
new file mode 100644
index 0000000..5295c55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -0,0 +1,200 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
+import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
+import com.android.tools.r8.graph.DexDebugEvent.EndLocal;
+import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
+import com.android.tools.r8.graph.DexDebugEvent.SetFile;
+import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Builder for constructing a list of debug events suitable for DexDebugInfo.
+ *
+ * This builder is intended to be very pedantic and ensure a well-formed structure of the resulting
+ * event stream.
+ */
+public class DexDebugEventBuilder {
+
+  private static final int NO_PC_INFO = -1;
+  private static final int NO_LINE_INFO = -1;
+
+  private static class PositionState {
+    int pc = NO_PC_INFO;
+    int line = NO_LINE_INFO;
+    DexString file = null;
+    ImmutableMap<Integer, DebugLocalInfo> locals = null;
+  }
+
+  private final DexMethod method;
+
+  // Previous and current position info to delay emitting position changes.
+  private final PositionState previous;
+  private final PositionState current;
+
+  // In order list of non-this argument locals.
+  private int lastArgumentRegister = -1;
+  private final List<DebugLocalInfo> arguments;
+
+  // Mapping from register to local for currently open/visible locals.
+  private final Map<Integer, DebugLocalInfo> openLocals = new HashMap<>();
+
+  // Mapping from register to the last known local in that register (See DBG_RESTART_LOCAL).
+  private final Map<Integer, DebugLocalInfo> lastKnownLocals = new HashMap<>();
+
+  // Flushed events.
+  private final List<DexDebugEvent> events = new ArrayList<>();
+
+  private int startLine = NO_LINE_INFO;
+
+  public DexDebugEventBuilder(DexMethod method) {
+    this.method = method;
+    arguments = new ArrayList<>(method.proto.parameters.values.length);
+    current = new PositionState();
+    previous = new PositionState();
+  }
+
+  public void startArgument(int register, DebugLocalInfo local, boolean isThis) {
+    // Verify that arguments are started in order.
+    assert register > lastArgumentRegister;
+    lastArgumentRegister = register;
+    // If this is an actual argument record it for header information.
+    if (!isThis) {
+      arguments.add(local);
+    }
+    // If the argument does not have a parametrized type, implicitly open it.
+    if (local != null && local.signature == null) {
+      openLocals.put(register, local);
+      lastKnownLocals.put(register, local);
+    }
+  }
+
+  /** Emits a positions entry if the position has changed and associates any local changes. */
+  public void setPosition(int pc, DebugPosition position) {
+    setPosition(pc, position.line, position.file, position.getLocals());
+  }
+
+  public void setPosition(
+      int pc, int line, DexString file, ImmutableMap<Integer, DebugLocalInfo> locals) {
+    // If we have a pending position and the next differs from it flush the pending one.
+    if (previous.pc != current.pc && positionChanged(current, pc, line, file)) {
+      flushCurrentPosition();
+    }
+    current.pc = pc;
+    current.line = line;
+    current.file = file;
+    current.locals = locals;
+  }
+
+  private void flushCurrentPosition() {
+    // If this is the first emitted possition, initialize previous state: start-line is forced to be
+    // the first actual line, in-effect, causing the first position to be a zero-delta line change.
+    if (startLine == NO_LINE_INFO) {
+      assert events.isEmpty();
+      assert previous.pc == NO_PC_INFO;
+      assert previous.line == NO_LINE_INFO;
+      startLine = current.line;
+      previous.line = current.line;
+      previous.pc = 0;
+    }
+    // Emit position change (which might result in additional advancement events).
+    emitAdvancementEvents();
+    // Emit local changes for new current position (they relate to the already emitted position).
+    // Locals are either defined on all positions or on none.
+    assert current.locals != null || previous.locals == null;
+    if (current.locals != null) {
+      emitLocalChanges();
+    }
+  }
+
+  /** Build the resulting DexDebugInfo object. */
+  public DexDebugInfo build() {
+    if (previous.pc != current.pc) {
+      flushCurrentPosition();
+    }
+    if (startLine == NO_LINE_INFO) {
+      return null;
+    }
+    DexString[] params = new DexString[method.proto.parameters.values.length];
+    assert arguments.isEmpty() || params.length == arguments.size();
+    for (int i = 0; i < arguments.size(); i++) {
+      DebugLocalInfo local = arguments.get(i);
+      params[i] = (local == null || local.signature != null) ? null : local.name;
+    }
+    return new DexDebugInfo(startLine, params, events.toArray(new DexDebugEvent[events.size()]));
+  }
+
+  private static boolean positionChanged(
+      PositionState current, int nextPc, int nextLine, DexString nextFile) {
+    return nextPc != current.pc && (nextLine != current.line || nextFile != current.file);
+  }
+
+  private void emitAdvancementEvents() {
+    int pcDelta = current.pc - previous.pc;
+    int lineDelta = current.line - previous.line;
+    assert pcDelta >= 0;
+    if (current.file != previous.file) {
+      assert current.file == null || !current.file.equals(previous.file);
+      events.add(new SetFile(current.file));
+    }
+    if (lineDelta < Constants.DBG_LINE_BASE
+        || lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
+      events.add(new AdvanceLine(lineDelta));
+      // TODO(herhut): To be super clever, encode only the part that is above limit.
+      lineDelta = 0;
+    }
+    if (pcDelta >= Constants.DBG_ADDRESS_RANGE) {
+      events.add(new AdvancePC(pcDelta));
+      pcDelta = 0;
+    }
+    // TODO(herhut): Maybe only write this one if needed (would differ from DEX).
+    int specialOpcode =
+        0x0a + (lineDelta - Constants.DBG_LINE_BASE) + Constants.DBG_LINE_RANGE * pcDelta;
+    assert specialOpcode >= 0x0a;
+    assert specialOpcode <= 0xff;
+    events.add(new Default(specialOpcode));
+    previous.pc = current.pc;
+    previous.line = current.line;
+    previous.file = current.file;
+  }
+
+  private void emitLocalChanges() {
+    if (previous.locals == current.locals) {
+      return;
+    }
+    SortedSet<Integer> currentRegisters = new TreeSet<>(openLocals.keySet());
+    SortedSet<Integer> positionRegisters = new TreeSet<>(current.locals.keySet());
+    for (Integer register : currentRegisters) {
+      if (!positionRegisters.contains(register)) {
+        events.add(new EndLocal(register));
+        openLocals.put(register, null);
+      }
+    }
+    for (Integer register : positionRegisters) {
+      DebugLocalInfo positionLocal = current.locals.get(register);
+      DebugLocalInfo currentLocal = openLocals.get(register);
+      if (currentLocal != positionLocal) {
+        openLocals.put(register, positionLocal);
+        if (currentLocal == null && lastKnownLocals.get(register) == positionLocal) {
+          events.add(new RestartLocal(register));
+        } else {
+          events.add(new StartLocal(register, positionLocal));
+          lastKnownLocals.put(register, positionLocal);
+        }
+      }
+    }
+    previous.locals = current.locals;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
new file mode 100644
index 0000000..f82fe2a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+import java.util.List;
+
+public class DexDebugInfo extends CanonicalizedDexItem {
+
+  public final int startLine;
+  public final DexString[] parameters;
+  public final DexDebugEvent[] events;
+
+  public DexDebugInfo(int startLine, DexString[] parameters, DexDebugEvent[] events) {
+    assert startLine >= 0;
+    this.startLine = startLine;
+    this.parameters = parameters;
+    this.events = events;
+    // This call to hashCode is just an optimization to speedup equality when
+    // canonicalizing DexDebugInfo objects inside a synchronize method.
+    hashCode();
+  }
+
+  public List<DexDebugEntry> computeEntries() {
+    DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine);
+    for (DexDebugEvent event : events) {
+      event.addToBuilder(builder);
+    }
+    return builder.build();
+  }
+
+  public int computeHashCode() {
+    return startLine
+        + Arrays.hashCode(parameters) * 7
+        + Arrays.hashCode(events) * 13;
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexDebugInfo) {
+      DexDebugInfo o = (DexDebugInfo) other;
+      if (startLine != o.startLine) {
+        return false;
+      }
+      if (!Arrays.equals(parameters, o.parameters)) {
+        return false;
+      }
+      return Arrays.equals(events, o.events);
+    }
+    return false;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection collection) {
+    collectAll(collection, parameters);
+    collectAll(collection, events);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection collection) {
+    collection.add(this);
+  }
+
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("DebugInfo (line " + startLine + ") events: [\n");
+    for (DexDebugEvent event : events) {
+      builder.append("  ").append(event).append("\n");
+    }
+    builder.append("  END_SEQUENCE\n");
+    builder.append("]\n");
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
new file mode 100644
index 0000000..b421a9c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedAnnotation.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexEncodedAnnotation extends DexItem {
+
+  private static final int UNSORTED = 0;
+
+  public final DexType type;
+  public final DexAnnotationElement[] elements;
+
+  private int sorted = UNSORTED;
+
+  public DexEncodedAnnotation(DexType type, DexAnnotationElement[] elements) {
+    this.type = type;
+    this.elements = elements;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    type.collectIndexedItems(indexedItems);
+    collectAll(indexedItems, elements);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Should never be called.
+    assert false;
+  }
+
+  public String toString() {
+    return "Encoded annotation " + type + " " + Arrays.toString(elements);
+  }
+
+  @Override
+  public int hashCode() {
+    return type.hashCode() * 7 + Arrays.hashCode(elements);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof DexEncodedAnnotation) {
+      DexEncodedAnnotation that = (DexEncodedAnnotation) other;
+      return that.type.equals(type) && Arrays.equals(that.elements, elements);
+    }
+    return false;
+  }
+
+  public void sort() {
+    if (sorted != UNSORTED) {
+      assert sorted == sortedHashCode();
+      return;
+    }
+    Arrays.sort(elements, (a, b) -> a.name.compareTo(b.name));
+    for (DexAnnotationElement element : elements) {
+      element.value.sort();
+    }
+    sorted = sortedHashCode();
+  }
+
+  private int sortedHashCode() {
+    int hashCode = hashCode();
+    return hashCode == UNSORTED ? 1 : hashCode;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java b/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
new file mode 100644
index 0000000..8d7eba9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedArray.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexEncodedArray extends DexItem {
+
+  public final DexValue[] values;
+
+  public DexEncodedArray(DexValue[] values) {
+    this.values = values;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    collectAll(indexedItems, values);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    mixedItems.add(this);
+  }
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(values);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o == this) {
+      return true;
+    }
+    return (o instanceof DexEncodedArray) && (Arrays.equals(((DexEncodedArray) o).values, values));
+  }
+
+  @Override
+  public String toString() {
+    return "EncodedArray " + Arrays.toString(values);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
new file mode 100644
index 0000000..06add73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+
+public class DexEncodedField extends KeyedDexItem<DexField> {
+
+  public static final DexEncodedField[] EMPTY_ARRAY = new DexEncodedField[]{};
+
+  public final DexField field;
+  public final DexAccessFlags accessFlags;
+  public DexAnnotationSet annotations;
+  public final DexValue staticValue;
+
+  public DexEncodedField(DexField field, DexAccessFlags accessFlags, DexAnnotationSet annotations,
+      DexValue staticValue) {
+    assert !accessFlags.isStatic() || staticValue != null;
+    this.field = field;
+    this.accessFlags = accessFlags;
+    this.annotations = annotations;
+    this.staticValue = staticValue;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    field.collectIndexedItems(indexedItems);
+    annotations.collectIndexedItems(indexedItems);
+    if (staticValue != null) {
+      staticValue.collectIndexedItems(indexedItems);
+    }
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    annotations.collectMixedSectionItems(mixedItems);
+  }
+
+  @Override
+  public String toString() {
+    return "Encoded field " + field;
+  }
+
+  @Override
+  public String toSmaliString() {
+    return field.toSmaliString();
+  }
+
+  @Override
+  public String toSourceString() {
+    return field.toSourceString();
+  }
+
+  @Override
+  public DexField getKey() {
+    return field;
+  }
+
+  public boolean hasAnnotation() {
+    return !annotations.isEmpty();
+  }
+
+  // Returns a const instructions if this field is a compile time final const.
+  public Instruction valueAsConstInstruction(AppInfo appInfo, Value dest) {
+    // The only way to figure out whether the DexValue contains the final value
+    // is ensure the value is not the default or check <clinit> is not present.
+    if (accessFlags.isStatic() && accessFlags.isPublic() && accessFlags.isFinal()) {
+      DexClass clazz = appInfo.definitionFor(field.getHolder());
+      assert clazz != null : "Class for the field must be present";
+      return staticValue.asConstInstruction(
+          clazz.getClassInitializer(appInfo.dexItemFactory) != null, dest);
+    }
+    return null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
new file mode 100644
index 0000000..f041e52
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -0,0 +1,377 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.code.Const;
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.ConstStringJumbo;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.NewInstance;
+import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
+
+  public enum CompilationState {
+    NOT_PROCESSED,
+    PROCESSED_NOT_INLINING_CANDIDATE,
+    // Code only contains instructions that access public entities.
+    PROCESSED_INLINING_CANDIDATE_PUBLIC,
+    // Code only contains instructions that access public and package private entities.
+    PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE,
+    // Code also contains instructions that access public entities.
+    PROCESSED_INLINING_CANDIDATE_PRIVATE,
+  }
+
+  public static final DexEncodedMethod[] EMPTY_ARRAY = new DexEncodedMethod[]{};
+  public static final DexEncodedMethod SENTINEL =
+      new DexEncodedMethod(null, null, null, null, null);
+
+  public final DexMethod method;
+  public final DexAccessFlags accessFlags;
+  public DexAnnotationSet annotations;
+  public DexAnnotationSetRefList parameterAnnotations;
+  private Code code;
+  private CompilationState compilationState = CompilationState.NOT_PROCESSED;
+  private OptimizationInfo optimizationInfo = DefaultOptimizationInfo.DEFAULT;
+
+  public DexEncodedMethod(DexMethod method, DexAccessFlags accessFlags,
+      DexAnnotationSet annotations, DexAnnotationSetRefList parameterAnnotations, Code code) {
+    this.method = method;
+    this.accessFlags = accessFlags;
+    this.annotations = annotations;
+    this.parameterAnnotations = parameterAnnotations;
+    this.code = code;
+  }
+
+  public boolean isProcessed() {
+    return compilationState != CompilationState.NOT_PROCESSED;
+  }
+
+  public boolean isInliningCandidate(DexEncodedMethod container, boolean alwaysInline) {
+    if (alwaysInline && (compilationState != CompilationState.NOT_PROCESSED)) {
+      // Inline iff holder classes are equal.
+      // Interestingly, alwaysInline is true for some constructors.
+      // (accessFlags.isConstructor())
+      return container.method.getHolder() == method.getHolder();
+    }
+    switch (compilationState) {
+      case PROCESSED_INLINING_CANDIDATE_PUBLIC:
+        return true;
+      case PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE:
+         return container.method.getHolder().isSamePackage(method.getHolder());
+      // TODO(bak): Expand check for package private access:
+      case PROCESSED_INLINING_CANDIDATE_PRIVATE:
+        return container.method.getHolder() == method.getHolder();
+      default:
+        return false;
+    }
+  }
+
+  public void markProcessed(InliningConstraint state) {
+    switch (state) {
+      case ALWAYS:
+        compilationState = CompilationState.PROCESSED_INLINING_CANDIDATE_PUBLIC;
+        break;
+      case PACKAGE:
+        compilationState = CompilationState.PROCESSED_INLINING_CANDIDATE_PACKAGE_PRIVATE;
+        break;
+      case PRIVATE:
+        compilationState = CompilationState.PROCESSED_INLINING_CANDIDATE_PRIVATE;
+        break;
+      case NEVER:
+        compilationState = CompilationState.PROCESSED_NOT_INLINING_CANDIDATE;
+        break;
+    }
+  }
+
+  public void markNotProcessed() {
+    compilationState = CompilationState.NOT_PROCESSED;
+  }
+
+  public IRCode buildIR(InternalOptions options) {
+    return code == null ? null : code.buildIR(this, options);
+  }
+
+  public IRCode buildIR(ValueNumberGenerator valueNumberGenerator, InternalOptions options) {
+    return code == null
+        ? null
+        : code.asDexCode().buildIR(this, valueNumberGenerator, options);
+  }
+
+  public void setCode(IRCode ir, RegisterAllocator registerAllocator) {
+    final DexBuilder builder = new DexBuilder(ir, registerAllocator);
+    code = builder.build(method.proto.parameters.values.length);
+  }
+
+  // Replaces the dex code in the method by setting code to result of compiling the IR.
+  public void setCode(IRCode ir, RegisterAllocator registerAllocator,
+      DexString firstJumboString) {
+    final DexBuilder builder = new DexBuilder(ir, registerAllocator, firstJumboString);
+    code = builder.build(method.proto.parameters.values.length);
+  }
+
+  public String toString() {
+    return "Encoded method " + method;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    method.collectIndexedItems(indexedItems);
+    if (code != null) {
+      code.collectIndexedItems(indexedItems);
+    }
+    annotations.collectIndexedItems(indexedItems);
+    parameterAnnotations.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    if (code != null) {
+      code.collectMixedSectionItems(mixedItems);
+    }
+    annotations.collectMixedSectionItems(mixedItems);
+    parameterAnnotations.collectMixedSectionItems(mixedItems);
+  }
+
+  public Code getCode() {
+    return code;
+  }
+
+  public void removeCode() {
+    code = null;
+  }
+
+  public String qualifiedName() {
+    return method.qualifiedName();
+  }
+
+  public String descriptor() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("(");
+    for (DexType type : method.proto.parameters.values) {
+      builder.append(type.descriptor.toString());
+    }
+    builder.append(")");
+    builder.append(method.proto.returnType.descriptor.toString());
+    return builder.toString();
+  }
+
+  public String toSmaliString(ClassNameMapper naming) {
+    StringBuilder builder = new StringBuilder();
+    builder.append(".method ");
+    builder.append(accessFlags.toSmaliString());
+    builder.append(" ");
+    builder.append(method.name.toSmaliString());
+    builder.append(method.proto.toSmaliString());
+    builder.append("\n");
+    if (code != null) {
+      DexCode dexCode = code.asDexCode();
+      builder.append("    .registers ");
+      builder.append(dexCode.registerSize);
+      builder.append("\n\n");
+      builder.append(dexCode.toSmaliString(naming));
+    }
+    builder.append(".end method\n");
+    return builder.toString();
+  }
+
+  @Override
+  public String toSourceString() {
+    return method.toSourceString();
+  }
+
+  public DexEncodedMethod toAbstractMethod() {
+    accessFlags.setAbstract();
+    this.code = null;
+    return this;
+  }
+
+  /**
+   * Generates a {@link DexCode} object for the given instructions.
+   * <p>
+   * As the code object is produced outside of the normal compilation cycle, it has to use
+   * {@link ConstStringJumbo} to reference string constants. Hence, code produced form these
+   * templates might incur a size overhead.
+   */
+  private DexCode generateCodeFromTemplate(
+      int numberOfRegisters, int outRegisters, Instruction[] instructions) {
+    int offset = 0;
+    for (Instruction instruction : instructions) {
+      assert !(instruction instanceof ConstString);
+      instruction.setOffset(offset);
+      offset += instruction.getSize();
+    }
+    int requiredArgRegisters = accessFlags.isStatic() ? 0 : 1;
+    for (DexType type : method.proto.parameters.values) {
+      requiredArgRegisters += MoveType.fromDexType(type).requiredRegisters();
+    }
+    // Passing null as highestSortingString is save, as ConstString instructions are not allowed.
+    return new DexCode(Math.max(numberOfRegisters, requiredArgRegisters), requiredArgRegisters,
+        outRegisters, instructions, new DexCode.Try[0], new DexCode.TryHandler[0], null, null);
+  }
+
+  public DexEncodedMethod toEmptyThrowingMethod() {
+    Instruction insn[] = {new Const(0, 0), new Throw(0)};
+    code = generateCodeFromTemplate(1, 0, insn);
+    return this;
+  }
+
+  public DexEncodedMethod toMethodThatLogsError(DexItemFactory itemFactory) {
+    Signature signature = MethodSignature.fromDexMethod(this.method);
+    // TODO(herhut): Construct this out of parts to enable reuse, maybe even using descriptors.
+    DexString message = itemFactory.createString(
+        "Shaking error: Missing method in " + method.holder.toSourceString() + ": "
+            + signature);
+    DexString tag = itemFactory.createString("TOIGHTNESS");
+    DexType[] args = {itemFactory.stringType, itemFactory.stringType};
+    DexProto proto = itemFactory.createProto(itemFactory.intType, args);
+    DexMethod logMethod = itemFactory
+        .createMethod(itemFactory.createType("Landroid/util/Log;"), proto,
+            itemFactory.createString("e"));
+    DexType exceptionType = itemFactory.createType("Ljava/lang/RuntimeException;");
+    DexType[] exceptionArgs = {exceptionType, itemFactory.stringType};
+    DexMethod initMethod = itemFactory
+        .createMethod(exceptionType, itemFactory.createProto(itemFactory.voidType, exceptionArgs),
+            itemFactory.constructorMethodName);
+    // These methods might not get registered for jumbo string processing, therefore we always
+    // use the jumbo string encoding for the const string instruction.
+    Instruction insn[] = {
+        new ConstStringJumbo(0, tag),
+        new ConstStringJumbo(1, message),
+        new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
+        new NewInstance(0, exceptionType),
+        new InvokeStatic(2, initMethod, 0, 1, 0, 0, 0),
+        new Throw(0)
+    };
+    code = generateCodeFromTemplate(2, 2, insn);
+    return this;
+  }
+
+  public String codeToString() {
+    return code == null ? "<no code>" : code.toString();
+  }
+
+  @Override
+  public DexMethod getKey() {
+    return method;
+  }
+
+  public boolean hasAnnotation() {
+    return !annotations.isEmpty() || !parameterAnnotations.isEmpty();
+  }
+
+  public void registerReachableDefinitions(UseRegistry registry) {
+    if (code != null) {
+      if (Log.ENABLED) {
+        Log.verbose((Class) getClass(), "Registering definitions reachable from `%s`.", method);
+      }
+      code.registerReachableDefinitions(registry);
+    }
+  }
+
+  public static class OptimizationInfo {
+    private int returnedArgument = -1;
+    private boolean neverReturnsNull = false;
+    private boolean returnsConstant = false;
+    private long returnedConstant = 0;
+    private boolean forceInline = false;
+
+    private OptimizationInfo() {
+      // Intentionally left empty.
+    }
+
+    public boolean returnsArgument() {
+      return returnedArgument != -1;
+    }
+
+    public int getReturnedArgument() {
+      assert returnsArgument();
+      return returnedArgument;
+    }
+
+    public boolean neverReturnsNull() {
+      return neverReturnsNull;
+    }
+
+    public boolean returnsConstant() {
+      return returnsConstant;
+    }
+
+    public long getReturnedConstant() {
+      assert returnsConstant();
+      return returnedConstant;
+    }
+
+    public boolean forceInline() {
+      return forceInline;
+    }
+
+    private void markReturnsArgument(int argument) {
+      assert argument >= 0;
+      assert returnedArgument == -1 || returnedArgument == argument;
+      returnedArgument = argument;
+    }
+
+    private void markNeverReturnsNull() {
+      neverReturnsNull = true;
+    }
+
+    private void markReturnsConstant(long value) {
+      assert !returnsConstant || returnedConstant == value;
+      returnsConstant = true;
+      returnedConstant = value;
+    }
+
+    private void markForceInline() {
+      forceInline = true;
+    }
+  }
+
+  private static class DefaultOptimizationInfo extends OptimizationInfo {
+    public static final OptimizationInfo DEFAULT = new DefaultOptimizationInfo();
+
+    private DefaultOptimizationInfo() {}
+  }
+
+  synchronized private OptimizationInfo ensureMutableOI() {
+    if (optimizationInfo == DefaultOptimizationInfo.DEFAULT) {
+      optimizationInfo = new OptimizationInfo();
+    }
+    return optimizationInfo;
+  }
+
+  synchronized public void markReturnsArgument(int argument) {
+    ensureMutableOI().markReturnsArgument(argument);
+  }
+
+  synchronized public void markNeverReturnsNull() {
+    ensureMutableOI().markNeverReturnsNull();
+  }
+
+  synchronized public void markReturnsConstant(long value) {
+    ensureMutableOI().markReturnsConstant(value);
+  }
+
+  synchronized public void markForceInline() {
+    ensureMutableOI().markForceInline();
+  }
+
+  public OptimizationInfo getOptimizationInfo() {
+    return optimizationInfo;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
new file mode 100644
index 0000000..f97c147
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DexField extends Descriptor<DexEncodedField, DexField> implements
+    PresortedComparable<DexField> {
+
+  public final DexType clazz;
+  public final DexType type;
+  public final DexString name;
+
+  DexField(DexType clazz, DexType type, DexString name) {
+    this.clazz = clazz;
+    this.type = type;
+    this.name = name;
+  }
+
+  @Override
+  public int computeHashCode() {
+    return clazz.hashCode()
+        + type.hashCode() * 7
+        + name.hashCode() * 31;
+  }
+
+  @Override
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexField) {
+      DexField o = (DexField) other;
+      return clazz.equals(o.clazz)
+          && type.equals(o.type)
+          && name.equals(o.name);
+    }
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    return "Field " + type + " " + clazz + "." + name;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (indexedItems.addField(this)) {
+      clazz.collectIndexedItems(indexedItems);
+      type.collectIndexedItems(indexedItems);
+      indexedItems.getRenamedName(this).collectIndexedItems(indexedItems);
+    }
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  @Override
+  public int compareTo(DexField other) {
+    return sortedCompareTo(other.getSortedIndex());
+  }
+
+  @Override
+  public int slowCompareTo(DexField other) {
+    int result = clazz.slowCompareTo(other.clazz);
+    if (result != 0) {
+      return result;
+    }
+    result = name.slowCompareTo(other.name);
+    if (result != 0) {
+      return result;
+    }
+    return type.slowCompareTo(other.type);
+  }
+
+  @Override
+  public int slowCompareTo(DexField other, NamingLens namingLens) {
+    int result = clazz.slowCompareTo(other.clazz, namingLens);
+    if (result != 0) {
+      return result;
+    }
+    result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
+    if (result != 0) {
+      return result;
+    }
+    return type.slowCompareTo(other.type, namingLens);
+  }
+
+  @Override
+  public int layeredCompareTo(DexField other, NamingLens namingLens) {
+    int result = clazz.compareTo(other.clazz);
+    if (result != 0) {
+      return result;
+    }
+    result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
+    if (result != 0) {
+      return result;
+    }
+    return type.compareTo(other.type);
+  }
+
+  @Override
+  public boolean match(DexEncodedField entry) {
+    return entry.field.name == name && entry.field.type == type;
+  }
+
+  @Override
+  public DexType getHolder() {
+    return clazz;
+  }
+
+  public String toSmaliString() {
+    return clazz.toSmaliString() + "->" + name + ":" + type.toSmaliString();
+  }
+
+  @Override
+  public String toSourceString() {
+    return type.toSourceString() + " " + clazz.toSourceString() + "." + name.toSourceString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItem.java b/src/main/java/com/android/tools/r8/graph/DexItem.java
new file mode 100644
index 0000000..c0df752
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexItem.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.function.Consumer;
+
+public abstract class DexItem {
+
+  static <T extends DexItem> void collectAll(IndexedItemCollection indexedItems, T[] items) {
+    consumeArray(items, (T item) -> item.collectIndexedItems(indexedItems));
+  }
+
+  public static <T extends DexItem> void collectAll(MixedSectionCollection mixedItems, T[] items) {
+    consumeArray(items, (T item) -> item.collectMixedSectionItems(mixedItems));
+  }
+
+  /**
+   * Helper method to iterate over elements in an array.
+   * Handles the case where the array is null.
+   */
+  private static <T extends DexItem> void consumeArray(T[] items, Consumer<T> consumer) {
+    if (items == null) {
+      return;
+    }
+    for (T item : items) {
+      if (item != null) {
+        consumer.accept(item);
+      }
+    }
+  }
+
+  abstract void collectIndexedItems(IndexedItemCollection collection);
+
+  abstract void collectMixedSectionItems(MixedSectionCollection collection);
+
+  protected void flushCachedValues() {
+    // Overwritten in subclasses.
+  }
+
+  public String toSmaliString() {
+    return toString();
+  }
+
+  public String toSourceString() {
+    return toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
new file mode 100644
index 0000000..ce974ce
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -0,0 +1,367 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.naming.NamingLens;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class DexItemFactory {
+
+  private final Map<DexString, DexString> strings = new HashMap<>();
+  private final Map<DexType, DexType> types = new HashMap<>();
+  private final Map<DexField, DexField> fields = new HashMap<>();
+  private final Map<DexProto, DexProto> protos = new HashMap<>();
+  private final Map<DexMethod, DexMethod> methods = new HashMap<>();
+  private final Map<DexCallSite, DexCallSite> callSites = new HashMap<>();
+  private final Map<DexMethodHandle, DexMethodHandle> methodHandles = new HashMap<>();
+
+  boolean sorted = false;
+
+  public static final DexType catchAllType = new DexType(new DexString("CATCH_ALL"));
+  private static final Set<DexItem> internalSentinels = ImmutableSet.of(catchAllType);
+
+  public DexString booleanDescriptor = createString("Z");
+  public DexString byteDescriptor = createString("B");
+  public DexString charDescriptor = createString("C");
+  public DexString doubleDescriptor = createString("D");
+  public DexString floatDescriptor = createString("F");
+  public DexString intDescriptor = createString("I");
+  public DexString longDescriptor = createString("J");
+  public DexString shortDescriptor = createString("S");
+  public DexString voidDescriptor = createString("V");
+
+  public DexString boxedBooleanDescriptor = createString("Ljava/lang/Boolean;");
+  public DexString boxedByteDescriptor = createString("Ljava/lang/Byte;");
+  public DexString boxedCharDescriptor = createString("Ljava/lang/Character;");
+  public DexString boxedDoubleDescriptor = createString("Ljava/lang/Double;");
+  public DexString boxedFloatDescriptor = createString("Ljava/lang/Float;");
+  public DexString boxedIntDescriptor = createString("Ljava/lang/Integer;");
+  public DexString boxedLongDescriptor = createString("Ljava/lang/Long;");
+  public DexString boxedShortDescriptor = createString("Ljava/lang/Short;");
+  public DexString boxedNumberDescriptor = createString("Ljava/lang/Number;");
+
+  public DexString unboxBooleanMethodName = createString("booleanValue");
+  public DexString unboxByteMethodName = createString("byteValue");
+  public DexString unboxCharMethodName = createString("charValue");
+  public DexString unboxShortMethodName = createString("shortValue");
+  public DexString unboxIntMethodName = createString("intValue");
+  public DexString unboxLongMethodName = createString("longValue");
+  public DexString unboxFloatMethodName = createString("floatValue");
+  public DexString unboxDoubleMethodName = createString("doubleValue");
+
+  public DexString valueOfMethodName = createString("valueOf");
+
+  public DexString getClassMethodName = createString("getClass");
+
+  public DexString stringDescriptor = createString("Ljava/lang/String;");
+  public DexString objectDescriptor = createString("Ljava/lang/Object;");
+  public DexString classDescriptor = createString("Ljava/lang/Class;");
+  public DexString objectsDescriptor = createString("Ljava/util/Objects;");
+
+  public DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME);
+  public DexString classConstructorMethodName = createString(Constants.CLASS_INITIALIZER_NAME);
+
+  public DexString thisName = createString("this");
+
+  private DexString charArrayDescriptor = createString("[C");
+
+  public DexType booleanType = createType(booleanDescriptor);
+  public DexType byteType = createType(byteDescriptor);
+  public DexType charType = createType(charDescriptor);
+  public DexType doubleType = createType(doubleDescriptor);
+  public DexType floatType = createType(floatDescriptor);
+  public DexType intType = createType(intDescriptor);
+  public DexType longType = createType(longDescriptor);
+  public DexType shortType = createType(shortDescriptor);
+  public DexType voidType = createType(voidDescriptor);
+
+  public DexType boxedBooleanType = createType(boxedBooleanDescriptor);
+  public DexType boxedByteType = createType(boxedByteDescriptor);
+  public DexType boxedCharType = createType(boxedCharDescriptor);
+  public DexType boxedDoubleType = createType(boxedDoubleDescriptor);
+  public DexType boxedFloatType = createType(boxedFloatDescriptor);
+  public DexType boxedIntType = createType(boxedIntDescriptor);
+  public DexType boxedLongType = createType(boxedLongDescriptor);
+  public DexType boxedShortType = createType(boxedShortDescriptor);
+  public DexType boxedNumberType = createType(boxedNumberDescriptor);
+
+  public DexType stringType = createType(stringDescriptor);
+  public DexType objectType = createType(objectDescriptor);
+
+  public StringBuildingMethods stringBuilderMethods =
+      new StringBuildingMethods(createString("Ljava/lang/StringBuilder;"));
+  public StringBuildingMethods stringBufferMethods =
+      new StringBuildingMethods(createString("Ljava/lang/StringBuffer;"));
+  public ObjectsMethods objectsMethods = new ObjectsMethods();
+  public ObjectMethods objectMethods = new ObjectMethods();
+  public LongMethods longMethods = new LongMethods();
+
+  public void clearSubtypeInformation() {
+    types.values().forEach(DexType::clearSubtypeInformation);
+  }
+
+  public class LongMethods {
+    public DexMethod compare;
+
+    private LongMethods() {
+      compare = createMethod(boxedLongDescriptor,
+          createString("compare"), intDescriptor, new DexString[]{longDescriptor, longDescriptor});
+    }
+  }
+
+  public class ObjectMethods {
+    public DexMethod getClass;
+
+    private ObjectMethods() {
+      getClass = createMethod(objectsDescriptor,
+          getClassMethodName, classDescriptor, new DexString[]{});
+    }
+  }
+
+  public class ObjectsMethods {
+    public DexMethod requireNonNull;
+
+    private ObjectsMethods() {
+      requireNonNull = createMethod(objectsDescriptor,
+          createString("requireNonNull"), objectDescriptor, new DexString[]{objectDescriptor});
+    }
+  }
+
+  public class StringBuildingMethods {
+    public DexMethod appendBoolean;
+    public DexMethod appendChar;
+    public DexMethod appendCharArray;
+    public DexMethod appendSubCharArray;
+    public DexMethod appendCharSequence;
+    public DexMethod appendSubCharSequence;
+    public DexMethod appendInt;
+    public DexMethod appendDouble;
+    public DexMethod appendFloat;
+    public DexMethod appendLong;
+    public DexMethod appendObject;
+    public DexMethod appendString;
+    public DexMethod appendStringBuffer;
+    public DexMethod toString;
+
+    private StringBuildingMethods(DexString receiver) {
+      DexString sbuf = createString("Ljava/lang/StringBuffer;");
+      DexString charSequence = createString("Ljava/lang/CharSequence;");
+      DexString append = createString("append");
+      DexString toStringMethodName = createString("toString");
+
+      appendBoolean = createMethod(receiver, append, receiver, new DexString[]{booleanDescriptor});
+      appendChar = createMethod(receiver, append, receiver, new DexString[]{charDescriptor});
+      appendCharArray = createMethod(receiver, append, receiver, new DexString[]{
+          charArrayDescriptor});
+      appendSubCharArray = createMethod(receiver, append, receiver,
+          new DexString[]{charArrayDescriptor, intDescriptor, intDescriptor});
+      appendCharSequence = createMethod(receiver, append, receiver,
+          new DexString[]{charSequence});
+      appendSubCharSequence = createMethod(receiver, append, receiver,
+          new DexString[]{charSequence, intDescriptor, intDescriptor});
+      appendInt = createMethod(receiver, append, receiver, new DexString[]{intDescriptor});
+      appendDouble = createMethod(receiver, append, receiver, new DexString[]{doubleDescriptor});
+      appendFloat = createMethod(receiver, append, receiver, new DexString[]{floatDescriptor});
+      appendLong = createMethod(receiver, append, receiver, new DexString[]{longDescriptor});
+      appendObject = createMethod(receiver, append, receiver, new DexString[]{objectDescriptor});
+      appendString = createMethod(receiver, append, receiver, new DexString[]{stringDescriptor});
+      appendStringBuffer = createMethod(receiver, append, receiver, new DexString[]{sbuf});
+      toString = createMethod(receiver, toStringMethodName, stringDescriptor,
+          DexString.EMPTY_ARRAY);
+    }
+
+    public void forEeachAppendMethod(Consumer<DexMethod> consumer) {
+      consumer.accept(appendBoolean);
+      consumer.accept(appendChar);
+      consumer.accept(appendCharArray);
+      consumer.accept(appendSubCharArray);
+      consumer.accept(appendCharSequence);
+      consumer.accept(appendSubCharSequence);
+      consumer.accept(appendInt);
+      consumer.accept(appendDouble);
+      consumer.accept(appendFloat);
+      consumer.accept(appendLong);
+      consumer.accept(appendObject);
+      consumer.accept(appendString);
+      consumer.accept(appendStringBuffer);
+      consumer.accept(appendBoolean);
+    }
+  }
+
+  synchronized private static <T extends DexItem> T canonicalize(Map<T, T> map, T item) {
+    assert item != null;
+    assert !internalSentinels.contains(item);
+    T previous = map.putIfAbsent(item, item);
+    return previous == null ? item : previous;
+  }
+
+  public DexString createString(int size, byte[] content) {
+    assert !sorted;
+    DexString string = new DexString(size, content);
+    return canonicalize(strings, string);
+  }
+
+  public DexString createString(String source) {
+    assert !sorted;
+    DexString string = new DexString(source);
+    return canonicalize(strings, string);
+  }
+
+  public DexString lookupString(String source) {
+    return strings.get(new DexString(source));
+  }
+
+  public DexType createType(DexString descriptor) {
+    assert !sorted;
+    DexType type = new DexType(descriptor);
+    return canonicalize(types, type);
+  }
+
+  public DexType createType(String descriptor) {
+    return createType(createString(descriptor));
+  }
+
+  public DexType lookupType(String descriptor) {
+    DexString string = lookupString(descriptor);
+    if (string != null) {
+      return types.get(new DexType(string));
+    }
+    return null;
+  }
+
+  public DexField createField(DexType clazz, DexType type, DexString name) {
+    assert !sorted;
+    DexField field = new DexField(clazz, type, name);
+    return canonicalize(fields, field);
+  }
+
+  public DexProto createProto(DexString shorty, DexType type, DexTypeList parameters) {
+    assert !sorted;
+    DexProto proto = new DexProto(shorty, type, parameters);
+    return canonicalize(protos, proto);
+  }
+
+  public DexProto createProto(DexString shorty, DexType type, DexType[] parameters) {
+    assert !sorted;
+    return createProto(shorty, type,
+        parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
+  }
+
+  public DexProto createProto(DexType type, DexType[] parameters) {
+    return createProto(createShorty(type, parameters), type, parameters);
+  }
+
+  public DexString createShorty(DexType returnType, DexType[] argumentTypes) {
+    StringBuilder shortyBuilder = new StringBuilder();
+    shortyBuilder.append(returnType.toShorty());
+    for (DexType argumentType : argumentTypes) {
+      shortyBuilder.append(argumentType.toShorty());
+    }
+    return createString(shortyBuilder.toString());
+  }
+
+  public DexMethod createMethod(DexType holder, DexProto proto, DexString name) {
+    assert !sorted;
+    DexMethod method = new DexMethod(holder, proto, name);
+    return canonicalize(methods, method);
+  }
+
+  public DexMethodHandle createMethodHandle(
+      MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
+    assert !sorted;
+    DexMethodHandle methodHandle = new DexMethodHandle(type, fieldOrMethod);
+    return canonicalize(methodHandles, methodHandle);
+  }
+
+  public DexCallSite createCallSite(
+      DexString methodName, DexProto methodProto,
+      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+    assert !sorted;
+    DexCallSite callSite = new DexCallSite(methodName, methodProto, bootstrapMethod, bootstrapArgs);
+    return canonicalize(callSites, callSite);
+  }
+
+  public DexMethod createMethod(DexString clazzDescriptor, DexString name,
+      DexString returnTypeDescriptor,
+      DexString[] parameterDescriptors) {
+    assert !sorted;
+    DexType clazz = createType(clazzDescriptor);
+    DexType returnType = createType(returnTypeDescriptor);
+    DexType[] parameterTypes = new DexType[parameterDescriptors.length];
+    for (int i = 0; i < parameterDescriptors.length; i++) {
+      parameterTypes[i] = createType(parameterDescriptors[i]);
+    }
+    DexProto proto = createProto(shorty(returnType, parameterTypes), returnType, parameterTypes);
+
+    return createMethod(clazz, proto, name);
+  }
+
+  public boolean isConstructor(DexMethod method) {
+    return method.name == constructorMethodName;
+  }
+
+  public boolean isClassConstructor(DexMethod method) {
+    return method.name == classConstructorMethodName;
+  }
+
+  private DexString shorty(DexType returnType, DexType[] parameters) {
+    StringBuilder builder = new StringBuilder();
+    builder.append(returnType.toDescriptorString().charAt(0));
+    for (DexType parameter : parameters) {
+      String descriptor = parameter.toDescriptorString();
+      if (descriptor.charAt(0) == '[') {
+        builder.append('L');
+      } else {
+        builder.append(descriptor.charAt(0));
+      }
+    }
+    return createString(builder.toString());
+  }
+
+  private static <S extends PresortedComparable<S>> void assignSortedIndices(Collection<S> items,
+      NamingLens namingLens) {
+    List<S> sorted = new ArrayList<>(items);
+    sorted.sort((a, b) -> a.layeredCompareTo(b, namingLens));
+    int i = 0;
+    for (S value : sorted) {
+      value.setSortedIndex(i++);
+    }
+  }
+
+  synchronized public void sort(NamingLens namingLens) {
+    assert !sorted;
+    assignSortedIndices(strings.values(), namingLens);
+    assignSortedIndices(types.values(), namingLens);
+    assignSortedIndices(fields.values(), namingLens);
+    assignSortedIndices(protos.values(), namingLens);
+    assignSortedIndices(methods.values(), namingLens);
+    sorted = true;
+  }
+
+  synchronized public void resetSortedIndices() {
+    if (!sorted) {
+      return;
+    }
+    // Only used for asserting that we don't use the sorted index after we build the graph.
+    strings.values().forEach(IndexedDexItem::resetSortedIndex);
+    types.values().forEach(IndexedDexItem::resetSortedIndex);
+    fields.values().forEach(IndexedDexItem::resetSortedIndex);
+    protos.values().forEach(IndexedDexItem::resetSortedIndex);
+    methods.values().forEach(IndexedDexItem::resetSortedIndex);
+    sorted = false;
+  }
+
+  synchronized public void forAllTypes(Consumer<DexType> f) {
+    new ArrayList<>(types.values()).forEach(f);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
new file mode 100644
index 0000000..f0ca9b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+
+public class DexLibraryClass extends DexClass {
+
+  public DexLibraryClass(DexType type, Origin origin, DexAccessFlags accessFlags, DexType superType,
+      DexTypeList interfaces, DexString sourceFile, DexAnnotationSet annotations,
+      DexEncodedField[] staticFields, DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods, DexEncodedMethod[] virtualMethods) {
+    super(sourceFile, interfaces, accessFlags, superType, type,
+        staticFields, instanceFields, directMethods, virtualMethods, annotations, origin);
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public String toString() {
+    return type.toString() + "(library class)";
+  }
+
+  @Override
+  public void addDependencies(MixedSectionCollection collector) {
+    // Should never happen but does not harm.
+    assert false;
+  }
+
+  @Override
+  public boolean isLibraryClass() {
+    return true;
+  }
+
+  @Override
+  public DexLibraryClass asLibraryClass() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
new file mode 100644
index 0000000..11a9396
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexMemberAnnotation.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+
+public class DexMemberAnnotation<T extends Descriptor, S extends DexItem> extends DexItem {
+
+  public final T item;
+  public final S annotations;
+
+  public DexMemberAnnotation(T item, S annotations) {
+    this.item = item;
+    this.annotations = annotations;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    item.collectIndexedItems(indexedItems);
+    annotations.collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    annotations.collectMixedSectionItems(mixedItems);
+  }
+
+  @Override
+  public int hashCode() {
+    return item.hashCode() * 7 + annotations.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof DexMemberAnnotation) {
+      DexMemberAnnotation otherMember = (DexMemberAnnotation) other;
+      return item.equals(otherMember.item) && annotations.equals(otherMember.annotations);
+    }
+    return false;
+  }
+
+  public static class DexFieldAnnotation extends DexMemberAnnotation<DexField, DexAnnotationSet> {
+
+    public DexFieldAnnotation(DexField item, DexAnnotationSet annotations) {
+      super(item, annotations);
+    }
+  }
+
+  public static class DexMethodAnnotation extends DexMemberAnnotation<DexMethod, DexAnnotationSet> {
+
+    public DexMethodAnnotation(DexMethod item, DexAnnotationSet annotations) {
+      super(item, annotations);
+    }
+  }
+
+  public static class DexParameterAnnotation extends
+      DexMemberAnnotation<DexMethod, DexAnnotationSetRefList> {
+
+    public DexParameterAnnotation(DexMethod item, DexAnnotationSetRefList annotations) {
+      super(item, annotations);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
new file mode 100644
index 0000000..de536f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DexMethod extends Descriptor<DexEncodedMethod, DexMethod>
+    implements PresortedComparable<DexMethod> {
+
+  public final DexType holder;
+  public final DexProto proto;
+  public final DexString name;
+
+  // Caches used during processing.
+  private DexEncodedMethod singleTargetCache;
+
+  DexMethod(DexType holder, DexProto proto, DexString name) {
+    this.holder = holder;
+    this.proto = proto;
+    this.name = name;
+  }
+
+  @Override
+  public String toString() {
+    return "Method " + holder + "." + name + " " + proto.toString();
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (indexedItems.addMethod(this)) {
+      holder.collectIndexedItems(indexedItems);
+      proto.collectIndexedItems(indexedItems);
+      indexedItems.getRenamedName(this).collectIndexedItems(indexedItems);
+    }
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  public int computeHashCode() {
+    return holder.hashCode()
+        + proto.hashCode() * 7
+        + name.hashCode() * 31;
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexMethod) {
+      DexMethod o = (DexMethod) other;
+      return holder.equals(o.holder)
+          && name.equals(o.name)
+          && proto.equals(o.proto);
+    }
+    return false;
+  }
+
+  @Override
+  public int compareTo(DexMethod other) {
+    return sortedCompareTo(other.getSortedIndex());
+  }
+
+  @Override
+  public int slowCompareTo(DexMethod other) {
+    int result = holder.slowCompareTo(other.holder);
+    if (result != 0) {
+      return result;
+    }
+    result = name.slowCompareTo(other.name);
+    if (result != 0) {
+      return result;
+    }
+    return proto.slowCompareTo(other.proto);
+  }
+
+  @Override
+  public int slowCompareTo(DexMethod other, NamingLens namingLens) {
+    int result = holder.slowCompareTo(other.holder, namingLens);
+    if (result != 0) {
+      return result;
+    }
+    result = namingLens.lookupName(this).slowCompareTo(namingLens.lookupName(other));
+    if (result != 0) {
+      return result;
+    }
+    return proto.slowCompareTo(other.proto, namingLens);
+  }
+
+  @Override
+  public int layeredCompareTo(DexMethod other, NamingLens namingLens) {
+    int result = holder.compareTo(other.holder);
+    if (result != 0) {
+      return result;
+    }
+    result = namingLens.lookupName(this).compareTo(namingLens.lookupName(other));
+    if (result != 0) {
+      return result;
+    }
+    return proto.compareTo(other.proto);
+  }
+
+  @Override
+  public boolean match(DexEncodedMethod entry) {
+    return entry.method.name == name && entry.method.proto == proto;
+  }
+
+  @Override
+  public DexType getHolder() {
+    return holder;
+  }
+
+  public String qualifiedName() {
+    return holder + "." + name;
+  }
+
+  public String toSmaliString() {
+    return holder.toSmaliString() + "->" + name + proto.toSmaliString();
+  }
+
+  public String toSourceString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append(proto.returnType.toSourceString());
+    builder.append(" ");
+    builder.append(holder.toSourceString());
+    builder.append(".");
+    builder.append(name);
+    builder.append("(");
+    for (int i = 0; i < proto.parameters.values.length; i++) {
+      if (i != 0) {
+        builder.append(", ");
+      }
+      builder.append(proto.parameters.values[i].toSourceString());
+    }
+    builder.append(")");
+    return builder.toString();
+  }
+
+  synchronized public void setSingleVirtualMethodCache(DexEncodedMethod method) {
+    singleTargetCache = method == null ? DexEncodedMethod.SENTINEL : method;
+  }
+
+  synchronized public boolean isSingleVirtualMethodCached() {
+    return singleTargetCache != null;
+  }
+
+  synchronized public DexEncodedMethod getSingleVirtualMethodCache() {
+    assert isSingleVirtualMethodCached();
+    return singleTargetCache == DexEncodedMethod.SENTINEL ? null : singleTargetCache;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
new file mode 100644
index 0000000..e59e7c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -0,0 +1,187 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+
+public class DexMethodHandle extends IndexedDexItem {
+
+  public enum MethodHandleType {
+    // Method handle dex type.
+    STATIC_PUT((short) 0x00),
+    STATIC_GET((short) 0x01),
+    INSTANCE_PUT((short) 0x02),
+    INSTANCE_GET((short) 0x03),
+    INVOKE_STATIC((short) 0x04),
+    INVOKE_INSTANCE((short) 0x05),
+    // Upcoming method handle dex type.
+    INVOKE_CONSTRUCTOR((short) 0x06),
+    // Internal method handle needed by lambda desugaring.
+    INVOKE_INTERFACE((short) 0x07),
+    INVOKE_SUPER((short) 0x08);
+
+    private final short value;
+
+    MethodHandleType(short value) {
+      this.value = value;
+    }
+
+    public short getValue() {
+      return value;
+    }
+
+    public static MethodHandleType getKind(int value) {
+      MethodHandleType kind;
+
+      switch (value) {
+        case 0x00:
+          kind = STATIC_PUT;
+          break;
+        case 0x01:
+          kind = STATIC_GET;
+          break;
+        case 0x02:
+          kind = INSTANCE_PUT;
+          break;
+        case 0x03:
+          kind = INSTANCE_GET;
+          break;
+        case 0x04:
+          kind = INVOKE_STATIC;
+          break;
+        case 0x05:
+          kind = INVOKE_INSTANCE;
+          break;
+        case 0x06:
+          kind = INVOKE_CONSTRUCTOR;
+          break;
+        case 0x07:
+          kind = INVOKE_INTERFACE;
+          break;
+        case 0x08:
+          kind = INVOKE_SUPER;
+          break;
+        default:
+          throw new AssertionError();
+      }
+
+      assert kind.getValue() == value;
+      return kind;
+    }
+
+    public boolean isFieldType() {
+      return isStaticPut() || isStaticGet() || isInstancePut() || isInstanceGet();
+    }
+
+    public boolean isMethodType() {
+      return isInvokeStatic() || isInvokeInstance() || isInvokeInterface() || isInvokeSuper()
+          || isInvokeConstructor();
+    }
+
+    public boolean isStaticPut() {
+      return this == MethodHandleType.STATIC_PUT;
+    }
+
+    public boolean isStaticGet() {
+      return this == MethodHandleType.STATIC_GET;
+    }
+
+    public boolean isInstancePut() {
+      return this == MethodHandleType.INSTANCE_PUT;
+    }
+
+    public boolean isInstanceGet() {
+      return this == MethodHandleType.INSTANCE_GET;
+    }
+
+    public boolean isInvokeStatic() {
+      return this == MethodHandleType.INVOKE_STATIC;
+    }
+
+    public boolean isInvokeInstance() {
+      return this == MethodHandleType.INVOKE_INSTANCE;
+    }
+
+    public boolean isInvokeInterface() {
+      return this == MethodHandleType.INVOKE_INTERFACE;
+    }
+
+    public boolean isInvokeSuper() {
+      return this == MethodHandleType.INVOKE_SUPER;
+    }
+
+    public boolean isInvokeConstructor() {
+      return this == MethodHandleType.INVOKE_CONSTRUCTOR;
+    }
+  }
+
+  public MethodHandleType type;
+  public Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod;
+
+  public DexMethodHandle(
+      MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
+    this.type = type;
+    this.fieldOrMethod = fieldOrMethod;
+  }
+
+  public int computeHashCode() {
+    return type.hashCode() + fieldOrMethod.computeHashCode() * 7;
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexMethodHandle) {
+      DexMethodHandle o = (DexMethodHandle) other;
+      return type.equals(o.type) && fieldOrMethod.computeEquals(o.fieldOrMethod);
+    }
+    return false;
+  }
+
+  public String toString() {
+    StringBuilder builder = new StringBuilder("MethodHandle: {")
+            .append(type)
+            .append(", ")
+            .append(fieldOrMethod.toSourceString())
+            .append("}");
+    return builder.toString();
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (indexedItems.addMethodHandle(this)) {
+      fieldOrMethod.collectIndexedItems(indexedItems);
+    }
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  // TODO(mikaelpeltier): Adapt syntax when invoke-custom will be available into smali.
+  public String toSmaliString() {
+    return toString();
+  }
+
+  public boolean isFieldHandle() {
+    return type.isFieldType();
+  }
+
+  public boolean isMethodHandle() {
+    return type.isMethodType();
+  }
+
+  public boolean isStaticHandle() {
+    return type.isStaticPut() || type.isStaticGet() || type.isInvokeStatic();
+  }
+
+  public DexMethod asMethod() {
+    assert isMethodHandle();
+    return (DexMethod) fieldOrMethod;
+  }
+
+  public DexField asField() {
+    assert isFieldHandle();
+    return (DexField) fieldOrMethod;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
new file mode 100644
index 0000000..2fc73bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+public class DexProgramClass extends DexClass {
+
+  private DexEncodedArray staticValues;
+
+  public DexProgramClass(DexType type,
+      Origin origin,
+      DexAccessFlags accessFlags,
+      DexType superType,
+      DexTypeList interfaces,
+      DexString sourceFile,
+      DexAnnotationSet classAnnotations,
+      DexEncodedField[] staticFields,
+      DexEncodedField[] instanceFields,
+      DexEncodedMethod[] directMethods,
+      DexEncodedMethod[] virtualMethods) {
+    super(sourceFile, interfaces, accessFlags, superType, type, staticFields,
+        instanceFields, directMethods, virtualMethods, classAnnotations, origin);
+    assert classAnnotations != null;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (indexedItems.addClass(this)) {
+      type.collectIndexedItems(indexedItems);
+      if (superType != null) {
+        superType.collectIndexedItems(indexedItems);
+      } else {
+        assert type.toDescriptorString().equals("Ljava/lang/Object;");
+      }
+      if (sourceFile != null) {
+        sourceFile.collectIndexedItems(indexedItems);
+      }
+      if (annotations != null) {
+        annotations.collectIndexedItems(indexedItems);
+      }
+      if (interfaces != null) {
+        interfaces.collectIndexedItems(indexedItems);
+      }
+      collectAll(indexedItems, staticFields);
+      collectAll(indexedItems, instanceFields);
+      collectAll(indexedItems, directMethods);
+      collectAll(indexedItems, virtualMethods);
+    }
+  }
+
+  public void addDependencies(MixedSectionCollection collector) {
+    // We only have a class data item if there are methods or fields.
+    if (hasMethodsOrFields()) {
+      collector.add(this);
+      collectAll(collector, directMethods);
+      collectAll(collector, virtualMethods);
+      collectAll(collector, staticFields);
+      collectAll(collector, instanceFields);
+    }
+    if (annotations != null) {
+      annotations.collectMixedSectionItems(collector);
+    }
+    if (interfaces != null) {
+      interfaces.collectMixedSectionItems(collector);
+    }
+    annotations.collectMixedSectionItems(collector);
+  }
+
+  @Override
+  public String toString() {
+    return type.toString();
+  }
+
+  @Override
+  public boolean isProgramClass() {
+    return true;
+  }
+
+  @Override
+  public DexProgramClass asProgramClass() {
+    return this;
+  }
+
+  public boolean hasMethodsOrFields() {
+    int numberOfFields = staticFields().length + instanceFields().length;
+    int numberOfMethods = directMethods().length + virtualMethods().length;
+    return numberOfFields + numberOfMethods > 0;
+  }
+
+  public boolean hasAnnotations() {
+    return !annotations.isEmpty()
+        || hasAnnotations(virtualMethods)
+        || hasAnnotations(directMethods)
+        || hasAnnotations(staticFields)
+        || hasAnnotations(instanceFields);
+  }
+
+  private boolean hasAnnotations(DexEncodedField[] fields) {
+    return fields != null && Arrays.stream(fields).anyMatch(DexEncodedField::hasAnnotation);
+  }
+
+  private boolean hasAnnotations(DexEncodedMethod[] methods) {
+    return methods != null && Arrays.stream(methods).anyMatch(DexEncodedMethod::hasAnnotation);
+  }
+
+  public void setStaticValues(DexEncodedArray staticValues) {
+    this.staticValues = staticValues;
+  }
+
+  public DexEncodedArray getStaticValues() {
+    return staticValues;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java
new file mode 100644
index 0000000..946eb6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+
+public class DexProto extends IndexedDexItem implements PresortedComparable<DexProto> {
+
+  public final DexString shorty;
+  public final DexType returnType;
+  public final DexTypeList parameters;
+
+  DexProto(DexString shorty, DexType returnType, DexTypeList parameters) {
+    this.shorty = shorty;
+    this.returnType = returnType;
+    this.parameters = parameters;
+  }
+
+  public int computeHashCode() {
+    return shorty.hashCode()
+        + returnType.hashCode() * 7
+        + parameters.hashCode() * 31;
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexProto) {
+      DexProto o = (DexProto) other;
+      return shorty.equals(o.shorty)
+          && returnType.equals(o.returnType)
+          && parameters.equals(o.parameters);
+    }
+    return false;
+  }
+
+  public String toString() {
+    return "Proto " + shorty + " " + returnType + " " + parameters;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    if (indexedItems.addProto(this)) {
+      shorty.collectIndexedItems(indexedItems);
+      returnType.collectIndexedItems(indexedItems);
+      parameters.collectIndexedItems(indexedItems);
+    }
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  @Override
+  public int compareTo(DexProto other) {
+    return sortedCompareTo(other.getSortedIndex());
+  }
+
+  @Override
+  public int slowCompareTo(DexProto other) {
+    int result = returnType.slowCompareTo(other.returnType);
+    if (result == 0) {
+      result = parameters.slowCompareTo(other.parameters);
+    }
+    return result;
+  }
+
+  @Override
+  public int slowCompareTo(DexProto other, NamingLens namingLens) {
+    int result = returnType.slowCompareTo(other.returnType, namingLens);
+    if (result == 0) {
+      result = parameters.slowCompareTo(other.parameters, namingLens);
+    }
+    return result;
+  }
+
+  @Override
+  public int layeredCompareTo(DexProto other, NamingLens namingLens) {
+    int result = returnType.compareTo(other.returnType);
+    if (result == 0) {
+      result = parameters.compareTo(other.parameters);
+    }
+    return result;
+  }
+
+  @Override
+  public String toSmaliString() {
+    return toDescriptorString();
+  }
+
+  public String toDescriptorString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("(");
+    for (int i = 0; i < parameters.values.length; i++) {
+      builder.append(parameters.values[i].toDescriptorString());
+    }
+    builder.append(")");
+    builder.append(returnType.toDescriptorString());
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
new file mode 100644
index 0000000..e9b687e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -0,0 +1,201 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.naming.NamingLens;
+import java.io.UTFDataFormatException;
+import java.util.Arrays;
+
+public class DexString extends IndexedDexItem implements PresortedComparable<DexString> {
+
+  public static final DexString[] EMPTY_ARRAY = new DexString[]{};
+
+  public final int size;  // size of this string, in UTF-16
+  public final byte[] content;
+
+  DexString(int size, byte[] content) {
+    this.size = size;
+    this.content = content;
+  }
+
+  DexString(String string) {
+    this.size = string.length();
+    this.content = encode(string);
+  }
+
+  public int computeHashCode() {
+    return size * 7 + Arrays.hashCode(content);
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexString) {
+      DexString o = (DexString) other;
+      return size == o.size && Arrays.equals(content, o.content);
+    }
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    try {
+      return decode();
+    } catch (UTFDataFormatException e) {
+      throw new RuntimeException("Bad format", e);
+    }
+  }
+
+  public int numberOfLeadingSquareBrackets() {
+    int result = 0;
+    while (content.length > result && content[result] == ((byte) '[')) {
+      result++;
+    }
+    return result;
+  }
+
+  // Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
+  private String decode() throws UTFDataFormatException {
+    int s = 0;
+    int p = 0;
+    char[] out = new char[size];
+    while (true) {
+      char a = (char) (content[p++] & 0xff);
+      if (a == 0) {
+        return new String(out, 0, s);
+      }
+      out[s] = a;
+      if (a < '\u0080') {
+        s++;
+      } else if ((a & 0xe0) == 0xc0) {
+        int b = content[p++] & 0xff;
+        if ((b & 0xC0) != 0x80) {
+          throw new UTFDataFormatException("bad second byte");
+        }
+        out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
+      } else if ((a & 0xf0) == 0xe0) {
+        int b = content[p++] & 0xff;
+        int c = content[p++] & 0xff;
+        if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
+          throw new UTFDataFormatException("bad second or third byte");
+        }
+        out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
+      } else {
+        throw new UTFDataFormatException("bad byte");
+      }
+    }
+  }
+
+  // Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
+  private static int countBytes(String string) {
+    int result = 0;
+    for (int i = 0; i < string.length(); ++i) {
+      char ch = string.charAt(i);
+      if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+        ++result;
+      } else if (ch <= 2047) {
+        result += 2;
+      } else {
+        result += 3;
+      }
+      assert result > 0;
+    }
+    // We need an extra byte for the terminating '0'.
+    return result + 1;
+  }
+
+  // Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
+  private static byte[] encode(String string) {
+    byte[] result = new byte[countBytes(string)];
+    int offset = 0;
+    for (int i = 0; i < string.length(); i++) {
+      char ch = string.charAt(i);
+      if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
+        result[offset++] = (byte) ch;
+      } else if (ch <= 2047) {
+        result[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
+        result[offset++] = (byte) (0x80 | (0x3f & ch));
+      } else {
+        result[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
+        result[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
+        result[offset++] = (byte) (0x80 | (0x3f & ch));
+      }
+    }
+    result[offset] = 0;
+    return result;
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection indexedItems) {
+    indexedItems.addString(this);
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  @Override
+  public int compareTo(DexString other) {
+    return sortedCompareTo(other.getSortedIndex());
+  }
+
+  @Override
+  public int slowCompareTo(DexString other) {
+    // Compare the bytes, as comparing UTF-8 encoded strings as strings of unsigned bytes gives
+    // the same result as comparing the corresponding Unicode strings lexicographically by
+    // codepoint. The only complication is the MUTF-8 encoding have the two byte encoding c0 80 of
+    // the null character (U+0000) to allow embedded null characters.
+    // Supplementary characters (unicode code points above U+FFFF) are always represented as
+    // surrogate pairs and are compared using UTF-16 code units as per Java string semantics.
+    int index = 0;
+    while (true) {
+      char b1 = (char) (content[index] & 0xff);
+      char b2 = (char) (other.content[index] & 0xff);
+      int diff = b1 - b2;
+      if (diff != 0) {
+        // Check if either string ends here.
+        if (b1 == 0 || b2 == 0) {
+          return diff;
+        }
+        // If either of the strings have the null character starting here, the null character
+        // sort lowest.
+        if (b1 == 0xc0 && (content[index + 1] & 0xff) == 0x80 ||
+            b2 == 0xc0 && (other.content[index + 1] & 0xff) == 0x80) {
+          return b1 == 0xc0 && (content[index + 1] & 0xff) == 0x80 ? -1 : 1;
+        }
+        return diff;
+      } else if (b1 == 0) {
+        // Reached the end in both strings.
+        return 0;
+      }
+      index++;
+    }
+  }
+
+  @Override
+  public int slowCompareTo(DexString other, NamingLens lens) {
+    // The naming lens cannot affect strings.
+    return slowCompareTo(other);
+  }
+
+  @Override
+  public int layeredCompareTo(DexString other, NamingLens lens) {
+    // Strings have no subparts that are already sorted.
+    return slowCompareTo(other);
+  }
+
+  public String dump() {
+    StringBuilder builder = new StringBuilder();
+    builder.append(toString());
+    builder.append(" [");
+    for (int i = 0; i < content.length; i++) {
+      if (i > 0) {
+        builder.append(" ");
+      }
+      builder.append(Integer.toHexString(content[i] & 0xff));
+    }
+    builder.append("]");
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
new file mode 100644
index 0000000..1b70497
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -0,0 +1,424 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class DexType extends IndexedDexItem implements PresortedComparable<DexType> {
+
+  private final static int ROOT_LEVEL = 0;
+  private final static int UNKNOWN_LEVEL = -1;
+  private final static int INTERFACE_LEVEL = -2;
+
+  // Since most Java types has no sub-types, we can just share an empty immutable set until we need
+  // to add to it.
+  private final static Set<DexType> NO_DIRECT_SUBTYPE = ImmutableSet.of();
+
+  public final DexString descriptor;
+  private String toStringCache = null;
+  private int hierarchyLevel = UNKNOWN_LEVEL;
+
+  private Set<DexType> directSubtypes = NO_DIRECT_SUBTYPE;
+
+  DexType(DexString descriptor) {
+    assert !descriptor.toString().contains(".");
+    this.descriptor = descriptor;
+  }
+
+  public int computeHashCode() {
+    return descriptor.hashCode();
+  }
+
+  public boolean computeEquals(Object other) {
+    if (other instanceof DexType) {
+      return descriptor.equals(((DexType) other).descriptor);
+    }
+    return false;
+  }
+
+  private void ensureDirectSubTypeSet() {
+    if (directSubtypes == NO_DIRECT_SUBTYPE) {
+      directSubtypes = Sets.newIdentityHashSet();
+    }
+  }
+
+  private void setLevel(int level) {
+    if (level == hierarchyLevel) {
+      return;
+    }
+    if (hierarchyLevel == INTERFACE_LEVEL) {
+      assert level == ROOT_LEVEL + 1;
+    } else if (level == INTERFACE_LEVEL) {
+      assert hierarchyLevel == ROOT_LEVEL + 1 || hierarchyLevel == UNKNOWN_LEVEL;
+      hierarchyLevel = INTERFACE_LEVEL;
+    } else {
+      assert hierarchyLevel == UNKNOWN_LEVEL;
+      hierarchyLevel = level;
+    }
+  }
+
+  public void addDirectSubtype(DexType type) {
+    assert hierarchyLevel != UNKNOWN_LEVEL;
+    ensureDirectSubTypeSet();
+    directSubtypes.add(type);
+    type.setLevel(hierarchyLevel + 1);
+  }
+
+  public void tagAsSubtypeRoot() {
+    setLevel(ROOT_LEVEL);
+  }
+
+  public boolean isInterface() {
+    assert isClassType() && hierarchyLevel != UNKNOWN_LEVEL;
+    return hierarchyLevel == INTERFACE_LEVEL;
+  }
+
+  public void addInterfaceSubtype(DexType type) {
+    // Interfaces all inherit from java.lang.Object. However, we assign a special level to
+    // identify them later on.
+    setLevel(INTERFACE_LEVEL);
+    ensureDirectSubTypeSet();
+    directSubtypes.add(type);
+  }
+
+  static void clearSubtypeInformation(DexType type) {
+    type.hierarchyLevel = UNKNOWN_LEVEL;
+    type.directSubtypes = NO_DIRECT_SUBTYPE;
+  }
+
+  public boolean isSubtypeOf(DexType other, AppInfo appInfo) {
+    if (this == other) {
+      return true;
+    }
+    // Treat the object class special as it is always the supertype, even in the case of broken
+    // subtype chains.
+    if (this == appInfo.dexItemFactory.objectType) {
+      return false;
+    }
+    if (other == appInfo.dexItemFactory.objectType) {
+      return true;
+    }
+    if (this.hierarchyLevel == INTERFACE_LEVEL) {
+      return isInterfaceSubtypeOf(this, other, appInfo);
+    }
+    if (other.hierarchyLevel == INTERFACE_LEVEL) {
+      return other.directSubtypes.stream().anyMatch(subtype -> this.isSubtypeOf(subtype,
+          appInfo));
+    }
+    return isSubtypeOfClass(other, appInfo);
+  }
+
+  private boolean isInterfaceSubtypeOf(DexType candidate, DexType other, AppInfo appInfo) {
+    if (candidate == other || other == appInfo.dexItemFactory.objectType) {
+      return true;
+    }
+    DexClass candidateHolder = appInfo.definitionFor(candidate);
+    if (candidateHolder == null) {
+      return false;
+    }
+    for (DexType iface : candidateHolder.interfaces.values) {
+      assert iface.hierarchyLevel == INTERFACE_LEVEL;
+      if (isInterfaceSubtypeOf(iface, other, appInfo)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean isSubtypeOfClass(DexType other, AppInfo appInfo) {
+    DexType self = this;
+    while (other.hierarchyLevel < self.hierarchyLevel) {
+      DexClass holder = appInfo.definitionFor(self);
+      assert holder != null && !holder.isInterface();
+      self = holder.superType;
+    }
+    return self == other;
+  }
+
+  /**
+   * Apply the given function to all classes that directly extend this class.
+   *
+   * If this class is an interface, then this method will visit all sub-interfaces. This deviates
+   * from the dex-file encoding, where subinterfaces "implement" their super interfaces. However,
+   * it is consistent with the source language.
+   */
+  public void forAllExtendsSubtypes(Consumer<DexType> f) {
+    assert hierarchyLevel != UNKNOWN_LEVEL;
+    if (hierarchyLevel == INTERFACE_LEVEL) {
+      for (DexType subtype : directSubtypes) {
+        // Other interfaces that extend this interface.
+        if (subtype.hierarchyLevel == INTERFACE_LEVEL) {
+          f.accept(subtype);
+        }
+      }
+    } else if (hierarchyLevel == ROOT_LEVEL) {
+      // This is the object type. Filter out interfaces
+      for (DexType subtype : directSubtypes) {
+        // Other interfaces that extend this interface.
+        if (subtype.hierarchyLevel != INTERFACE_LEVEL) {
+          f.accept(subtype);
+        }
+      }
+    } else {
+      directSubtypes.forEach(f);
+    }
+  }
+
+  /**
+   * Apply the given function to all classes that directly implement this interface.
+   *
+   * The implementation does not consider how the hierarchy is encoded in the dex file, where
+   * interfaces "implement" their super interfaces. Instead it takes the view of the source
+   * language, where interfaces "extend" their superinterface.
+   */
+  public void forAllImplementsSubtypes(Consumer<DexType> f) {
+    if (hierarchyLevel != INTERFACE_LEVEL) {
+      return;
+    }
+    for (DexType subtype : directSubtypes) {
+      // Filter out other interfaces.
+      if (subtype.hierarchyLevel != INTERFACE_LEVEL) {
+        f.accept(subtype);
+      }
+    }
+  }
+
+  public static void forAllInterfaces(DexItemFactory factory, Consumer<DexType> f) {
+    DexType object = factory.objectType;
+    assert object.hierarchyLevel == ROOT_LEVEL;
+    for (DexType subtype : object.directSubtypes) {
+      if (subtype.isInterface()) {
+        f.accept(subtype);
+      }
+    }
+  }
+
+  public boolean isSamePackage(DexType other) {
+    return getPackageDescriptor().equals(other.getPackageDescriptor());
+  }
+
+  public String toDescriptorString() {
+    return descriptor.toString();
+  }
+
+  public String toSourceString() {
+    if (toStringCache == null) {
+      // TODO(ager): Pass in a ProguardMapReader to map names back to original names.
+      if (this == DexItemFactory.catchAllType) {
+        toStringCache = "CATCH_ALL";
+      } else {
+        toStringCache = DescriptorUtils.descriptorToJavaType(toDescriptorString());
+      }
+    }
+    return toStringCache;
+  }
+
+  public char toShorty() {
+    char c = (char) descriptor.content[0];
+    return c == '[' ? 'L' : c;
+  }
+
+  @Override
+  public String toSmaliString() {
+    return toDescriptorString();
+  }
+
+  @Override
+  public String toString() {
+    return toSourceString();
+  }
+
+  @Override
+  public void collectIndexedItems(IndexedItemCollection collection) {
+    if (collection.addType(this)) {
+      collection.getRenamedDescriptor(this).collectIndexedItems(collection);
+    }
+  }
+
+  @Override
+  public void flushCachedValues() {
+    super.flushCachedValues();
+    toStringCache = null;
+  }
+
+  @Override
+  public int getOffset(ObjectToOffsetMapping mapping) {
+    return mapping.getOffsetFor(this);
+  }
+
+  @Override
+  public int compareTo(DexType other) {
+    return sortedCompareTo(other.getSortedIndex());
+  }
+
+  @Override
+  public int slowCompareTo(DexType other) {
+    return descriptor.slowCompareTo(other.descriptor);
+  }
+
+  @Override
+  public int slowCompareTo(DexType other, NamingLens namingLens) {
+    DexString thisDescriptor = namingLens.lookupDescriptor(this);
+    DexString otherDescriptor = namingLens.lookupDescriptor(other);
+    return thisDescriptor.slowCompareTo(otherDescriptor);
+  }
+
+  @Override
+  public int layeredCompareTo(DexType other, NamingLens namingLens) {
+    DexString thisDescriptor = namingLens.lookupDescriptor(this);
+    DexString otherDescriptor = namingLens.lookupDescriptor(other);
+    return thisDescriptor.compareTo(otherDescriptor);
+  }
+
+  public boolean isPrimitiveType() {
+    char firstChar = (char) descriptor.content[0];
+    return firstChar != 'L' && firstChar != '[';
+  }
+
+  public boolean isVoidType() {
+    return (char) descriptor.content[0] == 'V';
+  }
+
+  public boolean isArrayType() {
+    char firstChar = (char) descriptor.content[0];
+    return firstChar == '[';
+  }
+
+  public boolean isClassType() {
+    char firstChar = (char) descriptor.content[0];
+    return firstChar == 'L';
+  }
+
+  public boolean isPrimitiveArrayType() {
+    if (!isArrayType()) {
+      return false;
+    }
+    switch (descriptor.content[1]) {
+      case 'Z':  // boolean
+      case 'B':  // byte
+      case 'S':  // short
+      case 'C':  // char
+      case 'I':  // int
+      case 'F':  // float
+      case 'J':  // long
+      case 'D':  // double
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  public int elementSizeForPrimitiveArrayType() {
+    assert isPrimitiveArrayType();
+    switch (descriptor.content[1]) {
+      case 'Z':  // boolean
+      case 'B':  // byte
+        return 1;
+      case 'S':  // short
+      case 'C':  // char
+        return 2;
+      case 'I':  // int
+      case 'F':  // float
+        return 4;
+      case 'J':  // long
+      case 'D':  // double
+        return 8;
+      default:
+        throw new Unreachable("Not array of primitives '" + descriptor + "'");
+    }
+  }
+
+  public DexType toBaseType(DexItemFactory dexItemFactory) {
+    int leadingSquareBrackets = 0;
+    while (descriptor.content[leadingSquareBrackets] == '[') {
+      leadingSquareBrackets++;
+    }
+    if (leadingSquareBrackets == 0) {
+      return this;
+    }
+    DexString newDesc = dexItemFactory.createString(descriptor.size - leadingSquareBrackets,
+        Arrays.copyOfRange(descriptor.content, leadingSquareBrackets, descriptor.content.length));
+    return dexItemFactory.createType(newDesc);
+  }
+
+  public DexType toArrayElementType(DexItemFactory dexItemFactory) {
+    assert this.isArrayType();
+    DexString newDesc = dexItemFactory.createString(descriptor.size - 1,
+        Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
+    return dexItemFactory.createType(newDesc);
+  }
+
+  static boolean validateLevelsAreCorrect(Function<DexType, DexClass> definitions,
+      DexItemFactory dexItemFactory) {
+    Set<DexType> seenTypes = Sets.newIdentityHashSet();
+    Deque<DexType> worklist = new ArrayDeque<>();
+    DexType objectType = dexItemFactory.objectType;
+    worklist.add(objectType);
+    while (!worklist.isEmpty()) {
+      DexType next = worklist.pop();
+      DexClass nextHolder = definitions.apply(next);
+      DexType superType;
+      if (nextHolder == null) {
+        // We might lack the definition of Object, so guard against that.
+        superType = next == dexItemFactory.objectType ? null : dexItemFactory.objectType;
+      } else {
+        superType = nextHolder.superType;
+      }
+      assert !seenTypes.contains(next);
+      seenTypes.add(next);
+      if (superType == null) {
+        assert next.hierarchyLevel == ROOT_LEVEL;
+      } else {
+        assert superType.hierarchyLevel == next.hierarchyLevel - 1
+            || superType.hierarchyLevel == ROOT_LEVEL && next.hierarchyLevel == INTERFACE_LEVEL;
+        assert superType.directSubtypes.contains(next);
+      }
+      if (next.hierarchyLevel != INTERFACE_LEVEL) {
+        // Only traverse the class hierarchy subtypes, not interfaces.
+        worklist.addAll(next.directSubtypes);
+      } else if (nextHolder != null) {
+        // Test that the interfaces of this class are interfaces and have this class as subtype.
+        for (DexType iface : nextHolder.interfaces.values) {
+          assert iface.directSubtypes.contains(next);
+          assert iface.hierarchyLevel == INTERFACE_LEVEL;
+        }
+      }
+    }
+    return true;
+  }
+
+  private String getPackageOrName(boolean packagePart) {
+    assert isClassType();
+    String descriptor = toDescriptorString();
+    int lastSeparator = descriptor.lastIndexOf('/');
+    if (lastSeparator == -1) {
+      return packagePart ? "" : descriptor.substring(1, descriptor.length() - 1);
+    } else {
+      return packagePart ? descriptor.substring(1, lastSeparator)
+          : descriptor.substring(lastSeparator + 1, descriptor.length() - 1);
+    }
+  }
+
+  public String getPackageDescriptor() {
+    return getPackageOrName(true);
+  }
+
+  public String getName() {
+    if (isPrimitiveType()) {
+      return toSourceString();
+    }
+    return getPackageOrName(false);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
new file mode 100644
index 0000000..39226d8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.Arrays;
+
+public class DexTypeList extends DexItem implements Comparable<DexTypeList> {
+
+  private static final DexTypeList theEmptyTypeList = new DexTypeList();
+
+  public final DexType[] values;
+
+  public static DexTypeList empty() {
+    return theEmptyTypeList;
+  }
+
+  private DexTypeList() {
+    this.values = new DexType[0];
+  }
+
+  public DexTypeList(DexType[] values) {
+    assert values != null && values.length > 0;
+    this.values = values;
+  }
+
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(values);
+  }
+
+  @Override
+  void collectIndexedItems(IndexedItemCollection indexedItems) {
+    for (DexType type : values) {
+      type.collectIndexedItems(indexedItems);
+    }
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    mixedItems.add(this);
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (this == other) {
+      return true;
+    }
+    return (other instanceof DexTypeList)
+        && Arrays.equals(values, ((DexTypeList) other).values);
+  }
+
+  public boolean isEmpty() {
+    return values.length == 0;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    for (DexType type : values) {
+      builder.append(' ').append(type);
+    }
+    return builder.toString();
+  }
+
+  @Override
+  public int compareTo(DexTypeList other) {
+    for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
+      if (i == values.length) {
+        return i == other.values.length ? 0 : -1;
+      } else if (i == other.values.length) {
+        return 1;
+      } else {
+        int result = values[i].compareTo(other.values[i]);
+        if (result != 0) {
+          return result;
+        }
+      }
+    }
+    throw new Unreachable();
+  }
+
+  public int slowCompareTo(DexTypeList other) {
+    for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
+      if (i == values.length) {
+        return i == other.values.length ? 0 : -1;
+      } else if (i == other.values.length) {
+        return 1;
+      } else {
+        int result = values[i].slowCompareTo(other.values[i]);
+        if (result != 0) {
+          return result;
+        }
+      }
+    }
+    throw new Unreachable();
+  }
+
+  public int slowCompareTo(DexTypeList other, NamingLens namingLens) {
+    for (int i = 0; i <= Math.min(values.length, other.values.length); i++) {
+      if (i == values.length) {
+        return i == other.values.length ? 0 : -1;
+      } else if (i == other.values.length) {
+        return 1;
+      } else {
+        int result = values[i].slowCompareTo(other.values[i], namingLens);
+        if (result != 0) {
+          return result;
+        }
+      }
+    }
+    throw new Unreachable();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
new file mode 100644
index 0000000..5430d6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -0,0 +1,783 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.DexOutputBuffer;
+import com.android.tools.r8.dex.FileWriter;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.EncodedValueUtils;
+import java.util.Arrays;
+
+public abstract class DexValue extends DexItem {
+
+  public static final byte VALUE_BYTE = 0x00;
+  public static final byte VALUE_SHORT = 0x02;
+  public static final byte VALUE_CHAR = 0x03;
+  public static final byte VALUE_INT = 0x04;
+  public static final byte VALUE_LONG = 0x06;
+  public static final byte VALUE_FLOAT = 0x10;
+  public static final byte VALUE_DOUBLE = 0x11;
+  public static final byte VALUE_METHOD_TYPE = 0x15;
+  public static final byte VALUE_METHOD_HANDLE = 0x16;
+  public static final byte VALUE_STRING = 0x17;
+  public static final byte VALUE_TYPE = 0x18;
+  public static final byte VALUE_FIELD = 0x19;
+  public static final byte VALUE_METHOD = 0x1a;
+  public static final byte VALUE_ENUM = 0x1b;
+  public static final byte VALUE_ARRAY = 0x1c;
+  public static final byte VALUE_ANNOTATION = 0x1d;
+  public static final byte VALUE_NULL = 0x1e;
+  public static final byte VALUE_BOOLEAN = 0x1f;
+
+  public static final DexValue NULL = new DexValueNull();
+
+  private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
+    dest.putByte((byte) ((arg << 5) | type));
+  }
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Should never be visited.
+    assert false;
+  }
+
+  public abstract void sort();
+
+  public abstract void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping);
+
+  @Override
+  public abstract int hashCode();
+
+  @Override
+  public abstract boolean equals(Object other);
+
+  @Override
+  public abstract String toString();
+
+  public static DexValue defaultForType(DexType type, DexItemFactory factory) {
+    if (type == factory.booleanType) {
+      return DexValueBoolean.DEFAULT;
+    }
+    if (type == factory.byteType) {
+      return DexValueByte.DEFAULT;
+    }
+    if (type == factory.charType) {
+      return DexValueChar.DEFAULT;
+    }
+    if (type == factory.shortType) {
+      return DexValueShort.DEFAULT;
+    }
+    if (type == factory.intType) {
+      return DexValueInt.DEFAULT;
+    }
+    if (type == factory.longType) {
+      return DexValueLong.DEFAULT;
+    }
+    if (type == factory.floatType) {
+      return DexValueFloat.DEFAULT;
+    }
+    if (type == factory.doubleType) {
+      return DexValueDouble.DEFAULT;
+    }
+    if (type.isArrayType() || type.isClassType()) {
+      return DexValue.NULL;
+    }
+    throw new Unreachable("No default value for unexpected type " + type);
+  }
+
+  // Returns a const instruction for the non default value.
+  public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+    return null;
+  }
+
+  public boolean isDefault(DexType type, DexItemFactory factory) {
+    return this == defaultForType(type, factory);
+  }
+
+  static private abstract class SimpleDexValue extends DexValue {
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      // Intentionally left empty
+    }
+
+    @Override
+    public void sort() {
+      // Intentionally empty
+    }
+
+    protected static void writeIntegerTo(byte type, long value, int expected,
+        DexOutputBuffer dest) {
+      // Leave space for header.
+      dest.forward(1);
+      int length = dest.putSignedEncodedValue(value, expected);
+      dest.rewind(length + 1);
+      writeHeader(type, length - 1, dest);
+      dest.forward(length);
+    }
+
+  }
+
+  static public class DexValueByte extends SimpleDexValue {
+
+    public static final DexValueByte DEFAULT = new DexValueByte((byte) 0);
+
+    final byte value;
+
+    private DexValueByte(byte value) {
+      this.value = value;
+    }
+
+    public static DexValueByte create(byte value) {
+      return value == DEFAULT.value ? DEFAULT : new DexValueByte(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeHeader(VALUE_BYTE, 0, dest);
+      dest.putSignedEncodedValue(value, 1);
+    }
+
+    @Override
+    public int hashCode() {
+      return value * 3;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return other instanceof DexValueByte && value == ((DexValueByte) other).value;
+    }
+
+    @Override
+    public String toString() {
+      return "Byte " + value;
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return (this == DEFAULT && hasClassInitializer)
+          ? null
+          : new ConstNumber(ConstType.INT, dest, value);
+    }
+  }
+
+  static public class DexValueShort extends SimpleDexValue {
+
+    public static final DexValueShort DEFAULT = new DexValueShort((short) 0);
+    final short value;
+
+    private DexValueShort(short value) {
+      this.value = value;
+    }
+
+    public static DexValueShort create(short value) {
+      return value == DEFAULT.value ? DEFAULT : new DexValueShort(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeIntegerTo(VALUE_SHORT, value, Short.BYTES, dest);
+    }
+
+    @Override
+    public int hashCode() {
+      return value * 7;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return other instanceof DexValueShort && value == ((DexValueShort) other).value;
+    }
+
+    @Override
+    public String toString() {
+      return "Short " + value;
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return (this == DEFAULT && hasClassInitializer)
+          ? null
+          : new ConstNumber(ConstType.INT, dest, value);
+    }
+  }
+
+  static public class DexValueChar extends SimpleDexValue {
+
+    public static final DexValueChar DEFAULT = new DexValueChar((char) 0);
+    final char value;
+
+    private DexValueChar(char value) {
+      this.value = value;
+    }
+
+    public static DexValueChar create(char value) {
+      return value == DEFAULT.value ? DEFAULT : new DexValueChar(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      dest.forward(1);
+      int length = dest.putUnsignedEncodedValue(value, 2);
+      dest.rewind(length + 1);
+      writeHeader(VALUE_CHAR, length - 1, dest);
+      dest.forward(length);
+    }
+
+    @Override
+    public int hashCode() {
+      return value * 5;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return other instanceof DexValueChar && value == ((DexValueChar) other).value;
+    }
+
+    @Override
+    public String toString() {
+      return "Char " + value;
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return (this == DEFAULT && hasClassInitializer)
+          ? null
+          : new ConstNumber(ConstType.INT, dest, value);
+    }
+  }
+
+  static public class DexValueInt extends SimpleDexValue {
+
+    public static final DexValueInt DEFAULT = new DexValueInt(0);
+    public final int value;
+
+    private DexValueInt(int value) {
+      this.value = value;
+    }
+
+    public static DexValueInt create(int value) {
+      return value == DEFAULT.value ? DEFAULT : new DexValueInt(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeIntegerTo(VALUE_INT, value, Integer.BYTES, dest);
+    }
+
+    @Override
+    public int hashCode() {
+      return value * 11;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return other instanceof DexValueInt && value == ((DexValueInt) other).value;
+    }
+
+    @Override
+    public String toString() {
+      return "Int " + value;
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return (this == DEFAULT && hasClassInitializer)
+          ? null
+          : new ConstNumber(ConstType.INT, dest, value);
+    }
+  }
+
+  static public class DexValueLong extends SimpleDexValue {
+
+    public static final DexValueLong DEFAULT = new DexValueLong(0);
+    final long value;
+
+    private DexValueLong(long value) {
+      this.value = value;
+    }
+
+    public static DexValueLong create(long value) {
+      return value == DEFAULT.value ? DEFAULT : new DexValueLong(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeIntegerTo(VALUE_LONG, value, Long.BYTES, dest);
+    }
+
+    @Override
+    public int hashCode() {
+      return (int) value * 13;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return other instanceof DexValueLong && value == ((DexValueLong) other).value;
+    }
+
+    @Override
+    public String toString() {
+      return "Long " + value;
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return (this == DEFAULT && hasClassInitializer)
+          ? null
+          : new ConstNumber(ConstType.LONG, dest, value);
+    }
+  }
+
+  static public class DexValueFloat extends SimpleDexValue {
+
+    public static final DexValueFloat DEFAULT = new DexValueFloat(0);
+    final float value;
+
+    private DexValueFloat(float value) {
+      this.value = value;
+    }
+
+    public static DexValueFloat create(float value) {
+      return Float.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueFloat(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      dest.forward(1);
+      int length = EncodedValueUtils.putFloat(dest, value);
+      dest.rewind(length + 1);
+      writeHeader(VALUE_FLOAT, length - 1, dest);
+      dest.forward(length);
+    }
+
+    @Override
+    public int hashCode() {
+      return (int) value * 19;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return (other instanceof DexValueFloat) &&
+          (Float.compare(value, ((DexValueFloat) other).value) == 0);
+    }
+
+    @Override
+    public String toString() {
+      return "Float " + value;
+    }
+
+  }
+
+  static public class DexValueDouble extends SimpleDexValue {
+
+    public static final DexValueDouble DEFAULT = new DexValueDouble(0);
+
+    final double value;
+
+    private DexValueDouble(double value) {
+      this.value = value;
+    }
+
+    public static DexValueDouble create(double value) {
+      return Double.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueDouble(value);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      dest.forward(1);
+      int length = EncodedValueUtils.putDouble(dest, value);
+      dest.rewind(length + 1);
+      writeHeader(VALUE_DOUBLE, length - 1, dest);
+      dest.forward(length);
+    }
+
+    @Override
+    public int hashCode() {
+      return (int) value * 29;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return (other instanceof DexValueDouble) &&
+          (Double.compare(value, ((DexValueDouble) other).value) == 0);
+    }
+
+    @Override
+    public String toString() {
+      return "Double " + value;
+    }
+  }
+
+  static private abstract class NestedDexValue<T extends IndexedDexItem> extends DexValue {
+
+    public final T value;
+
+    private NestedDexValue(T value) {
+      this.value = value;
+    }
+
+    protected abstract byte getValueKind();
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      int offset = value.getOffset(mapping);
+      dest.forward(1);
+      int length = dest.putUnsignedEncodedValue(offset, 4);
+      dest.rewind(length + 1);
+      writeHeader(getValueKind(), length - 1, dest);
+      dest.forward(length);
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+
+    @Override
+    public void sort() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public int hashCode() {
+      return value.hashCode() * 7 + getValueKind();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      if (other instanceof NestedDexValue) {
+        NestedDexValue<?> that = (NestedDexValue<?>) other;
+        return that.getValueKind() == getValueKind() && that.value.equals(value);
+      }
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return "Item " + getValueKind() + " " + value;
+    }
+  }
+
+  static public class DexValueString extends NestedDexValue<DexString> {
+
+    public DexValueString(DexString value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_STRING;
+    }
+  }
+
+  static public class DexValueType extends NestedDexValue<DexType> {
+
+    public DexValueType(DexType value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_TYPE;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+  }
+
+  static public class DexValueField extends NestedDexValue<DexField> {
+
+    public DexValueField(DexField value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_FIELD;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+  }
+
+  static public class DexValueMethod extends NestedDexValue<DexMethod> {
+
+    public DexValueMethod(DexMethod value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_METHOD;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+  }
+
+  static public class DexValueEnum extends NestedDexValue<DexField> {
+
+    public DexValueEnum(DexField value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_ENUM;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+  }
+
+  static public class DexValueMethodType extends NestedDexValue<DexProto> {
+
+    public DexValueMethodType(DexProto value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_METHOD_TYPE;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+  }
+
+  static public class DexValueArray extends DexValue {
+
+    final DexValue[] values;
+
+    public DexValueArray(DexValue[] values) {
+      this.values = values;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      collectAll(indexedItems, values);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeHeader(VALUE_ARRAY, 0, dest);
+      dest.putUleb128(values.length);
+      for (DexValue value : values) {
+        value.writeTo(dest, mapping);
+      }
+    }
+
+    @Override
+    public void sort() {
+      for (DexValue value : values) {
+        value.sort();
+      }
+    }
+
+    @Override
+    public int hashCode() {
+      return Arrays.hashCode(values);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      if (other instanceof DexValueArray) {
+        DexValueArray that = (DexValueArray) other;
+        return Arrays.equals(that.values, values);
+      }
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return "Array " + Arrays.toString(values);
+    }
+  }
+
+  static public class DexValueAnnotation extends DexValue {
+
+    public final DexEncodedAnnotation value;
+
+    public DexValueAnnotation(DexEncodedAnnotation value) {
+      this.value = value;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeHeader(VALUE_ANNOTATION, 0, dest);
+      FileWriter.writeEncodedAnnotation(value, dest, mapping);
+    }
+
+    @Override
+    public void sort() {
+      value.sort();
+    }
+
+    @Override
+    public int hashCode() {
+      return value.hashCode() * 7;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      if (other instanceof DexValueAnnotation) {
+        DexValueAnnotation that = (DexValueAnnotation) other;
+        return that.value.equals(value);
+      }
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return "Annotation " + value;
+    }
+  }
+
+  static public class DexValueNull extends SimpleDexValue {
+
+    // See DexValue.NULL
+    private DexValueNull() {
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeHeader(VALUE_NULL, 0, dest);
+    }
+
+    @Override
+    public int hashCode() {
+      return 42;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return (other instanceof DexValueNull);
+    }
+
+    @Override
+    public String toString() {
+      return "Null";
+    }
+  }
+
+  static public class DexValueBoolean extends SimpleDexValue {
+
+    private static final DexValueBoolean TRUE = new DexValueBoolean(true);
+    private static final DexValueBoolean FALSE = new DexValueBoolean(false);
+    // Use a separate instance for the default value to distinguish it from an explicit false value.
+    private static final DexValueBoolean DEFAULT = new DexValueBoolean(false);
+
+    final boolean value;
+
+    private DexValueBoolean(boolean value) {
+      this.value = value;
+    }
+
+    public static DexValueBoolean create(boolean value) {
+      return value ? TRUE : FALSE;
+    }
+
+    @Override
+    public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
+      writeHeader(VALUE_BOOLEAN, value ? 1 : 0, dest);
+    }
+
+    @Override
+    public int hashCode() {
+      return value ? 1234 : 4321;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other == this) {
+        return true;
+      }
+      return (other instanceof DexValueBoolean) && ((DexValueBoolean) other).value == value;
+    }
+
+    @Override
+    public String toString() {
+      return value ? "True" : "False";
+    }
+
+    @Override
+    public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
+      return (this == DEFAULT && hasClassInitializer)
+          ? null
+          : new ConstNumber(ConstType.INT, dest, value ? 1 : 0);
+    }
+  }
+
+  static public class DexValueMethodHandle extends NestedDexValue<DexMethodHandle> {
+
+    public DexValueMethodHandle(DexMethodHandle value) {
+      super(value);
+    }
+
+    @Override
+    protected byte getValueKind() {
+      return VALUE_METHOD_HANDLE;
+    }
+
+    @Override
+    public void collectIndexedItems(IndexedItemCollection indexedItems) {
+      value.collectIndexedItems(indexedItems);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
new file mode 100644
index 0000000..42f7c6e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public abstract class GraphLense {
+
+  public static class Builder {
+
+    private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+    private final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
+    private final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
+
+    public void map(DexType from, DexType to) {
+      typeMap.put(from, to);
+    }
+
+    public void map(DexMethod from, DexMethod to) {
+      methodMap.put(from, to);
+    }
+
+    public void map(DexField from, DexField to) {
+      fieldMap.put(from, to);
+    }
+
+    public GraphLense build() {
+      return build(new IdentityGraphLense());
+    }
+
+    public GraphLense build(GraphLense previousLense) {
+      return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense);
+    }
+
+  }
+
+  public abstract DexType lookupType(DexType type, DexEncodedMethod context);
+
+  public abstract DexMethod lookupMethod(DexMethod method, DexEncodedMethod context);
+
+  public abstract DexField lookupField(DexField field, DexEncodedMethod context);
+
+  public abstract boolean isContextFree();
+
+  public static GraphLense getIdentityLense() {
+    return new IdentityGraphLense();
+  }
+
+  public final boolean isIdentityLense() {
+    return this instanceof IdentityGraphLense;
+  }
+
+  private static class IdentityGraphLense extends GraphLense {
+
+    @Override
+    public DexType lookupType(DexType type, DexEncodedMethod context) {
+      return type;
+    }
+
+    @Override
+    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
+      return method;
+    }
+
+    @Override
+    public DexField lookupField(DexField field, DexEncodedMethod context) {
+      return field;
+    }
+
+    @Override
+    public boolean isContextFree() {
+      return true;
+    }
+  }
+
+  private static class NestedGraphLense extends GraphLense {
+
+    private final GraphLense previousLense;
+
+    private final Map<DexType, DexType> typeMap;
+    private final Map<DexMethod, DexMethod> methodMap;
+    private final Map<DexField, DexField> fieldMap;
+
+    private NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
+        Map<DexField, DexField> fieldMap, GraphLense previousLense) {
+      this.typeMap = typeMap;
+      this.methodMap = methodMap;
+      this.fieldMap = fieldMap;
+      this.previousLense = previousLense;
+    }
+
+    @Override
+    public DexType lookupType(DexType type, DexEncodedMethod context) {
+      DexType previous = previousLense.lookupType(type, context);
+      return typeMap.getOrDefault(previous, previous);
+    }
+
+    @Override
+    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
+      DexMethod previous = previousLense.lookupMethod(method, context);
+      return methodMap.getOrDefault(previous, previous);
+    }
+
+    @Override
+    public DexField lookupField(DexField field, DexEncodedMethod context) {
+      DexField previous = previousLense.lookupField(field, context);
+      return fieldMap.getOrDefault(previous, previous);
+    }
+
+    @Override
+    public boolean isContextFree() {
+      return previousLense.isContextFree();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
new file mode 100644
index 0000000..6c7f3b3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/IndexedDexItem.java
@@ -0,0 +1,159 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import java.util.Arrays;
+
+/**
+ * Subset of dex items that are referenced by some table index.
+ */
+public abstract class IndexedDexItem extends CanonicalizedDexItem implements Presorted {
+
+  private static final int SORTED_INDEX_UNKNOWN = -1;
+  private int sortedIndex = SORTED_INDEX_UNKNOWN; // assigned globally after reading.
+  /**
+   * Contains the indexes assigned to this item for the various virtual output files.
+   *
+   * <p>One DexItem might be assigned to multiple virtual files.
+   *
+   * <p>For a certain virtual file this DexItem has the value:
+   * <ul>
+   * <li>{@link #UNASSOCIATED_VALUE}, when not associated with the virtual file.
+   * <li>{@link #ASSOCIATED_VALUE}, when associated with the virtual file but no index allocated.
+   * <li>A zero or greater value when this item has been associated by the virtual file
+   * and the value denotes the assigned index.
+   * </ul>
+   * <p> Note that, in case of multiple files, for a specific IndexedDexItem, we may not have
+   * as many entries in the index as there are files (we only expand when we need to). If we lookup
+   * the value of an entry that is out of bounds it is equivalent to {@link #UNASSOCIATED_VALUE}
+   *
+   * <p>This field is initialized on first write in {@link #updateVirtualFileData(int, int)}}. It
+   * is assumed that multiple files are processed concurrently and thus the allocation of the
+   * array is synchronized. However, for any a given file id, sequential access is assumed.
+   */
+  private int[] virtualFileIndexes;
+
+  public abstract void collectIndexedItems(IndexedItemCollection indexedItems);
+
+  @Override
+  void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Should never be visited.
+    assert false;
+  }
+
+  public abstract int getOffset(ObjectToOffsetMapping mapping);
+
+  /**
+   * Constants used inside virtualFileIndexes.
+   */
+  public static final int UNASSOCIATED_VALUE = -2;
+  public static final int ASSOCIATED_VALUE = -1;
+  public static final int MIN_VALID_VALUE = 0;
+
+  /**
+   * Returns whether this item is assigned to the given file id.
+   */
+  public boolean hasVirtualFileData(int virtualFileId) {
+    return getVirtualFileIndex(virtualFileId) != UNASSOCIATED_VALUE;
+  }
+
+  /**
+   * Assigns this item to the given file id if it has not been assigned previously.
+   *
+   * <p>This method returns 'true' if the item was newly assigned, i.e., it was not previously
+   * assigned to the file id.
+   */
+  public boolean assignToVirtualFile(int virtualFileId) {
+    // Fast lock-free check whether already assigned.
+    if (hasVirtualFileData(virtualFileId)) {
+      return false;
+    }
+    return updateVirtualFileData(virtualFileId);
+  }
+
+  /**
+   * Assigns this item to the given file id.
+   *
+   * <p>As a side effect, the {@link #virtualFileIndexes} field might be initialized or expanded.
+   * Hence this method is synchronized. Note that the field is queried without synchronization.
+   * Therefor it has to remain in a valid state at all times and must transition atomically from
+   * null to an initialized allocated value.
+   */
+  private synchronized boolean updateVirtualFileData(int virtualFileId) {
+    if (virtualFileIndexes == null) {
+      int[] fileIndices = new int[virtualFileId + 1];
+      Arrays.fill(fileIndices, UNASSOCIATED_VALUE);
+      // This has to be an atomic transition from null to an initialized array.
+      virtualFileIndexes = fileIndices;
+    }
+    // We increased the number of files, increase the index size.
+    if (virtualFileId >= virtualFileIndexes.length) {
+      int oldLength = virtualFileIndexes.length;
+      int[] fileIndices = Arrays.copyOf(virtualFileIndexes, virtualFileId + 1);
+      Arrays.fill(fileIndices, oldLength, virtualFileId + 1, UNASSOCIATED_VALUE);
+      virtualFileIndexes = fileIndices;
+    }
+    assert virtualFileId < virtualFileIndexes.length;
+    boolean wasAdded = virtualFileIndexes[virtualFileId] == UNASSOCIATED_VALUE;
+    virtualFileIndexes[virtualFileId] = ASSOCIATED_VALUE;
+    return wasAdded;
+  }
+
+  /**
+   * Assigns an actual index for this item in the given file.
+   *
+   * <p>May only be used after this item has been assigned to the file using {@link
+   * #assignToVirtualFile(int, int)}.
+   */
+  public void assignVirtualFileIndex(int virtualFileId, int index) {
+    assert virtualFileIndexes != null;
+    assert virtualFileIndexes[virtualFileId] < MIN_VALID_VALUE;
+    virtualFileIndexes[virtualFileId] = index;
+  }
+
+  /**
+   * Returns the index associated with this item for the given file id or {@link
+   * #UNASSOCIATED_VALUE} if the item is not associated to the given file id.
+   */
+  public int getVirtualFileIndex(int virtualFileId) {
+    if (virtualFileIndexes == null) {
+      return UNASSOCIATED_VALUE;
+    }
+    // If more files were added, but this entry not associated with it, we would not have extended
+    // the size of the array. So if the {@link virtualFileId} is out of bounds, it means
+    // {@link #UNASSOCIATED_VALUE}
+    return virtualFileIndexes.length > virtualFileId
+        ? virtualFileIndexes[virtualFileId]
+        : UNASSOCIATED_VALUE;
+  }
+
+  // Partial implementation of PresortedComparable.
+
+  final public void setSortedIndex(int sortedIndex) {
+    assert sortedIndex > SORTED_INDEX_UNKNOWN;
+    assert this.sortedIndex == SORTED_INDEX_UNKNOWN;
+    this.sortedIndex = sortedIndex;
+  }
+
+  final public int getSortedIndex() {
+    return sortedIndex;
+  }
+
+  final public int sortedCompareTo(int other) {
+    assert sortedIndex > SORTED_INDEX_UNKNOWN;
+    assert other > SORTED_INDEX_UNKNOWN;
+    return Integer.compare(sortedIndex, other);
+  }
+
+  public void flushCachedValues() {
+    super.flushCachedValues();
+    resetSortedIndex();
+  }
+
+  public void resetSortedIndex() {
+    sortedIndex = SORTED_INDEX_UNKNOWN;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
new file mode 100644
index 0000000..b28ffb5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+import org.objectweb.asm.Type;
+
+/**
+ * Common structures used while reading in a Java application from jar files.
+ *
+ * The primary use of this class is to canonicalize dex items during read.
+ * The addition of classes to the builder also takes place through this class.
+ * It does not currently support multithreaded reading.
+ */
+public class JarApplicationReader {
+  public final InternalOptions options;
+
+  public JarApplicationReader(InternalOptions options) {
+    this.options = options;
+  }
+
+  public DexItemFactory getFactory() {
+    return options.itemFactory;
+  }
+
+  public DexString getString(String string) {
+    return options.itemFactory.createString(string);
+  }
+
+  public DexType getType(Type type) {
+    return getTypeFromDescriptor(type.getDescriptor());
+  }
+
+  public DexType getTypeFromName(String name) {
+    assert isValidInternalName(name);
+    return getType(Type.getObjectType(name));
+  }
+
+  public DexType getTypeFromDescriptor(String desc) {
+    assert isValidDescriptor(desc);
+    return options.itemFactory.createType(getString(desc));
+  }
+
+  public DexTypeList getTypeListFromNames(String[] names) {
+    if (names.length == 0) {
+      return DexTypeList.empty();
+    }
+    DexType[] types = new DexType[names.length];
+    for (int i = 0; i < names.length; i++) {
+      types[i] = getTypeFromName(names[i]);
+    }
+    return new DexTypeList(types);
+  }
+
+  public DexTypeList getTypeListFromDescriptors(String[] descriptors) {
+    if (descriptors.length == 0) {
+      return DexTypeList.empty();
+    }
+    DexType[] types = new DexType[descriptors.length];
+    for (int i = 0; i < descriptors.length; i++) {
+      types[i] = getTypeFromDescriptor(descriptors[i]);
+    }
+    return new DexTypeList(types);
+  }
+
+  public DexField getField(String owner, String name, String desc) {
+    return getField(getTypeFromName(owner), name, desc);
+  }
+
+  public DexField getField(DexType owner, String name, String desc) {
+    return options.itemFactory.createField(owner, getTypeFromDescriptor(desc), getString(name));
+  }
+
+  public DexMethod getMethod(String owner, String name, String desc) {
+    return getMethod(getTypeFromName(owner), name, desc);
+  }
+
+  public DexMethod getMethod(DexType owner, String name, String desc) {
+    return options.itemFactory.createMethod(owner, getProto(desc), getString(name));
+  }
+
+  public DexCallSite getCallSite(String methodName, String methodProto,
+      DexMethodHandle bootstrapMethod, List<DexValue> bootstrapArgs) {
+    return options.itemFactory.createCallSite(
+        getString(methodName), getProto(methodProto), bootstrapMethod, bootstrapArgs);
+  }
+
+  public DexMethodHandle getMethodHandle(
+      MethodHandleType type, Descriptor<? extends DexItem, ? extends Descriptor> fieldOrMethod) {
+    return options.itemFactory.createMethodHandle(type, fieldOrMethod);
+  }
+
+  public DexProto getProto(String desc) {
+    assert isValidDescriptor(desc);
+    Type returnType = Type.getReturnType(desc);
+    Type[] arguments = Type.getArgumentTypes(desc);
+
+    StringBuilder shortyDescriptor = new StringBuilder();
+    String[] argumentDescriptors = new String[arguments.length];
+    shortyDescriptor.append(getShortyDescriptor(returnType));
+    for (int i = 0; i < arguments.length; i++) {
+      shortyDescriptor.append(getShortyDescriptor(arguments[i]));
+      argumentDescriptors[i] = arguments[i].getDescriptor();
+    }
+    DexProto proto = options.itemFactory.createProto(
+        getString(shortyDescriptor.toString()),
+        getTypeFromDescriptor(returnType.getDescriptor()),
+        getTypeListFromDescriptors(argumentDescriptors));
+    return proto;
+  }
+
+  private static String getShortyDescriptor(Type type) {
+    switch (type.getSort()) {
+      case Type.METHOD:
+        throw new InternalCompilerError("Cannot produce a shorty decriptor for methods");
+      case Type.ARRAY:
+      case Type.OBJECT:
+        return "L";
+      default:
+        return type.getDescriptor();
+    }
+  }
+
+  private boolean isValidDescriptor(String desc) {
+    return Type.getType(desc).getDescriptor().equals(desc);
+  }
+
+  private boolean isValidInternalName(String name) {
+    return Type.getObjectType(name).getInternalName().equals(name);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
new file mode 100644
index 0000000..07a7231
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -0,0 +1,711 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
+import static org.objectweb.asm.Opcodes.ACC_DEPRECATED;
+import static org.objectweb.asm.Opcodes.ASM5;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueBoolean;
+import com.android.tools.r8.graph.DexValue.DexValueByte;
+import com.android.tools.r8.graph.DexValue.DexValueChar;
+import com.android.tools.r8.graph.DexValue.DexValueDouble;
+import com.android.tools.r8.graph.DexValue.DexValueEnum;
+import com.android.tools.r8.graph.DexValue.DexValueFloat;
+import com.android.tools.r8.graph.DexValue.DexValueInt;
+import com.android.tools.r8.graph.DexValue.DexValueLong;
+import com.android.tools.r8.graph.DexValue.DexValueShort;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.graph.JarCode.ReparseContext;
+import com.android.tools.r8.utils.InternalResource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+
+/**
+ * Java/Jar class reader for constructing dex/graph structure.
+ */
+public class JarClassFileReader {
+
+  // Hidden ASM "synthetic attribute" bit we need to clear.
+  private static int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
+
+  private final JarApplicationReader application;
+  private final Consumer<DexClass> classConsumer;
+
+  public JarClassFileReader(
+      JarApplicationReader application, Consumer<DexClass> classConsumer) {
+    this.application = application;
+    this.classConsumer = classConsumer;
+  }
+
+  public void read(String file, InternalResource.Kind kind, InputStream input) throws IOException {
+    ClassReader reader = new ClassReader(input);
+    reader.accept(new CreateDexClassVisitor(
+        file, kind, reader.b, application, classConsumer), SKIP_FRAMES);
+  }
+
+  private static DexAccessFlags createAccessFlags(int access) {
+    // Clear the "synthetic attribute" and "deprecated" flags if preset.
+    return new DexAccessFlags(access & ~ACC_SYNTHETIC_ATTRIBUTE & ~ACC_DEPRECATED);
+  }
+
+  private static AnnotationVisitor createAnnotationVisitor(String desc, boolean visible,
+      AnnotationVisitor annotationVisitor, List<DexAnnotation> annotations,
+      JarApplicationReader application) {
+    assert annotations != null;
+    int visiblity = visible ? DexAnnotation.VISIBILITY_RUNTIME : DexAnnotation.VISIBILITY_BUILD;
+    return new CreateAnnotationVisitor(annotationVisitor, application, (names, values) ->
+        annotations.add(new DexAnnotation(visiblity,
+            createEncodedAnnotation(desc, names, values, application))));
+  }
+
+  private static DexEncodedAnnotation createEncodedAnnotation(String desc,
+      List<DexString> names, List<DexValue> values, JarApplicationReader application) {
+    assert (names == null && values.isEmpty())
+        || (names != null && !names.isEmpty() && names.size() == values.size());
+    DexAnnotationElement[] elements = new DexAnnotationElement[values.size()];
+    for (int i = 0; i < values.size(); i++) {
+      elements[i] = new DexAnnotationElement(names.get(i), values.get(i));
+    }
+    return new DexEncodedAnnotation(application.getTypeFromDescriptor(desc), elements);
+  }
+
+  private static class CreateDexClassVisitor extends ClassVisitor {
+    private final String file;
+    private final InternalResource.Kind kind;
+    private final JarApplicationReader application;
+    private final Consumer<DexClass> classConsumer;
+    private final ReparseContext context = new ReparseContext();
+
+    // DexClass data.
+    private DexType type;
+    private DexAccessFlags accessFlags;
+    private DexType superType;
+    private DexTypeList interfaces;
+    private DexString sourceFile;
+    private List<DexType> memberClasses = null;
+    private List<DexAnnotation> annotations = null;
+    private List<DexAnnotationElement> defaultAnnotations = null;
+    private DexAnnotation innerClassAnnotation = null;
+    private DexAnnotation enclosingAnnotation = null;
+    private final List<DexEncodedField> staticFields = new ArrayList<>();
+    private final List<DexEncodedField> instanceFields = new ArrayList<>();
+    private final List<DexEncodedMethod> directMethods = new ArrayList<>();
+    private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
+
+    public CreateDexClassVisitor(
+        String file,
+        InternalResource.Kind kind,
+        byte[] classCache,
+        JarApplicationReader application,
+        Consumer<DexClass> classConsumer) {
+      super(ASM5);
+      this.file = file;
+      this.kind = kind;
+      this.classConsumer = classConsumer;
+      this.context.classCache = classCache;
+      this.application = application;
+    }
+
+    @Override
+    public void visitInnerClass(String name, String outerName, String innerName, int access) {
+      if (type == application.getTypeFromName(name)) {
+        // If the inner class is this class, record its original access flags and name in its
+        // InnerClass annotation. We defer storing the actual annotation until we have found
+        // a matching enclosing annotation.
+        assert innerClassAnnotation == null;
+        innerClassAnnotation = DexAnnotation.createInnerClassAnnotation(
+            innerName, access, application.getFactory());
+        // If this is a named inner class (in which case outerName and innerName are defined)
+        // record the outer class in its EnclosingClass annotation.
+        if (outerName != null && innerName != null) {
+          assert enclosingAnnotation == null;
+          enclosingAnnotation = DexAnnotation.createEnclosingClassAnnotation(
+              application.getTypeFromName(outerName),
+              application.getFactory());
+        }
+      } else if (outerName != null && innerName != null
+          && type == application.getTypeFromName(outerName)) {
+        // If the inner class is a member of this class, record it for the MemberClasses annotation.
+        if (memberClasses == null) {
+          memberClasses = new ArrayList<>();
+        }
+        memberClasses.add(application.getTypeFromName(name));
+      }
+    }
+
+    @Override
+    public void visitOuterClass(String owner, String name, String desc) {
+      assert enclosingAnnotation == null;
+      // This is called for anonymous inner classes defined in classes or in methods.
+      DexType ownerType = application.getTypeFromName(owner);
+      if (name == null) {
+        enclosingAnnotation = DexAnnotation.createEnclosingClassAnnotation(
+            ownerType, application.getFactory());
+      } else {
+        enclosingAnnotation = DexAnnotation.createEnclosingMethodAnnotation(
+            application.getMethod(ownerType, name, desc), application.getFactory());
+      }
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature, String superName,
+        String[] interfaces) {
+      accessFlags = createAccessFlags(access);
+      // Unset the (in dex) non-existent ACC_SUPER flag on the class.
+      assert Constants.ACC_SYNCHRONIZED == Opcodes.ACC_SUPER;
+      accessFlags.unsetSynchronized();
+      type = application.getTypeFromName(name);
+      assert superName != null || name.equals(Constants.JAVA_LANG_OBJECT_NAME);
+      superType = superName == null ? null : application.getTypeFromName(superName);
+      this.interfaces = application.getTypeListFromNames(interfaces);
+      if (signature != null && !signature.isEmpty()) {
+        addAnnotation(DexAnnotation.createSignatureAnnotation(signature, application.getFactory()));
+      }
+    }
+
+    @Override
+    public void visitSource(String source, String debug) {
+      if (source != null) {
+        sourceFile = application.getString(source);
+      }
+      if (debug != null) {
+        getAnnotations().add(
+                DexAnnotation.createSourceDebugExtensionAnnotation(
+                    new DexValueString(application.getString(debug)), application.getFactory()));
+      }
+    }
+
+    @Override
+    public FieldVisitor visitField(
+        int access, String name, String desc, String signature, Object value) {
+      return new CreateFieldVisitor(this, access, name, desc, signature, value);
+    }
+
+    @Override
+    public MethodVisitor visitMethod(
+        int access, String name, String desc, String signature, String[] exceptions) {
+      return new CreateMethodVisitor(access, name, desc, signature, exceptions, this);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      return createAnnotationVisitor(desc, visible, null, getAnnotations(), application);
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
+        boolean visible) {
+      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      return null;
+    }
+
+    @Override
+    public void visitAttribute(Attribute attr) {
+      // Unknown attribute must only be ignored
+    }
+
+    @Override
+    public void visitEnd() {
+      if (memberClasses != null) {
+        assert !memberClasses.isEmpty();
+        addAnnotation(DexAnnotation.createMemberClassesAnnotation(
+            memberClasses, application.getFactory()));
+      }
+      if (innerClassAnnotation != null) {
+        if (enclosingAnnotation == null) {
+          System.out.println("InnerClass annotation is missing a corresponding EnclosingMember " +
+              "annotation. This is typically a sign of using an outdated Java toolchain. To fix, " +
+              "recompile the source with an updated toolchain. The InnerClass annotation will be " +
+              "ignored.");
+        } else {
+          addAnnotation(innerClassAnnotation);
+          addAnnotation(enclosingAnnotation);
+        }
+      }
+      if (defaultAnnotations != null) {
+        addAnnotation(DexAnnotation.createAnnotationDefaultAnnotation(
+            type, defaultAnnotations, application.getFactory()));
+      }
+      DexClass clazz = DexClass.factoryForResourceKind(kind).create(
+          type,
+          DexClass.Origin.ClassFile,
+          accessFlags,
+          superType,
+          interfaces,
+          sourceFile,
+          createAnnotationSet(annotations),
+          staticFields.toArray(new DexEncodedField[staticFields.size()]),
+          instanceFields.toArray(new DexEncodedField[instanceFields.size()]),
+          directMethods.toArray(new DexEncodedMethod[directMethods.size()]),
+          virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]));
+      if (kind == InternalResource.Kind.PROGRAM) {
+        context.owner = clazz.asProgramClass();
+      }
+      classConsumer.accept(clazz);
+    }
+
+    private void addDefaultAnnotation(String name, DexValue value) {
+      if (defaultAnnotations == null) {
+        defaultAnnotations = new ArrayList<>();
+      }
+      defaultAnnotations.add(new DexAnnotationElement(application.getString(name), value));
+    }
+
+    private void addAnnotation(DexAnnotation annotation) {
+      getAnnotations().add(annotation);
+    }
+
+    private List<DexAnnotation> getAnnotations() {
+      if (annotations == null) {
+        annotations = new ArrayList<>();
+      }
+      return annotations;
+    }
+  }
+
+  private static DexAnnotationSet createAnnotationSet(List<DexAnnotation> annotations) {
+    return annotations == null || annotations.isEmpty()
+        ? DexAnnotationSet.empty()
+        : new DexAnnotationSet(annotations.toArray(new DexAnnotation[annotations.size()]));
+  }
+
+  private static class CreateFieldVisitor extends FieldVisitor {
+    private final CreateDexClassVisitor parent;
+    private final int access;
+    private final String name;
+    private final String desc;
+    private final Object value;
+    private List<DexAnnotation> annotations = null;
+
+    public CreateFieldVisitor(CreateDexClassVisitor parent,
+        int access, String name, String desc, String signature, Object value) {
+      super(ASM5);
+      this.parent = parent;
+      this.access = access;
+      this.name = name;
+      this.desc = desc;
+      this.value = value;
+      if (signature != null && !signature.isEmpty()) {
+        addAnnotation(DexAnnotation.createSignatureAnnotation(
+            signature, parent.application.getFactory()));
+      }
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      return createAnnotationVisitor(desc, visible, null, getAnnotations(), parent.application);
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
+        boolean visible) {
+      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      return null;
+    }
+
+    @Override
+    public void visitEnd() {
+      DexAccessFlags flags = createAccessFlags(access);
+      DexField dexField = parent.application.getField(parent.type, name, desc);
+      DexAnnotationSet annotationSet = createAnnotationSet(annotations);
+      DexValue staticValue = flags.isStatic() ? getStaticValue(value, dexField.type) : null;
+      DexEncodedField field = new DexEncodedField(dexField, flags, annotationSet, staticValue);
+      if (flags.isStatic()) {
+        parent.staticFields.add(field);
+      } else {
+        parent.instanceFields.add(field);
+      }
+    }
+
+    private DexValue getStaticValue(Object value, DexType type) {
+      if (value == null) {
+        return DexValue.defaultForType(type, parent.application.getFactory());
+      }
+      DexItemFactory factory = parent.application.getFactory();
+      if (type == factory.booleanType) {
+        int i = (Integer) value;
+        assert 0 <= i && i <= 1;
+        return DexValueBoolean.create(i == 1);
+      }
+      if (type == factory.byteType) {
+        return DexValueByte.create(((Integer) value).byteValue());
+      }
+      if (type == factory.shortType) {
+        return DexValueShort.create(((Integer) value).shortValue());
+      }
+      if (type == factory.charType) {
+        return DexValueChar.create((char) ((Integer) value).intValue());
+      }
+      if (type == factory.intType) {
+        return DexValueInt.create((Integer) value);
+      }
+      if (type == factory.floatType) {
+        return DexValueFloat.create((Float) value);
+      }
+      if (type == factory.longType) {
+        return DexValueLong.create((Long) value);
+      }
+      if (type == factory.doubleType) {
+        return DexValueDouble.create((Double) value);
+      }
+      if (type == factory.stringType) {
+        return new DexValueString(factory.createString((String) value));
+      }
+      throw new Unreachable("Unexpected static-value type " + type);
+    }
+
+    private void addAnnotation(DexAnnotation annotation) {
+      getAnnotations().add(annotation);
+    }
+
+    private List<DexAnnotation> getAnnotations() {
+      if (annotations == null) {
+        annotations = new ArrayList<>();
+      }
+      return annotations;
+    }
+  }
+
+  private static class CreateMethodVisitor extends MethodVisitor {
+    private final int access;
+    private final String name;
+    private final String desc;
+    private final CreateDexClassVisitor parent;
+    private final int parameterCount;
+    private List<DexAnnotation> annotations = null;
+    private DexValue defaultAnnotation = null;
+    private List<List<DexAnnotation>> parameterAnnotations = null;
+    private List<DexValue> parameterNames = null;
+    private List<DexValue> parameterFlags = null;
+
+    public CreateMethodVisitor(int access, String name, String desc, String signature,
+        String[] exceptions, CreateDexClassVisitor parent) {
+      super(ASM5);
+      this.access = access;
+      this.name = name;
+      this.desc = desc;
+      this.parent = parent;
+      parameterCount = Type.getArgumentTypes(desc).length;
+      if (exceptions != null && exceptions.length > 0) {
+        DexValue[] values = new DexValue[exceptions.length];
+        for (int i = 0; i < exceptions.length; i++) {
+          values[i] = new DexValueType(parent.application.getTypeFromName(exceptions[i]));
+        }
+        addAnnotation(DexAnnotation.createThrowsAnnotation(
+            values, parent.application.getFactory()));
+      }
+      if (signature != null && !signature.isEmpty()) {
+        addAnnotation(DexAnnotation.createSignatureAnnotation(
+            signature, parent.application.getFactory()));
+      }
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      return createAnnotationVisitor(desc, visible,
+          mv == null ? null : mv.visitAnnotation(desc, visible),
+          getAnnotations(), parent.application);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+      return new CreateAnnotationVisitor(mv == null ? null : mv.visitAnnotationDefault(),
+          parent.application, (names, elements) -> {
+        assert elements.size() == 1;
+        defaultAnnotation = elements.get(0);
+      });
+    }
+
+    @Override
+    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc,
+        boolean visible) {
+      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      return null;
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+      if (parameterAnnotations == null) {
+        parameterAnnotations = new ArrayList<>(parameterCount);
+        for (int i = 0; i < parameterCount; i++) {
+          parameterAnnotations.add(new ArrayList<>());
+        }
+      }
+      return createAnnotationVisitor(desc, visible,
+          mv == null ? null : mv.visitParameterAnnotation(parameter, desc, visible),
+          parameterAnnotations.get(parameter), parent.application);
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc,
+        boolean visible) {
+      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      return null;
+    }
+
+    @Override
+    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath,
+        Label[] start, Label[] end, int[] index, String desc, boolean visible) {
+      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      return null;
+    }
+
+    @Override
+    public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc,
+        boolean visible) {
+      // Java 8 type annotations are not supported by Dex, thus ignore them.
+      return null;
+    }
+
+    @Override
+    public void visitParameter(String name, int access) {
+      if (parameterNames == null) {
+        assert parameterFlags == null;
+        parameterNames = new ArrayList<>(parameterCount);
+        parameterFlags = new ArrayList<>(parameterCount);
+      }
+      parameterNames.add(new DexValueString(parent.application.getFactory().createString(name)));
+      parameterFlags.add(DexValueInt.create(access));
+      super.visitParameter(name, access);
+    }
+
+    @Override
+    public void visitEnd() {
+      DexMethod method = parent.application.getMethod(parent.type, name, desc);
+      DexAccessFlags flags = createMethodAccessFlags(access);
+      Code code = null;
+      if (!flags.isAbstract()
+          && !flags.isNative()
+          && parent.kind == InternalResource.Kind.PROGRAM) {
+        code = new JarCode(method, parent.context, parent.application);
+      }
+      DexAnnotationSetRefList parameterAnnotationSets;
+      if (parameterAnnotations == null) {
+        parameterAnnotationSets = DexAnnotationSetRefList.empty();
+      } else {
+        DexAnnotationSet[] sets = new DexAnnotationSet[parameterAnnotations.size()];
+        for (int i = 0; i < parameterAnnotations.size(); i++) {
+          sets[i] = createAnnotationSet(parameterAnnotations.get(i));
+        }
+        parameterAnnotationSets = new DexAnnotationSetRefList(sets);
+      }
+      if (parameterNames != null) {
+        assert parameterFlags != null;
+        if (parameterNames.size() != parameterCount) {
+          parent.application.options.warningInvalidParameterAnnotations =
+              "Invalid parameter count in MethodParameters attributes of "
+                  + method.toSourceString() + " from '" + parent.file + "'. Found "
+                  + parameterNames.size() + " while expecting " + parameterCount + "."
+                  + " This is likely due to proguard having removed a parameter.";
+        }
+        getAnnotations().add(DexAnnotation.createMethodParametersAnnotation(
+            parameterNames.toArray(new DexValue[parameterNames.size()]),
+            parameterFlags.toArray(new DexValue[parameterFlags.size()]),
+            parent.application.getFactory()));
+      }
+      DexEncodedMethod dexMethod = new DexEncodedMethod(method, flags,
+          createAnnotationSet(annotations), parameterAnnotationSets, code);
+      if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
+        parent.directMethods.add(dexMethod);
+      } else {
+        parent.virtualMethods.add(dexMethod);
+      }
+      if (defaultAnnotation != null) {
+        parent.addDefaultAnnotation(name, defaultAnnotation);
+      }
+    }
+
+    private List<DexAnnotation> getAnnotations() {
+      if (annotations == null) {
+        annotations = new ArrayList<>();
+      }
+      return annotations;
+    }
+
+    private void addAnnotation(DexAnnotation annotation) {
+      getAnnotations().add(annotation);
+    }
+
+    private DexAccessFlags createMethodAccessFlags(int access) {
+      DexAccessFlags flags = createAccessFlags(access);
+      // Set just the dex specific declared-synchronized flag if the method is synchronized.
+      // TODO(zerny): Should declared sync also be set if it is native?
+      if (flags.isSynchronized() && !flags.isNative()) {
+        flags.unsetSynchronized();
+        flags.setDeclaredSynchronized();
+      }
+      // Set the constructor bit on instance and class initializers.
+      if (name.equals(Constants.INSTANCE_INITIALIZER_NAME) || name.equals(
+          Constants.CLASS_INITIALIZER_NAME)) {
+        flags.setConstructor();
+      }
+      return flags;
+    }
+  }
+
+  private static class CreateAnnotationVisitor extends AnnotationVisitor {
+    private final JarApplicationReader application;
+    private final BiConsumer<List<DexString>, List<DexValue>> onVisitEnd;
+    private List<DexString> names = null;
+    private final List<DexValue> values = new ArrayList<>();
+
+    public CreateAnnotationVisitor(AnnotationVisitor annotationVisitor,
+        JarApplicationReader application,
+        BiConsumer<List<DexString>, List<DexValue>> onVisitEnd) {
+      super(ASM5, annotationVisitor);
+      this.application = application;
+      this.onVisitEnd = onVisitEnd;
+    }
+
+    @Override
+    public void visit(String name, Object value) {
+      addElement(name, getDexValue(value));
+    }
+
+    @Override
+    public void visitEnum(String name, String desc, String value) {
+      DexType owner = application.getTypeFromDescriptor(desc);
+      addElement(name, new DexValueEnum(application.getField(owner, value, desc)));
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+      return new CreateAnnotationVisitor(av, application, (names, values) ->
+          addElement(name, new DexValueAnnotation(
+              createEncodedAnnotation(desc, names, values, application))));
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(String name) {
+      return new CreateAnnotationVisitor(av, application, (names, values) -> {
+        assert names == null;
+        addElement(name, new DexValueArray(values.toArray(new DexValue[values.size()])));
+      });
+    }
+
+    @Override
+    public void visitEnd() {
+      onVisitEnd.accept(names, values);
+    }
+
+    private void addElement(String name, DexValue value) {
+      if (name != null) {
+        if (names == null){
+          names = new ArrayList<>();
+        }
+        names.add(application.getString(name));
+      }
+      values.add(value);
+    }
+
+    private static DexValueArray getDexValueArray(Object value) {
+      if (value instanceof byte[]) {
+        byte[] values = (byte[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueByte.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof boolean[]) {
+        boolean[] values = (boolean[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueBoolean.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof char[]) {
+        char[] values = (char[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueChar.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof short[]) {
+        short[] values = (short[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueShort.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof int[]) {
+        int[] values = (int[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueInt.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof long[]) {
+        long[] values = (long[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueLong.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof float[]) {
+        float[] values = (float[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueFloat.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      } else if (value instanceof double[]) {
+        double[] values = (double[]) value;
+        DexValue[] elements = new DexValue[values.length];
+        for (int i = 0; i < values.length; i++) {
+          elements[i] = DexValueDouble.create(values[i]);
+        }
+        return new DexValueArray(elements);
+      }
+      throw new Unreachable("Unexpected type of annotation value: " + value);
+    }
+
+    private DexValue getDexValue(Object value) {
+      if (value == null) {
+        return DexValue.NULL;
+      }
+      if (value instanceof Byte) {
+        return DexValueByte.create((Byte) value);
+      } else if (value instanceof Boolean) {
+        return DexValueBoolean.create((Boolean) value);
+      } else if (value instanceof Character) {
+        return DexValueChar.create((Character) value);
+      } else if (value instanceof Short) {
+        return DexValueShort.create((Short) value);
+      } else if (value instanceof Integer) {
+        return DexValueInt.create((Integer) value);
+      } else if (value instanceof Long) {
+        return DexValueLong.create((Long) value);
+      } else if (value instanceof Float) {
+        return DexValueFloat.create((Float) value);
+      } else if (value instanceof Double) {
+        return DexValueDouble.create((Double) value);
+      } else if (value instanceof String) {
+        return new DexValueString(application.getString((String) value));
+      } else if (value instanceof Type) {
+        return new DexValueType(application.getTypeFromDescriptor(((Type) value).getDescriptor()));
+      }
+      return getDexValueArray(value);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
new file mode 100644
index 0000000..b40f6ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.JarSourceCode;
+import com.android.tools.r8.jar.JarRegisterEffectsVisitor;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.IdentityHashMap;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.JSRInlinerAdapter;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceMethodVisitor;
+
+public class JarCode extends Code {
+
+  public static class ReparseContext {
+
+    // This will hold the content of the whole class. Once all the methods of the class are swapped
+    // from this to the actual JarCode, no other references would be left and the content can be
+    // GC'd.
+    public byte[] classCache;
+    public DexProgramClass owner;
+    private IdentityHashMap<DexMethod, JarCode> lookupMap = new IdentityHashMap<>();
+  }
+
+  private final DexType clazz;
+  private MethodNode node;
+  private ReparseContext context;
+
+  private final JarApplicationReader application;
+
+  public JarCode(DexMethod method, ReparseContext context, JarApplicationReader application) {
+    this.clazz = method.getHolder();
+    this.context = context;
+    this.application = application;
+    context.lookupMap.put(method, this);
+  }
+
+  @Override
+  public boolean isJarCode() {
+    return true;
+  }
+
+  @Override
+  public JarCode asJarCode() {
+    return this;
+  }
+
+  @Override
+  protected int computeHashCode() {
+    triggerDelayedParsingIfNeccessary();
+    return node.hashCode();
+  }
+
+  @Override
+  protected boolean computeEquals(Object other) {
+    triggerDelayedParsingIfNeccessary();
+    if (this == other) {
+      return true;
+    }
+    if (other instanceof JarCode) {
+      JarCode o = (JarCode) other;
+      o.triggerDelayedParsingIfNeccessary();
+      // TODO(zerny): This amounts to object equality.
+      return node.equals(o.node);
+    }
+    return false;
+  }
+
+  @Override
+  public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+    assert clazz == encodedMethod.method.getHolder();
+    triggerDelayedParsingIfNeccessary();
+    JarSourceCode source = new JarSourceCode(clazz, node, application);
+    IRBuilder builder = new IRBuilder(encodedMethod, source, options);
+    return builder.build();
+  }
+
+  @Override
+  public void registerReachableDefinitions(UseRegistry registry) {
+    triggerDelayedParsingIfNeccessary();
+    node.instructions.accept(new JarRegisterEffectsVisitor(clazz, registry, application));
+  }
+
+  @Override
+  public String toString() {
+    triggerDelayedParsingIfNeccessary();
+    TraceMethodVisitor visitor = new TraceMethodVisitor(new Textifier());
+    node.accept(visitor);
+    StringWriter writer = new StringWriter();
+    visitor.p.print(new PrintWriter(writer));
+    return writer.toString();
+  }
+
+  @Override
+  public String toString(ClassNameMapper naming) {
+    return toString();
+  }
+
+  private void triggerDelayedParsingIfNeccessary() {
+    if (context != null) {
+      // The SecondVistor is in charge of setting the context to null.
+      DexProgramClass owner = context.owner;
+      new ClassReader(context.classCache).accept(new SecondVisitor(context, application),
+          ClassReader.SKIP_FRAMES);
+      assert verifyNoReparseContext(owner);
+    }
+  }
+
+  /**
+   * Fills the MethodNodes of all the methods in the class and removes the ReparseContext.
+   */
+  private static class SecondVisitor extends ClassVisitor {
+
+    private final ReparseContext context;
+    private final JarApplicationReader application;
+
+    public SecondVisitor(ReparseContext context, JarApplicationReader application) {
+      super(Opcodes.ASM5);
+      this.context = context;
+      this.application = application;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+        String[] exceptions) {
+      MethodNode node = new JSRInlinerAdapter(null, access, name, desc, signature, exceptions);
+      JarCode code = context.lookupMap.get(application.getMethod(context.owner.type, name, desc));
+      if (code != null) {
+        code.context = null;
+        code.node = node;
+        return node;
+      }
+      return null;
+    }
+  }
+
+  private static boolean verifyNoReparseContext(DexProgramClass owner) {
+    for (DexEncodedMethod method : owner.virtualMethods()) {
+      Code code = method.getCode();
+      if (code != null && code.isJarCode()) {
+        if (code.asJarCode().context != null) {
+          return false;
+        }
+      }
+    }
+
+    for (DexEncodedMethod method : owner.directMethods()) {
+      Code code = method.getCode();
+      if (code != null && code.isJarCode()) {
+        if (code.asJarCode().context != null) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/KeyedDexItem.java b/src/main/java/com/android/tools/r8/graph/KeyedDexItem.java
new file mode 100644
index 0000000..ad852eb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/KeyedDexItem.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class KeyedDexItem<T extends PresortedComparable<T>> extends DexItem {
+
+  public abstract T getKey();
+
+  @Override
+  public final boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+    return (other.getClass() == getClass()) && ((KeyedDexItem) other).getKey().equals(getKey());
+  }
+
+  @Override
+  public final int hashCode() {
+    return getKey().hashCode();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
new file mode 100644
index 0000000..01bf132
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/LazyClassFileLoader.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import static com.android.tools.r8.utils.FileUtils.DEFAULT_DEX_FILENAME;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.io.Closer;
+import java.io.IOException;
+
+// Lazily loads a class file represented by resource.
+public final class LazyClassFileLoader implements DexClassPromise {
+  // Resource representing file definition.
+  private final InternalResource resource;
+
+  // Application reader to be used. Note that the reader may be reused in
+  // many loaders and may be used concurrently, it is considered to be
+  // thread-safe since its only state is internal options which we
+  // consider immutable after they are initialized (barring dex factory
+  // which is thread-safe).
+  private final JarApplicationReader reader;
+
+  // Dex type of the class to be created.
+  private final DexType type;
+
+  // Cached loaded class if get(...) method has already been called, this
+  // field is only accessed in context synchronized on `this`.
+  private DexClass loadedClass = null;
+
+  public LazyClassFileLoader(DexType type, InternalResource resource, JarApplicationReader reader) {
+    this.resource = resource;
+    this.reader = reader;
+    this.type = type;
+  }
+
+  // Callback method for JarClassFileReader, is always called in synchronized context.
+  private void addClass(DexClass clazz) {
+    assert clazz != null;
+    assert loadedClass == null;
+    loadedClass = clazz;
+  }
+
+  @Override
+  public DexType getType() {
+    return type;
+  }
+
+  @Override
+  public DexClass.Origin getOrigin() {
+    return DexClass.Origin.ClassFile;
+  }
+
+  @Override
+  public boolean isProgramClass() {
+    return resource.kind == InternalResource.Kind.PROGRAM;
+  }
+
+  @Override
+  public boolean isClasspathClass() {
+    return resource.kind == InternalResource.Kind.CLASSPATH;
+  }
+
+  @Override
+  public boolean isLibraryClass() {
+    return resource.kind == InternalResource.Kind.LIBRARY;
+  }
+
+  // Loads the class from the resource. Synchronized on `this` to avoid
+  // unnecessary complications, thus all threads trying to load a class with
+  // this loader will wait for the first load to finish.
+  @Override
+  public synchronized DexClass get() {
+    if (loadedClass != null) {
+      return loadedClass;
+    }
+
+    try (Closer closer = Closer.create()) {
+      JarClassFileReader reader = new JarClassFileReader(this.reader, this::addClass);
+      reader.read(DEFAULT_DEX_FILENAME, resource.kind, resource.getStream(closer));
+    } catch (IOException e) {
+      throw new CompilationError("Failed to load class: " + resource.getClassDescriptor(), e);
+    }
+
+    if (loadedClass == null) {
+      throw new Unreachable("Class is supposed to be loaded: " + resource.getClassDescriptor());
+    }
+
+    if (loadedClass.type != type) {
+      throw new CompilationError("Class content provided for type descriptor "
+          + type.toSourceString() + " actually defines class " + loadedClass.type.toSourceString());
+    }
+
+    return loadedClass;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
new file mode 100644
index 0000000..1d5f38c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -0,0 +1,208 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.dex.Constants;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+public class ObjectToOffsetMapping {
+
+  private final int virtualFileId;
+
+  private final DexProgramClass[] classes;
+  private final DexProto[] protos;
+  private final DexType[] types;
+  private final DexMethod[] methods;
+  private final DexField[] fields;
+  private final DexString[] strings;
+  private final DexCallSite[] callSites;
+  private final DexMethodHandle[] methodHandles;
+  private DexString firstJumboString;
+
+  public ObjectToOffsetMapping(
+      int virtualFileId,
+      DexApplication application,
+      DexProgramClass[] classes,
+      DexProto[] protos,
+      DexType[] types,
+      DexMethod[] methods,
+      DexField[] fields,
+      DexString[] strings,
+      DexCallSite[] callSites,
+      DexMethodHandle[] methodHandles) {
+    assert application != null;
+    assert classes != null;
+    assert protos != null;
+    assert types != null;
+    assert methods != null;
+    assert fields != null;
+    assert strings != null;
+    assert callSites != null;
+    assert methodHandles != null;
+
+    this.virtualFileId = virtualFileId;
+    this.classes = sortClasses(application, classes);
+    this.protos = protos;
+    this.types = types;
+    this.methods = methods;
+    this.fields = fields;
+    this.strings = strings;
+    this.callSites = callSites;
+    this.methodHandles = methodHandles;
+
+    Arrays.sort(protos);
+    setIndexes(protos);
+
+    Arrays.sort(types);
+    setIndexes(types);
+
+    Arrays.sort(methods);
+    setIndexes(methods);
+
+    Arrays.sort(fields);
+    setIndexes(fields);
+
+    Arrays.sort(strings);
+    setIndexes(strings);
+
+    // No need to sort CallSite, they will be written in data section in the callSites order,
+    // consequently offset of call site used into the call site section will be in ascending order.
+    setIndexes(callSites);
+
+    // No need to sort method handle
+    setIndexes(methodHandles);
+  }
+
+  private static DexProgramClass[] sortClasses(
+      DexApplication application, DexProgramClass[] classes) {
+    SortingProgramClassVisitor classVisitor = new SortingProgramClassVisitor(application, classes);
+    classVisitor.run(classes);
+    return classVisitor.getSortedClasses();
+  }
+
+  private void setIndexes(IndexedDexItem[] items) {
+    int index = 0;
+    for (IndexedDexItem item : items) {
+      item.assignVirtualFileIndex(virtualFileId, index++);
+      // For strings collect the first jumbo string (if any).
+      if (index > Constants.MAX_NON_JUMBO_INDEX) {
+        assert item instanceof DexString;
+        if (index == Constants.FIRST_JUMBO_INDEX) {
+          firstJumboString = (DexString) item;
+        }
+      }
+    }
+  }
+
+  public DexMethod[] getMethods() {
+    return methods;
+  }
+
+  public DexProgramClass[] getClasses() {
+    return classes;
+  }
+
+  public DexType[] getTypes() {
+    return types;
+  }
+
+  public DexProto[] getProtos() {
+    return protos;
+  }
+
+  public DexField[] getFields() {
+    return fields;
+  }
+
+  public DexString[] getStrings() {
+    return strings;
+  }
+
+  public DexCallSite[] getCallSites() {
+    return callSites;
+  }
+
+  public DexMethodHandle[] getMethodHandles() {
+    return methodHandles;
+  }
+
+  public boolean hasJumboStrings() {
+    return firstJumboString != null;
+  }
+
+  public DexString getFirstJumboString() {
+    return firstJumboString;
+  }
+
+  private boolean isContainedInMapping(IndexedDexItem item) {
+    return item.getVirtualFileIndex(virtualFileId) != IndexedDexItem.UNASSOCIATED_VALUE;
+  }
+
+  public int getOffsetFor(DexProto proto) {
+    assert isContainedInMapping(proto) : "Missing dependency: " + proto;
+    return proto.getVirtualFileIndex(virtualFileId);
+  }
+
+  public int getOffsetFor(DexField field) {
+    assert isContainedInMapping(field) : "Missing dependency: " + field;
+    return field.getVirtualFileIndex(virtualFileId);
+  }
+
+  public int getOffsetFor(DexMethod method) {
+    assert isContainedInMapping(method) : "Missing dependency: " + method;
+    return method.getVirtualFileIndex(virtualFileId);
+  }
+
+  public int getOffsetFor(DexString string) {
+    assert isContainedInMapping(string) : "Missing dependency: " + string;
+    return string.getVirtualFileIndex(virtualFileId);
+  }
+
+  public int getOffsetFor(DexType type) {
+    assert isContainedInMapping(type) : "Missing dependency: " + type;
+    return type.getVirtualFileIndex(virtualFileId);
+  }
+
+  public int getOffsetFor(DexCallSite callSite) {
+    assert isContainedInMapping(callSite) : "Missing dependency: " + callSite;
+    return callSite.getVirtualFileIndex(virtualFileId);
+  }
+
+  public int getOffsetFor(DexMethodHandle methodHandle) {
+    assert isContainedInMapping(methodHandle) : "Missing dependency: " + methodHandle;
+    return methodHandle.getVirtualFileIndex(virtualFileId);
+  }
+
+  private static class SortingProgramClassVisitor extends ProgramClassVisitor {
+    private final Set<DexClass> classSet = Sets.newIdentityHashSet();
+    private final DexProgramClass[] sortedClasses;
+
+    private int index = 0;
+
+    public SortingProgramClassVisitor(DexApplication application, DexProgramClass[] classes) {
+      super(application);
+      this.sortedClasses = new DexProgramClass[classes.length];
+      Collections.addAll(classSet, classes);
+    }
+
+    @Override
+    public void visit(DexType type) {}
+
+    @Override
+    public void visit(DexClass clazz) {
+      if (classSet.contains(clazz)) {
+        assert index < sortedClasses.length;
+        sortedClasses[index++] = (DexProgramClass) clazz;
+      }
+    }
+
+    public DexProgramClass[] getSortedClasses() {
+      assert index == sortedClasses.length;
+      return sortedClasses;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/OffsetToObjectMapping.java b/src/main/java/com/android/tools/r8/graph/OffsetToObjectMapping.java
new file mode 100644
index 0000000..5e42113
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/OffsetToObjectMapping.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public class OffsetToObjectMapping {
+
+  private DexProgramClass[] classes;
+  private DexMethod[] methods;
+  private DexProto[] protos;
+  private DexField[] fields;
+  private DexType[] types;
+  private DexString[] strings;
+  private DexCallSite[] callSites;
+  private DexMethodHandle[] methodHandles;
+
+  public void initializeClasses(int length) {
+    assert classes == null;
+    classes = new DexProgramClass[length];
+  }
+
+  public void initializeMethods(int length) {
+    assert methods == null;
+    methods = new DexMethod[length];
+  }
+
+  public void initializeProtos(int length) {
+    assert protos == null;
+    protos = new DexProto[length];
+  }
+
+  public void initializeFields(int length) {
+    assert fields == null;
+    fields = new DexField[length];
+  }
+
+  public void initializeTypes(int length) {
+    assert types == null;
+    types = new DexType[length];
+  }
+
+  public void initializeStrings(int length) {
+    assert strings == null;
+    strings = new DexString[length];
+  }
+
+  public void initializeCallSites(int length) {
+    assert callSites == null;
+    callSites = new DexCallSite[length];
+  }
+
+  public void initializeMethodHandles(int length) {
+    assert methodHandles == null;
+    methodHandles = new DexMethodHandle[length];
+  }
+
+  public DexProgramClass[] getClassMap() {
+    assert classes != null;
+    return classes;
+  }
+
+  public DexMethod[] getMethodMap() {
+    assert methods != null;
+    return methods;
+  }
+
+  public DexProto[] getProtosMap() {
+    assert protos != null;
+    return protos;
+  }
+
+  public DexField[] getFieldMap() {
+    assert fields != null;
+    return fields;
+  }
+
+  public DexType[] getTypeMap() {
+    assert types != null;
+    return types;
+  }
+
+  public DexString[] getStringMap() {
+    assert strings != null;
+    return strings;
+  }
+
+  public DexCallSite[] getCallSiteMap() {
+    assert callSites != null;
+    return callSites;
+  }
+
+  public DexMethodHandle[] getMethodHandleMap() {
+    assert methodHandles != null;
+    return methodHandles;
+  }
+
+  public DexProgramClass getClass(int index) {
+    assert classes[index] != null;
+    return classes[index];
+  }
+
+  public DexMethod getMethod(int index) {
+    assert methods[index] != null;
+    return methods[index];
+  }
+
+  public DexProto getProto(int index) {
+    assert protos[index] != null;
+    return protos[index];
+  }
+
+  public DexField getField(int index) {
+    assert fields[index] != null;
+    return fields[index];
+  }
+
+  public DexType getType(int index) {
+    assert types[index] != null;
+    return types[index];
+  }
+
+  public DexString getString(int index) {
+    assert strings[index] != null;
+    return strings[index];
+  }
+
+  public DexCallSite getCallSite(int index) {
+    assert callSites[index] != null;
+    return callSites[index];
+  }
+
+  public DexMethodHandle getMethodHandle(int index) {
+    assert methodHandles[index] != null;
+    return methodHandles[index];
+  }
+
+  public void setClass(int index, DexProgramClass clazz) {
+    assert classes[index] == null;
+    classes[index] = clazz;
+  }
+
+  public void setProto(int index, DexProto proto) {
+    assert protos[index] == null;
+    protos[index] = proto;
+  }
+
+  public void setMethod(int index, DexMethod method) {
+    assert methods[index] == null;
+    methods[index] = method;
+  }
+
+  public void setField(int index, DexField field) {
+    assert fields[index] == null;
+    fields[index] = field;
+  }
+
+  public void setType(int index, DexType type) {
+    assert types[index] == null;
+    types[index] = type;
+  }
+
+  public void setString(int index, DexString string) {
+    assert strings[index] == null;
+    strings[index] = string;
+  }
+
+  public void setCallSites(int index, DexCallSite callSite) {
+    assert callSites[index] == null;
+    callSites[index] = callSite;
+  }
+
+  public void setMethodHandle(int index, DexMethodHandle methodHandle) {
+    assert methodHandles[index] == null;
+    methodHandles[index] = methodHandle;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/Presorted.java b/src/main/java/com/android/tools/r8/graph/Presorted.java
new file mode 100644
index 0000000..475da13
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/Presorted.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+/**
+ * Interface for capturing presorted behavior for Dex items.
+ */
+public interface Presorted {
+
+  void setSortedIndex(int sortedIndex);
+
+  int getSortedIndex();
+
+  int sortedCompareTo(int other);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
new file mode 100644
index 0000000..3caf8f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.naming.NamingLens;
+
+public interface PresortedComparable<T> extends Presorted, Comparable<T> {
+  // Slow comparison methods that make no use of indices for comparisons. These are used
+  // for sorting operations when reading dex files.
+  int slowCompareTo(T other);
+  int slowCompareTo(T other, NamingLens namingLens);
+  // Layered comparison methods that make use of indices for subpart comparisons. These rely
+  // on subparts already being sorted and having indices assigned.
+  int layeredCompareTo(T other, NamingLens namingLens);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java b/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
new file mode 100644
index 0000000..826e260
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/ProgramClassVisitor.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Implements traversal of the class hierarchy in topological order. A class is visited after its
+ * super class and its interfaces are visited. Only visits program classes and does NOT visit 
+ * classpath, nor library classes.
+ *
+ * NOTE: The visiting is processed by traversing program classes only, which means that
+ * in presence of classpath it is NOT guaranteed that class C is visited before class D
+ * if there exists a classpath class X in class hierarchy between C and D, like:
+ *
+ * <pre>
+ *   class ProgramClassS {}
+ *   class ClasspathClassX extends ProgramClassS {}
+ *   class ProgramClassD extends ClasspathClassX {}
+ * </pre>
+ *
+ * The above consideration does not apply to library classes, since we assume library
+ * classes never extend or implement program/classpath class.
+ */
+public abstract class ProgramClassVisitor {
+
+  final DexApplication application;
+  private final Set<DexItem> visited = new HashSet<>();
+
+  protected ProgramClassVisitor(DexApplication application) {
+    this.application = application;
+  }
+
+  private void accept(DexType type) {
+    if (type == null || visited.contains(type)) {
+      return;
+    }
+    DexClass clazz = application.programDefinitionFor(type);
+    if (clazz != null) {
+      accept(clazz);
+      return;
+    }
+    visit(type);
+    visited.add(type);
+  }
+
+  private void accept(DexTypeList types) {
+    for (DexType type : types.values) {
+      accept(type);
+    }
+  }
+
+  private void accept(DexClass clazz) {
+    if (visited.contains(clazz)) {
+      return;
+    }
+    accept(clazz.superType);
+    accept(clazz.interfaces);
+    visit(clazz);
+    visited.add(clazz);
+  }
+
+  public void run(DexProgramClass[] classes) {
+    for (DexProgramClass clazz : classes) {
+      accept(clazz);
+    }
+  }
+
+  public void run() {
+    for (DexProgramClass clazz : application.classes()) {
+      accept(clazz);
+    }
+  }
+
+  /**
+   * Called for each library class used in the class hierarchy. A library class is a class that is
+   * not present in the application.
+   */
+  public abstract void visit(DexType type);
+
+  /**
+   * Called for each class defined in the application.
+   */
+  public abstract void visit(DexClass clazz);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
new file mode 100644
index 0000000..45824c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.graph;
+
+public abstract class UseRegistry {
+
+  public abstract boolean registerInvokeVirtual(DexMethod method);
+
+  public abstract boolean registerInvokeDirect(DexMethod method);
+
+  public abstract boolean registerInvokeStatic(DexMethod method);
+
+  public abstract boolean registerInvokeInterface(DexMethod method);
+
+  public abstract boolean registerInvokeSuper(DexMethod method);
+
+  public abstract boolean registerInstanceFieldWrite(DexField field);
+
+  public abstract boolean registerInstanceFieldRead(DexField field);
+
+  public abstract boolean registerNewInstance(DexType type);
+
+  public abstract boolean registerStaticFieldRead(DexField field);
+
+  public abstract boolean registerStaticFieldWrite(DexField field);
+
+  public abstract boolean registerTypeReference(DexType type);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
new file mode 100644
index 0000000..ba11abb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.AddDouble;
+import com.android.tools.r8.code.AddDouble2Addr;
+import com.android.tools.r8.code.AddFloat;
+import com.android.tools.r8.code.AddFloat2Addr;
+import com.android.tools.r8.code.AddInt;
+import com.android.tools.r8.code.AddInt2Addr;
+import com.android.tools.r8.code.AddIntLit16;
+import com.android.tools.r8.code.AddIntLit8;
+import com.android.tools.r8.code.AddLong;
+import com.android.tools.r8.code.AddLong2Addr;
+
+public class Add extends ArithmeticBinop {
+
+  public Add(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return true;
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new AddInt(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+    // the first part of the result long before reading the second part of the input longs.
+    // Therefore, there can be no overlap of the second part of an input long and the first
+    // part of the output long.
+    assert dest != left + 1;
+    assert dest != right + 1;
+    return new AddLong(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+    return new AddFloat(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+    return new AddDouble(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new AddInt2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new AddLong2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+    return new AddFloat2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+    return new AddDouble2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new AddIntLit8(dest, left, constant);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new AddIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asAdd().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asAdd().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left + right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left + right;
+  }
+
+  @Override
+  float foldFloat(float left, float right) {
+    return left + right;
+  }
+
+  @Override
+  double foldDouble(double left, double right) {
+    return left + right;
+  }
+
+  @Override
+  public boolean isAdd() {
+    return true;
+  }
+
+  @Override
+  public Add asAdd() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
new file mode 100644
index 0000000..a6e71ae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.AndInt;
+import com.android.tools.r8.code.AndInt2Addr;
+import com.android.tools.r8.code.AndIntLit16;
+import com.android.tools.r8.code.AndIntLit8;
+import com.android.tools.r8.code.AndLong;
+import com.android.tools.r8.code.AndLong2Addr;
+
+public class And extends LogicalBinop {
+
+  public And(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isAnd() {
+    return true;
+  }
+
+  @Override
+  public And asAnd() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return true;
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new AndInt(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+    // the first part of the result long before reading the second part of the input longs.
+    // Therefore, there can be no overlap of the second part of an input long and the first
+    // part of the output long.
+    assert dest != left + 1;
+    assert dest != right + 1;
+    return new AndLong(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new AndInt2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new AndLong2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new AndIntLit8(dest, left, constant);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new AndIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asAnd().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asAnd().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left & right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left & right;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
new file mode 100644
index 0000000..94c415e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.utils.InternalOptions;
+
+/**
+ * Argument pseudo instruction used to introduce values for all arguments for SSA conversion.
+ */
+public class Argument extends Instruction {
+
+  public Argument(Value outValue) {
+    super(outValue);
+    outValue.markAsArgument();;
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    // Never remove argument instructions. That would change the signature of the method.
+    // TODO(ager): If we can tell that a method never uses an argument we might be able to
+    // rewrite the signature and call-sites.
+    return false;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "Argument has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U16BIT_MAX;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.addArgument(this);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isArgument();
+    return true;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isArgument();
+    return 0;
+  }
+
+  @Override
+  public boolean isArgument() {
+    return true;
+  }
+
+  @Override
+  public Argument asArgument() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
new file mode 100644
index 0000000..e793190
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public abstract class ArithmeticBinop extends Binop {
+
+  public ArithmeticBinop(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  public abstract com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right);
+
+  public abstract Instruction CreateLong(int dest, int left, int right);
+
+  public abstract Instruction CreateFloat(int dest, int left, int right);
+
+  public abstract Instruction CreateDouble(int dest, int left, int right);
+
+  public abstract Instruction CreateInt2Addr(int left, int right);
+
+  public abstract Instruction CreateLong2Addr(int left, int right);
+
+  public abstract Instruction CreateFloat2Addr(int left, int right);
+
+  public abstract Instruction CreateDouble2Addr(int left, int right);
+
+  public abstract Instruction CreateIntLit8(int dest, int left, int constant);
+
+  public abstract Instruction CreateIntLit16(int dest, int left, int constant);
+
+  @Override
+  public boolean canBeFolded() {
+    return (type == NumericType.INT || type == NumericType.LONG || type == NumericType.FLOAT
+            || type == NumericType.DOUBLE)
+        && leftValue().isConstant() && rightValue().isConstant();
+  }
+
+  public boolean needsValueInRegister(Value value) {
+    assert !isSub();  // Constants in instructions for sub must be handled in subclass Sub.
+    // Always require the left value in a register. If left and right are the same value, then
+    // both will use its register.
+    if (value == leftValue()) {
+      return true;
+    }
+    assert value == rightValue();
+    return !fitsInDexInstruction(value);
+  }
+
+  public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+    assert canBeFolded();
+    if (type == NumericType.INT) {
+      int left = leftValue().getConstInstruction().asConstNumber().getIntValue();
+      int right = rightValue().getConstInstruction().asConstNumber().getIntValue();
+      int result = foldIntegers(left, right);
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+      return new ConstNumber(ConstType.INT, value, result);
+    } else if (type == NumericType.LONG) {
+      long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
+      long right = rightValue().getConstInstruction().asConstNumber().getLongValue();
+      long result = foldLongs(left, right);
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+      return new ConstNumber(ConstType.LONG, value, result);
+    } else if (type == NumericType.FLOAT) {
+      float left = leftValue().getConstInstruction().asConstNumber().getFloatValue();
+      float right = rightValue().getConstInstruction().asConstNumber().getFloatValue();
+      float result = foldFloat(left, right);
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+      return new ConstNumber(ConstType.FLOAT, value, Float.floatToIntBits(result));
+    } else {
+      assert type == NumericType.DOUBLE;
+      double left = leftValue().getConstInstruction().asConstNumber().getDoubleValue();
+      double right = rightValue().getConstInstruction().asConstNumber().getDoubleValue();
+      double result = foldDouble(left, right);
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+      return new ConstNumber(ConstType.DOUBLE, value, Double.doubleToLongBits(result));
+    }
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    // Method needsValueInRegister ensures that left value has an allocated register.
+    int left = builder.allocatedRegister(leftValue(), getNumber());
+    int dest = builder.allocatedRegister(outValue, getNumber());
+    Instruction instruction = null;
+    if (isTwoAddr(builder)) {
+      int right = builder.allocatedRegister(rightValue(), getNumber());
+      if (left != dest) {
+        assert isCommutative();
+        assert right == dest;
+        right = left;
+      }
+      switch (type) {
+        case DOUBLE:
+          instruction = CreateDouble2Addr(dest, right);
+          break;
+        case FLOAT:
+          instruction = CreateFloat2Addr(dest, right);
+          break;
+        case INT:
+          instruction = CreateInt2Addr(dest, right);
+          break;
+        case LONG:
+          instruction = CreateLong2Addr(dest, right);
+          break;
+      }
+    } else if (!rightValue().needsRegister()) {
+      assert !isSub();  // Constants in instructions for sub must be handled in subclass Sub.
+      assert fitsInDexInstruction(rightValue());
+      ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+      if (right.is8Bit()) {
+        instruction = CreateIntLit8(dest, left, right.getIntValue());
+      } else {
+        assert right.is16Bit();
+        instruction = CreateIntLit16(dest, left, right.getIntValue());
+      }
+    } else {
+      int right = builder.allocatedRegister(rightValue(), getNumber());
+      switch (type) {
+        case DOUBLE:
+          instruction = CreateDouble(dest, left, right);
+          break;
+        case FLOAT:
+          instruction = CreateFloat(dest, left, right);
+          break;
+        case INT:
+          instruction = CreateInt(dest, left, right);
+          break;
+        case LONG:
+          instruction = CreateLong(dest, left, right);
+          break;
+      }
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean isArithmeticBinop() {
+    return true;
+  }
+
+  @Override
+  public ArithmeticBinop asArithmeticBinop() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
new file mode 100644
index 0000000..c2cd2ea
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Aget;
+import com.android.tools.r8.code.AgetBoolean;
+import com.android.tools.r8.code.AgetByte;
+import com.android.tools.r8.code.AgetChar;
+import com.android.tools.r8.code.AgetObject;
+import com.android.tools.r8.code.AgetShort;
+import com.android.tools.r8.code.AgetWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import java.util.Arrays;
+
+public class ArrayGet extends Instruction {
+
+  private final MemberType type;
+
+  public ArrayGet(MemberType type, Value dest, Value array, Value index) {
+    super(dest, Arrays.asList(array, index));
+    this.type = type;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value array() {
+    return inValues.get(0);
+  }
+
+  public Value index() {
+    return inValues.get(1);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    int array = builder.allocatedRegister(array(), getNumber());
+    int index = builder.allocatedRegister(index(), getNumber());
+    com.android.tools.r8.code.Instruction instruction;
+    switch (type) {
+      case SINGLE:
+        instruction = new Aget(dest, array, index);
+        break;
+      case WIDE:
+        instruction = new AgetWide(dest, array, index);
+        break;
+      case OBJECT:
+        instruction = new AgetObject(dest, array, index);
+        break;
+      case BOOLEAN:
+        instruction = new AgetBoolean(dest, array, index);
+        break;
+      case BYTE:
+        instruction = new AgetByte(dest, array, index);
+        break;
+      case CHAR:
+        instruction = new AgetChar(dest, array, index);
+        break;
+      case SHORT:
+        instruction = new AgetShort(dest, array, index);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+    // We cannot share ArrayGet instructions without knowledge of the type of the array input.
+    // If multiple primitive array types flow to the same ArrayGet instruction the art verifier
+    // gets confused.
+    return false;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asArrayGet().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asArrayGet().type.ordinal();
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    // TODO: Determine if the array index out-of-bounds exception cannot happen.
+    return true;
+  }
+
+  @Override
+  public boolean isArrayGet() {
+    return true;
+  }
+
+  @Override
+  public ArrayGet asArrayGet() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
new file mode 100644
index 0000000..40bd443
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+
+public class ArrayLength extends Instruction {
+
+  public ArrayLength(Value dest, Value array) {
+    super(dest, array);
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value array() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public boolean isArrayLength() {
+    return true;
+  }
+
+  @Override
+  public ArrayLength asArrayLength() {
+    return this;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    int array = builder.allocatedRegister(array(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.ArrayLength(dest, array));
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+    if (super.identicalAfterRegisterAllocation(other, allocator)) {
+      // The array length instruction doesn't carry the element type. The art verifier doesn't
+      // allow an array length instruction into which arrays of two different base types can
+      // flow. Therefore, as a safe approximation we only consider array length instructions
+      // equal when they have the same inflowing SSA value.
+      // TODO(ager): We could perform conservative type propagation earlier in the pipeline and
+      // add a member type to array length instructions.
+      return array() == other.asArrayLength().array();
+    }
+    return false;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isArrayLength();
+    return true;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isArrayLength();
+    return 0;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
new file mode 100644
index 0000000..5e1ff16
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Aput;
+import com.android.tools.r8.code.AputBoolean;
+import com.android.tools.r8.code.AputByte;
+import com.android.tools.r8.code.AputChar;
+import com.android.tools.r8.code.AputObject;
+import com.android.tools.r8.code.AputShort;
+import com.android.tools.r8.code.AputWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+
+public class ArrayPut extends Instruction {
+
+  private final MemberType type;
+
+  public ArrayPut(MemberType type, List<Value> ins) {
+    super(null, ins);
+    assert type != null;
+    this.type = type;
+  }
+
+  public Value source() {
+    return inValues.get(0);
+  }
+
+  public Value array() {
+    return inValues.get(1);
+  }
+
+  public Value index() {
+    return inValues.get(2);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int source = builder.allocatedRegister(source(), getNumber());
+    int array = builder.allocatedRegister(array(), getNumber());
+    int index = builder.allocatedRegister(index(), getNumber());
+    com.android.tools.r8.code.Instruction instruction;
+    switch (type) {
+      case SINGLE:
+        instruction = new Aput(source, array, index);
+        break;
+      case WIDE:
+        instruction = new AputWide(source, array, index);
+        break;
+      case OBJECT:
+        instruction = new AputObject(source, array, index);
+        break;
+      case BOOLEAN:
+        instruction = new AputBoolean(source, array, index);
+        break;
+      case BYTE:
+        instruction = new AputByte(source, array, index);
+        break;
+      case CHAR:
+        instruction = new AputChar(source, array, index);
+        break;
+      case SHORT:
+        instruction = new AputShort(source, array, index);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "ArrayPut instructions define no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean instructionInstanceCanThrow() {
+    if (index().isConstant() && !array().isPhi() && array().definition.isNewArrayEmpty()) {
+      Value newArraySizeValue = array().definition.asNewArrayEmpty().size();
+      if (newArraySizeValue.isConstant()) {
+        int newArraySize = newArraySizeValue.getConstInstruction().asConstNumber().getIntValue();
+        int index = index().getConstInstruction().asConstNumber().getIntValue();
+        return newArraySize <= 0 || index < 0 || newArraySize <= index;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    // ArrayPut has side-effects on input values.
+    return false;
+  }
+
+  @Override
+  public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+    // We cannot share ArrayPut instructions without knowledge of the type of the array input.
+    // If multiple primitive array types flow to the same ArrayPut instruction the art verifier
+    // gets confused.
+    return false;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asArrayPut().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asArrayPut().type.ordinal();
+  }
+
+  @Override
+  public boolean isArrayPut() {
+    return true;
+  }
+
+  @Override
+  public ArrayPut asArrayPut() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
new file mode 100644
index 0000000..db44f9a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -0,0 +1,1190 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Basic block abstraction.
+ */
+public class BasicBlock {
+
+  public enum ThrowingInfo {
+    NO_THROW, CAN_THROW
+  }
+
+  public enum EdgeType {
+    NON_EDGE,
+    NORMAL,
+    EXCEPTIONAL
+  }
+
+  public static class Pair implements Comparable<Pair> {
+
+    public BasicBlock first;
+    public BasicBlock second;
+
+    public Pair(BasicBlock first, BasicBlock second) {
+      this.first = first;
+      this.second = second;
+    }
+
+    @Override
+    public int compareTo(Pair o) {
+      if (first != o.first) {
+        return first.getNumber() - o.first.getNumber();
+      }
+      if (second != o.second) {
+        return second.getNumber() - o.second.getNumber();
+      }
+      return 0;
+    }
+  }
+
+  private final List<BasicBlock> successors = new ArrayList<>();
+  private final List<BasicBlock> predecessors = new ArrayList<>();
+
+  // Catch handler information about which successors are catch handlers and what their guards are.
+  private CatchHandlers<Integer> catchHandlers = CatchHandlers.EMPTY_INDICES;
+
+  private LinkedList<Instruction> instructions = new LinkedList<>();
+  private int number = -1;
+  private List<Phi> phis = new ArrayList<>();
+
+  // State used during SSA construction. The SSA construction is based on the paper:
+  //
+  // "Simple and Efficient Construction of Static Single Assignment Form"
+  // http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf
+  //
+  // A basic block is filled when local value numbering is complete for that block.
+  // A basic block is sealed when all predecessor blocks have been filled.
+  //
+  // Therefore, for a sealed block we can always search backwards to find reaching values
+  // in predecessor blocks.
+  private boolean filled = false;
+  private boolean sealed = false;
+  private Map<Integer, Phi> incompletePhis = new HashMap<>();
+  private int estimatedPredecessorsCount = 0;
+  private int unfilledPredecessorsCount = 0;
+
+  // State used for basic block sorting and tracing.
+  private int color = 0;
+
+  // Map of registers to current SSA value. Used during SSA numbering and cleared once filled.
+  private Map<Integer, Value> currentDefinitions = new HashMap<>();
+
+  public List<BasicBlock> getSuccessors() {
+    return successors;
+  }
+
+  public List<BasicBlock> getNormalSucessors() {
+    if (!hasCatchHandlers()) {
+      return successors;
+    }
+    Set<Integer> handlers = catchHandlers.getUniqueTargets();
+    ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder();
+    for (int i = 0; i < successors.size(); i++) {
+      if (!handlers.contains(i)) {
+        normals.add(successors.get(i));
+      }
+    }
+    return normals.build();
+  }
+
+  public List<BasicBlock> getPredecessors() {
+    return predecessors;
+  }
+
+  public List<BasicBlock> getNormalPredecessors() {
+    ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder();
+    for (BasicBlock predecessor : predecessors) {
+      if (!predecessor.isCatchSuccessor(this)) {
+        normals.add(predecessor);
+      }
+    }
+    return normals.build();
+  }
+
+  public void removeSuccessor(BasicBlock block) {
+    int index = successors.indexOf(block);
+    assert index >= 0 : "removeSuccessor did not find the successor to remove";
+    removeSuccessorsByIndex(Arrays.asList(index));
+  }
+
+  public void removePredecessor(BasicBlock block) {
+    int index = predecessors.indexOf(block);
+    assert index >= 0 : "removePredecessor did not find the predecessor to remove";
+    predecessors.remove(index);
+    if (phis != null) {
+      for (Phi phi : getPhis()) {
+        phi.removeOperand(index);
+      }
+      // Collect and remove trivial phis after block removal.
+      List<Phi> trivials = new ArrayList<>();
+      for (Phi phi : getPhis()) {
+        if (phi.isTrivialPhi()) {
+          trivials.add(phi);
+        }
+      }
+      for (Phi phi : trivials) {
+        phi.removeTrivialPhi();
+      }
+    }
+  }
+
+  private void swapSuccessors(int x, int y) {
+    assert x != y;
+    if (hasCatchHandlers()) {
+      List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets());
+      for (int i = 0; i < targets.size(); i++) {
+        if (targets.get(i) == x) {
+          targets.set(i, y);
+        } else if (targets.get(i) == y) {
+          targets.set(i, x);
+        }
+      }
+      catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets);
+    }
+    BasicBlock tmp = successors.get(x);
+    successors.set(x, successors.get(y));
+    successors.set(y, tmp);
+  }
+
+  public void replaceSuccessor(BasicBlock block, BasicBlock newBlock) {
+    assert successors.contains(block) : "attempt to replace non-existent successor";
+
+    if (successors.contains(newBlock)) {
+      int indexOfOldBlock = successors.indexOf(block);
+      int indexOfNewBlock = successors.indexOf(newBlock);
+
+      // Always rewrite catch handlers.
+      if (hasCatchHandlers()) {
+        List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets());
+        for (int i = 0; i < targets.size(); i++) {
+          if (targets.get(i) == indexOfOldBlock) {
+            targets.set(i, indexOfNewBlock);
+          }
+          if (targets.get(i) > indexOfOldBlock) {
+            targets.set(i, targets.get(i) - 1);
+          }
+        }
+        catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets);
+      }
+
+      // Check if the replacement influences jump targets and rewrite as needed.
+      if (exit().isGoto()) {
+        if (indexOfOldBlock == successors.size() - 1 && indexOfNewBlock != successors.size() - 2) {
+          // Replacing the goto target and the new block will not become the goto target.
+          // We perform a swap to get the new block into the goto target position.
+          swapSuccessors(indexOfOldBlock - 1, indexOfNewBlock);
+        }
+      } else if (exit().isIf()) {
+        if (indexOfNewBlock >= successors.size() - 2 && indexOfOldBlock >= successors.size() - 2) {
+          // New and old are true target and fallthrough, replace last instruction with a goto.
+          Instruction instruction = getInstructions().removeLast();
+          for (Value value : instruction.inValues()) {
+            if (value.hasUsersInfo()) {
+              value.removeUser(instruction);
+            }
+          }
+          Instruction exit = new Goto();
+          exit.setBlock(this);
+          getInstructions().addLast(exit);
+        } else if (indexOfOldBlock >= successors.size() - 2) {
+          // Old is either true or fallthrough and we need to swap the new block into the right
+          // position to become that target.
+          swapSuccessors(indexOfOldBlock - 1, indexOfNewBlock);
+        }
+      } else if (exit().isSwitch()) {
+        // Rewrite fallthrough and case target indices.
+        Switch exit = exit().asSwitch();
+        if (exit.getFallthroughBlockIndex() == indexOfOldBlock) {
+          exit.setFallthroughBlockIndex(indexOfNewBlock);
+        }
+        if (exit.getFallthroughBlockIndex() > indexOfOldBlock) {
+          exit.setFallthroughBlockIndex(exit.getFallthroughBlockIndex() - 1);
+        }
+        int[] indices = exit.targetBlockIndices();
+        for (int i = 0; i < indices.length; i++) {
+          if (indices[i] == indexOfOldBlock) {
+            indices[i] = indexOfNewBlock;
+          }
+          if (indices[i] > indexOfOldBlock) {
+            indices[i] = indices[i] - 1;
+          }
+        }
+      }
+
+      // Remove the replaced successor.
+      boolean removed = successors.remove(block);
+      assert removed;
+    } else {
+      // If the new block is not a successor we don't have to rewrite indices or instructions
+      // and we can just replace the old successor with the new one.
+      for (int i = 0; i < successors.size(); i++) {
+        if (successors.get(i) == block) {
+          successors.set(i, newBlock);
+          return;
+        }
+      }
+    }
+  }
+
+  public void replacePredecessor(BasicBlock block, BasicBlock newBlock) {
+    for (int i = 0; i < predecessors.size(); i++) {
+      if (predecessors.get(i) == block) {
+        predecessors.set(i, newBlock);
+        return;
+      }
+    }
+    assert false : "replaceSuccessor did not find the predecessor to replace";
+  }
+
+  public void removeSuccessorsByIndex(List<Integer> successorsToRemove) {
+    if (successorsToRemove.isEmpty()) {
+      return;
+    }
+    List<BasicBlock> copy = new ArrayList<>(successors);
+    successors.clear();
+    int current = 0;
+    for (int i : successorsToRemove) {
+      successors.addAll(copy.subList(current, i));
+      current = i + 1;
+    }
+    successors.addAll(copy.subList(current, copy.size()));
+
+    if (hasCatchHandlers()) {
+      int size = catchHandlers.size();
+      List<DexType> guards = new ArrayList<>(size);
+      List<Integer> targets = new ArrayList<>(size);
+      current = 0;
+      for (int i = 0; i < catchHandlers.getAllTargets().size(); i++) {
+        if (successorsToRemove.contains(catchHandlers.getAllTargets().get(i))) {
+          guards.addAll(catchHandlers.getGuards().subList(current, i));
+          targets.addAll(catchHandlers.getAllTargets().subList(current, i));
+          current = i + 1;
+        }
+      }
+      if (guards.isEmpty()) {
+        catchHandlers = CatchHandlers.EMPTY_INDICES;
+      } else {
+        catchHandlers = new CatchHandlers<>(guards, targets);
+      }
+    }
+  }
+
+  public void removePredecessorsByIndex(List<Integer> predecessorsToRemove) {
+    if (predecessorsToRemove.isEmpty()) {
+      return;
+    }
+    List<BasicBlock> copy = new ArrayList<>(predecessors);
+    predecessors.clear();
+    int current = 0;
+    for (int i : predecessorsToRemove) {
+      predecessors.addAll(copy.subList(current, i));
+      current = i + 1;
+    }
+    predecessors.addAll(copy.subList(current, copy.size()));
+  }
+
+  public void removePhisByIndex(List<Integer> predecessorsToRemove) {
+    for (Phi phi : phis) {
+      phi.removeOperandsByIndex(predecessorsToRemove);
+    }
+  }
+
+  public List<Phi> getPhis() {
+    return phis;
+  }
+
+  public void setPhis(List<Phi> phis) {
+    this.phis = phis;
+  }
+
+  public boolean isFilled() {
+    return filled;
+  }
+
+  void setFilledForTesting() {
+    filled = true;
+  }
+
+  public boolean hasCatchHandlers() {
+    assert catchHandlers != null;
+    return !catchHandlers.isEmpty();
+  }
+
+  public int getNumber() {
+    assert number >= 0;
+    return number;
+  }
+
+  public void setNumber(int number) {
+    assert number >= 0;
+    this.number = number;
+  }
+
+  public LinkedList<Instruction> getInstructions() {
+    return instructions;
+  }
+
+  public Instruction entry() {
+    return instructions.get(0);
+  }
+
+  public JumpInstruction exit() {
+    assert filled;
+    assert instructions.get(instructions.size() - 1).isJumpInstruction();
+    return instructions.get(instructions.size() - 1).asJumpInstruction();
+  }
+
+  public void clearUserInfo() {
+    phis = null;
+    instructions.forEach(Instruction::clearUserInfo);
+  }
+
+  public void buildDex(DexBuilder builder) {
+    for (Instruction instruction : instructions) {
+      instruction.buildDex(builder);
+    }
+  }
+
+  public void mark() {
+    assert color == 0;
+    color = 1;
+  }
+
+  public void clearMark() {
+    color = 0;
+  }
+
+  public boolean isMarked() {
+    return color == 1;
+  }
+
+  public void setColor(int color) {
+    this.color = color;
+  }
+
+  public int getColor() {
+    return color;
+  }
+
+  public boolean hasColor(int color) {
+    return this.color == color;
+  }
+
+  public void incrementUnfilledPredecessorCount() {
+    ++unfilledPredecessorsCount;
+    ++estimatedPredecessorsCount;
+  }
+
+  public void decrementUnfilledPredecessorCount(int n) {
+    unfilledPredecessorsCount -= n;
+    estimatedPredecessorsCount -= n;
+  }
+
+  public void decrementUnfilledPredecessorCount() {
+    --unfilledPredecessorsCount;
+    --estimatedPredecessorsCount;
+  }
+
+  public boolean verifyFilledPredecessors() {
+    assert estimatedPredecessorsCount == predecessors.size();
+    assert unfilledPredecessorsCount == 0;
+    return true;
+  }
+
+  public void addPhi(Phi phi) {
+    phis.add(phi);
+  }
+
+  public void removePhi(Phi phi) {
+    phis.remove(phi);
+  }
+
+  public void add(Instruction next) {
+    assert !isFilled();
+    instructions.add(next);
+    next.setBlock(this);
+  }
+
+  public void close(IRBuilder builder) {
+    assert !isFilled();
+    assert !instructions.isEmpty();
+    filled = true;
+    sealed = unfilledPredecessorsCount == 0;
+    assert exit().isJumpInstruction();
+    assert verifyNoValuesAfterThrowingInstruction();
+    for (BasicBlock successor : successors) {
+      successor.filledPredecessor(builder);
+    }
+  }
+
+  public void link(BasicBlock successor) {
+    assert !successors.contains(successor);
+    assert !successor.predecessors.contains(this);
+    successors.add(successor);
+    successor.predecessors.add(this);
+  }
+
+  private boolean allPredecessorsDominated(BasicBlock block, DominatorTree dominator) {
+    for (BasicBlock pred : block.predecessors) {
+      if (!dominator.dominatedBy(pred, block)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean blocksClean(List<BasicBlock> blocks) {
+    blocks.forEach((b) -> {
+      assert b.predecessors.size() == 0;
+      assert b.successors.size() == 0;
+    });
+    return true;
+  }
+
+  /**
+   * Unlinks this block from a single predecessor.
+   *
+   * @return returns the unlinked predecessor.
+   */
+  public BasicBlock unlinkSinglePredecessor() {
+    assert predecessors.size() == 1;
+    assert predecessors.get(0).successors.size() == 1;
+    BasicBlock unlinkedBlock = predecessors.get(0);
+    predecessors.get(0).successors.clear();
+    predecessors.clear();
+    return unlinkedBlock;
+  }
+
+  /**
+   * Unlinks this block from a single normal successor.
+   *
+   * @return Returns the unlinked successor.
+   */
+  public BasicBlock unlinkSingleSuccessor() {
+    assert !hasCatchHandlers();
+    assert successors.size() == 1;
+    assert successors.get(0).predecessors.size() == 1;
+    BasicBlock unlinkedBlock = successors.get(0);
+    successors.get(0).predecessors.clear();
+    successors.clear();
+    return unlinkedBlock;
+  }
+
+  /**
+   * Unlinks this block from a single predecessor and successor.
+   *
+   * @return Returns the unlinked successor
+   */
+  public BasicBlock unlinkSingle() {
+    unlinkSinglePredecessor();
+    return unlinkSingleSuccessor();
+  }
+
+  public List<BasicBlock> unlink(BasicBlock successor, DominatorTree dominator) {
+    assert successors.contains(successor);
+    assert successor.predecessors.contains(this);
+    List<BasicBlock> removedBlocks = new ArrayList<>();
+    TreeSet<Pair> worklist = new TreeSet<>();
+    worklist.add(new Pair(this, successor));
+    while (!worklist.isEmpty()) {
+      Pair pair = worklist.pollFirst();
+      BasicBlock pred = pair.first;
+      BasicBlock succ = pair.second;
+      assert pred.successors.contains(succ);
+      assert succ.predecessors.contains(pred);
+      int size = pred.successors.size();
+      pred.removeSuccessor(succ);
+      assert size == pred.successors.size() + 1;
+      size = succ.predecessors.size();
+      succ.removePredecessor(pred);
+      assert size == succ.predecessors.size() + 1;
+      // A predecessor has been removed. If all remaining predecessors are dominated by this block
+      // schedule it for removal, as it is no longer reachable.
+      if (allPredecessorsDominated(succ, dominator)) {
+        removedBlocks.add(succ);
+        for (BasicBlock block : succ.successors) {
+          worklist.add(new Pair(succ, block));
+        }
+        for (Instruction instruction : succ.getInstructions()) {
+          for (Value value : instruction.inValues) {
+            value.removeUser(instruction);
+          }
+          Value previousLocalValue = instruction.getPreviousLocalValue();
+          if (previousLocalValue != null) {
+            previousLocalValue.removeDebugUser(instruction);
+          }
+        }
+      }
+    }
+    assert blocksClean(removedBlocks);
+    return removedBlocks;
+  }
+
+  public void linkCatchSuccessors(List<DexType> guards, List<BasicBlock> targets) {
+    List<Integer> successorIndexes = new ArrayList<>(targets.size());
+    for (BasicBlock target : targets) {
+      int index = successors.indexOf(target);
+      if (index < 0) {
+        index = successors.size();
+        link(target);
+      }
+      successorIndexes.add(index);
+    }
+    catchHandlers = new CatchHandlers<>(guards, successorIndexes);
+  }
+
+  public void clearCurrentDefinitions() {
+    currentDefinitions = null;
+    for (Phi phi : getPhis()) {
+      phi.clearDefinitionsUsers();
+    }
+  }
+
+  // The proper incoming register for a catch successor (that is otherwise shadowed by the out-value
+  // of a throwing instruction) is stored at the negative register-index in the definitions map.
+  // (See readCurrentDefinition/writeCurrentDefinition/updateCurrentDefinition).
+  private int onThrowValueRegister(int register) {
+    return -(register + 1);
+  }
+
+  private Value readOnThrowValue(int register, EdgeType readingEdge) {
+    if (readingEdge == EdgeType.EXCEPTIONAL) {
+      return currentDefinitions.get(onThrowValueRegister(register));
+    }
+    return null;
+  }
+
+  private boolean isOnThrowValue(int register, EdgeType readingEdge) {
+    return readOnThrowValue(register, readingEdge) != null;
+  }
+
+  public Value readCurrentDefinition(int register, EdgeType readingEdge) {
+    // If the block reading the current definition is a catch successor, then we must return the
+    // previous value of the throwing-instructions outgoing register if any.
+    Value result = readOnThrowValue(register, readingEdge);
+    if (result != null) {
+      return result == Value.UNDEFINED ? null : result;
+    }
+    return currentDefinitions.get(register);
+  }
+
+  public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) {
+    // If the reading/writing block is a catch successor, possibly update the on-throw value.
+    if (isOnThrowValue(register, readingEdge)) {
+      register = onThrowValueRegister(register);
+    }
+    // We keep track of all users of phis so that we can update all users during
+    // trivial phi elimination. We only rewrite phi values during IR construction, so
+    // we only need to record definition users for phis.
+    Value previousValue = currentDefinitions.get(register);
+    if (value.isPhi()) {
+      value.asPhi().addDefinitionsUser(currentDefinitions);
+    }
+    assert verifyOnThrowWrite(register);
+    currentDefinitions.put(register, value);
+    // We have replaced one occurrence of value in currentDefinitions. There could be
+    // other occurrences. We only remove currentDefinitions from the set of users
+    // of the phi if we have removed all occurrences.
+    if (previousValue != null &&
+        previousValue.isPhi() &&
+        !currentDefinitions.values().contains(previousValue)) {
+      previousValue.asPhi().removeDefinitionsUser(currentDefinitions);
+    }
+  }
+
+  public void writeCurrentDefinition(int register, Value value, ThrowingInfo throwing) {
+    // If this write is dependent on not throwing, we move the existing value to its negative index
+    // so that it can be read by catch successors.
+    if (throwing == ThrowingInfo.CAN_THROW) {
+      Value previous = currentDefinitions.get(register);
+      assert verifyOnThrowWrite(register);
+      currentDefinitions.put(onThrowValueRegister(register),
+          previous == null ? Value.UNDEFINED : previous);
+    }
+    updateCurrentDefinition(register, value, EdgeType.NON_EDGE);
+  }
+
+  public void filledPredecessor(IRBuilder builder) {
+    assert unfilledPredecessorsCount > 0;
+    if (--unfilledPredecessorsCount == 0) {
+      assert estimatedPredecessorsCount == predecessors.size();
+      for (Phi phi : incompletePhis.values()) {
+        phi.addOperands(builder);
+      }
+      sealed = true;
+      incompletePhis.clear();
+    }
+  }
+
+  public EdgeType getEdgeType(BasicBlock successor) {
+    assert successors.indexOf(successor) >= 0;
+    return isCatchSuccessor(successor) ? EdgeType.EXCEPTIONAL : EdgeType.NORMAL;
+  }
+
+  public boolean isCatchSuccessor(BasicBlock block) {
+    if (!hasCatchHandlers()) {
+      return false;
+    }
+    return catchHandlers.getAllTargets().contains(successors.indexOf(block));
+  }
+
+  public int guardsForCatchSuccessor(BasicBlock block) {
+    assert isCatchSuccessor(block);
+    int index = successors.indexOf(block);
+    int count = 0;
+    for (int handler : catchHandlers.getAllTargets()) {
+      if (handler == index) {
+        count++;
+      }
+    }
+    assert count > 0;
+    return count;
+  }
+
+  public boolean isSealed() {
+    return sealed;
+  }
+
+  public void addIncompletePhi(int register, Phi phi, EdgeType readingEdge) {
+    if (isOnThrowValue(register, readingEdge)) {
+      register = onThrowValueRegister(register);
+    }
+    assert !incompletePhis.containsKey(register);
+    incompletePhis.put(register, phi);
+  }
+
+  public boolean hasIncompletePhis() {
+    return !incompletePhis.isEmpty();
+  }
+
+  public Collection<Integer> getIncompletePhiRegisters() {
+    return incompletePhis.keySet();
+  }
+
+  private void appendBasicBlockList(
+      StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix) {
+    if (list.size() > 0) {
+      for (BasicBlock block : list) {
+        builder.append(block.getNumber());
+        builder.append(postfix.apply(block));
+        builder.append(" ");
+      }
+    } else {
+      builder.append("-");
+    }
+  }
+
+  @Override
+  public String toString() {
+    return toDetailedString();
+  }
+
+  public String toSimpleString() {
+    return number < 0 ? super.toString() : ("block " + number);
+  }
+
+  private String predecessorPostfix(BasicBlock block) {
+    if (isCatchSuccessor(block)) {
+      return new String(new char[guardsForCatchSuccessor(block)]).replace("\0", "*");
+    }
+    return "";
+  }
+
+  public String toDetailedString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("block ");
+    builder.append(number);
+    builder.append(" (");
+    builder.append(System.identityHashCode(this));
+    builder.append(")");
+    builder.append(", pred-counts: " + predecessors.size());
+    if (unfilledPredecessorsCount > 0) {
+      builder.append(" (" + unfilledPredecessorsCount + " unfilled)");
+    }
+    builder.append(", succ-count: " + successors.size());
+    builder.append(", filled: " + isFilled());
+    builder.append(", sealed: " + isSealed());
+    builder.append("\n");
+    builder.append("predecessors: ");
+    appendBasicBlockList(builder, predecessors, b -> "");
+    builder.append("\n");
+    builder.append("successors: ");
+    appendBasicBlockList(builder, successors, this::predecessorPostfix);
+    if (successors.size() > 0) {
+      builder.append(" (");
+      if (hasCatchHandlers()) {
+        builder.append(catchHandlers.size());
+      } else {
+        builder.append("no");
+      }
+      builder.append(" try/catch successors)");
+    }
+    builder.append("\n");
+    if (phis != null && phis.size() > 0) {
+      for (Phi phi : phis) {
+        builder.append(phi.printPhi());
+        if (incompletePhis.values().contains(phi)) {
+          builder.append(" (incomplete)");
+        }
+        builder.append("\n");
+      }
+    } else {
+      builder.append("no phis\n");
+    }
+    for (Instruction instruction : instructions) {
+      StringUtils.appendLeftPadded(builder, Integer.toString(instruction.getNumber()), 6);
+      builder.append(": ");
+      StringUtils.appendRightPadded(builder, instruction.toString(), 20);
+      builder.append("\n");
+    }
+    return builder.toString();
+  }
+
+  public void print(CfgPrinter printer) {
+    printer.begin("block");
+    printer.print("name \"B").append(number).append("\"\n");
+    printer.print("from_bci -1\n");
+    printer.print("to_bci -1\n");
+    printer.print("predecessors");
+    printBlockList(printer, predecessors);
+    printer.ln();
+    printer.print("successors");
+    printBlockList(printer, successors);
+    printer.ln();
+    printer.print("xhandlers\n");
+    printer.print("flags\n");
+    printer.print("first_lir_id ").print(instructions.get(0).getNumber()).ln();
+    printer.print("last_lir_id ").print(instructions.get(instructions.size() - 1).getNumber()).ln();
+    printer.begin("HIR");
+    if (phis != null) {
+      for (Phi phi : phis) {
+        phi.print(printer);
+        printer.append(" <|@\n");
+      }
+    }
+    for (Instruction instruction : instructions) {
+      instruction.print(printer);
+      printer.append(" <|@\n");
+    }
+    printer.end("HIR");
+    printer.begin("LIR");
+    for (Instruction instruction : instructions) {
+      instruction.printLIR(printer);
+      printer.append(" <|@\n");
+    }
+    printer.end("LIR");
+    printer.end("block");
+  }
+
+  private static void printBlockList(CfgPrinter printer, List<BasicBlock> blocks) {
+    for (BasicBlock block : blocks) {
+      printer.append(" \"B").append(block.number).append("\"");
+    }
+  }
+
+  public void addPhiMove(Move move) {
+    // TODO(ager): Consider this more, is it always the case that we should add it before the
+    // exit instruction?
+    Instruction branch = exit();
+    instructions.set(instructions.size() - 1, move);
+    instructions.add(branch);
+  }
+
+  public void setInstructions(LinkedList<Instruction> instructions) {
+    this.instructions = instructions;
+  }
+
+  /**
+   * Remove a number of instructions. The instructions to remove are given as indexes in the
+   * instruction stream.
+   */
+  public void removeInstructions(List<Integer> toRemove) {
+    if (!toRemove.isEmpty()) {
+      LinkedList<Instruction> newInstructions = new LinkedList<>();
+      int nextIndex = 0;
+      for (Integer index : toRemove) {
+        assert index >= nextIndex;  // Indexes in toRemove must be sorted ascending.
+        newInstructions.addAll(instructions.subList(nextIndex, index));
+        instructions.get(index).clearBlock();
+        nextIndex = index + 1;
+      }
+      if (nextIndex < instructions.size()) {
+        newInstructions.addAll(instructions.subList(nextIndex, instructions.size()));
+      }
+      assert instructions.size() == newInstructions.size() + toRemove.size();
+      setInstructions(newInstructions);
+    }
+  }
+
+  /**
+   * Create a new basic block with a single goto instruction.
+   *
+   * <p>The constructed basic block has no predecessors and has one
+   * successors which is the target block.
+   *
+   * @param target the target of the goto block
+   * @param blockNumber the block number of the goto block
+   */
+  public static BasicBlock createGotoBlock(BasicBlock target, int blockNumber) {
+    BasicBlock block = createGotoBlock(blockNumber);
+    block.getSuccessors().add(target);
+    return block;
+  }
+
+  /**
+   * Create a new basic block with a single goto instruction.
+   *
+   * <p>The constructed basic block has no predecessors and no successors.
+   *
+   * @param blockNumber the block number of the goto block
+   */
+  public static BasicBlock createGotoBlock(int blockNumber) {
+    BasicBlock block = new BasicBlock();
+    block.add(new Goto());
+    block.close(null);
+    block.setNumber(blockNumber);
+    return block;
+  }
+
+  public boolean isTrivialGoto() {
+    return instructions.size() == 1 && exit().isGoto();
+  }
+
+  public boolean hasOneNormalExit() {
+    return successors.size() == 1 && exit().isGoto();
+  }
+
+  public CatchHandlers<BasicBlock> getCatchHandlers() {
+    if (!hasCatchHandlers()) {
+      return CatchHandlers.EMPTY_BASIC_BLOCK;
+    }
+    List<BasicBlock> targets = ListUtils.map(catchHandlers.getAllTargets(), successors::get);
+    return new CatchHandlers<>(catchHandlers.getGuards(), targets);
+  }
+
+  public CatchHandlers<Integer> getCatchHandlersWithSuccessorIndexes() {
+    return catchHandlers;
+  }
+
+  public void clearCatchHandlers() {
+    catchHandlers = CatchHandlers.EMPTY_INDICES;
+  }
+
+  public void transferCatchHandlers(BasicBlock other) {
+    catchHandlers = other.catchHandlers;
+    other.catchHandlers = CatchHandlers.EMPTY_INDICES;
+  }
+
+  public boolean canThrow() {
+    for (Instruction instruction : instructions) {
+      if (instruction.instructionTypeCanThrow()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // A block can have at most one "on throw" value.
+  private boolean verifyOnThrowWrite(int register) {
+    if (register >= 0) {
+      return true;
+    }
+    for (Integer other : currentDefinitions.keySet()) {
+      assert other >= 0 || other == register;
+    }
+    return true;
+  }
+
+  // Verify that if this block has a throwing instruction none of the following instructions may
+  // introduce an SSA value. Such values can lead to invalid uses since the values are not actually
+  // visible to exceptional successors.
+  private boolean verifyNoValuesAfterThrowingInstruction() {
+    if (hasCatchHandlers()) {
+      ListIterator<Instruction> iterator = listIterator(instructions.size());
+      while (iterator.hasPrevious()) {
+        Instruction instruction = iterator.previous();
+        if (instruction.instructionTypeCanThrow()) {
+          return true;
+        }
+        assert instruction.outValue() == null;
+      }
+    }
+    return true;
+  }
+
+  public InstructionIterator iterator() {
+    return new BasicBlockInstructionIterator(this);
+  }
+
+  public InstructionListIterator listIterator() {
+    return new BasicBlockInstructionIterator(this);
+  }
+
+  public InstructionListIterator listIterator(int index) {
+    return new BasicBlockInstructionIterator(this, index);
+  }
+
+  /**
+   * Creates a new empty block as a successor for this block.
+   *
+   * The new block will have all the normal successors of the original block.
+   *
+   * The catch successors are either on the original block or the new block depending on the
+   * value of <code>keepCatchHandlers</code>.
+   *
+   * The current block still has all the instructions, and the new block is empty instruction-wise.
+   *
+   * @param blockNumber block number for new block
+   * @param keepCatchHandlers keep catch successors on the original block
+   * @return the new block
+   */
+  BasicBlock createSplitBlock(int blockNumber, boolean keepCatchHandlers) {
+    boolean hadCatchHandlers = hasCatchHandlers();
+    BasicBlock newBlock = new BasicBlock();
+    newBlock.setNumber(blockNumber);
+
+    // Copy all successors including catch handlers to the new block, and update predecessors.
+    successors.forEach(newBlock.successors::add);
+    for (BasicBlock successor : newBlock.getSuccessors()) {
+      successor.replacePredecessor(this, newBlock);
+    }
+    successors.clear();
+    newBlock.catchHandlers = catchHandlers;
+    catchHandlers = CatchHandlers.EMPTY_INDICES;
+
+    // If the catch handlers should be kept on the original block move them back.
+    if (keepCatchHandlers && hadCatchHandlers) {
+      moveCatchHandlers(newBlock);
+    }
+
+    // Link the two blocks
+    link(newBlock);
+
+    // Mark the new block filled and sealed.
+    newBlock.filled = true;
+    newBlock.sealed = true;
+
+    return newBlock;
+  }
+
+  /**
+   * Moves catch successors from `fromBlock` into this block.
+   */
+  public void moveCatchHandlers(BasicBlock fromBlock) {
+    List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock);
+    for (BasicBlock successor : catchSuccessors) {
+      fromBlock.successors.remove(successor);
+      successor.removePredecessor(fromBlock);
+    }
+    fromBlock.catchHandlers = CatchHandlers.EMPTY_INDICES;
+  }
+
+  /**
+   * Clone catch successors from `fromBlock` into this block.
+   */
+  public void copyCatchHandlers(
+      IRCode code, ListIterator<BasicBlock> blockIterator, BasicBlock fromBlock) {
+    if (catchHandlers != null && catchHandlers.hasCatchAll()) {
+      return;
+    }
+    List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock);
+
+    // After cloning is done all catch handler targets are referenced from both the
+    // original and the newly created catch handlers. Thus, since we keep both of
+    // them, we need to split appropriate edges to make sure every catch handler
+    // target block has only one predecessor.
+    //
+    // Note that for each catch handler block target block we actually create two new blocks:
+    // a copy of the original block and a new block to serve as a merging point for
+    // the original and its copy. This actually simplifies things since we only need
+    // one new phi to merge the two exception values, and all other phis don't need
+    // to be changed.
+    for (BasicBlock catchSuccessor : catchSuccessors) {
+      catchSuccessor.splitCriticalExceptioEdges(
+          code.valueNumberGenerator,
+          newBlock -> {
+            newBlock.setNumber(code.blocks.size());
+            blockIterator.add(newBlock);
+          });
+    }
+  }
+
+  private boolean allPredecessorsHaveCatchEdges() {
+    for (BasicBlock predecessor : getPredecessors()) {
+      if (!predecessor.isCatchSuccessor(this)) {
+        assert false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Assumes that `this` block is a catch handler target (note that it does not have to
+   * start with MoveException instruction, since the instruction can be removed by
+   * optimizations like dead code remover.
+   *
+   * Introduces new blocks on all incoming edges and clones MoveException instruction to
+   * these blocks if it exists. All exception values introduced in newly created blocks
+   * are combined in a phi added to `this` block.
+   *
+   * Note that if there are any other phis defined on this block, they remain valid, since
+   * this method does not affect incoming edges in any way, and just adds new blocks with
+   * MoveException and Goto.
+   *
+   * NOTE: onNewBlock must assign block number to the newly created block.
+   */
+  public void splitCriticalExceptioEdges(
+      ValueNumberGenerator valueNumberGenerator, Consumer<BasicBlock> onNewBlock) {
+    assert allPredecessorsHaveCatchEdges();
+
+    List<BasicBlock> predecessors = this.getPredecessors();
+    boolean hasMoveException = entry().isMoveException();
+    MoveException move = null;
+    if (hasMoveException) {
+      // Remove the move-exception instruction.
+      move = entry().asMoveException();
+      assert move.getPreviousLocalValue() == null;
+      this.getInstructions().remove(0);
+    }
+    // Create new predecessor blocks.
+    List<BasicBlock> newPredecessors = new ArrayList<>();
+    List<Value> values = new ArrayList<>(predecessors.size());
+    for (BasicBlock predecessor : predecessors) {
+      BasicBlock newBlock = new BasicBlock();
+      newPredecessors.add(newBlock);
+      if (hasMoveException) {
+        Value value = new Value(
+            valueNumberGenerator.next(), -1, MoveType.OBJECT, move.getDebugInfo());
+        values.add(value);
+        newBlock.add(new MoveException(value));
+      }
+      newBlock.add(new Goto());
+      newBlock.close(null);
+      newBlock.getSuccessors().add(this);
+      newBlock.getPredecessors().add(predecessor);
+      predecessor.replaceSuccessor(this, newBlock);
+      onNewBlock.accept(newBlock);
+      assert newBlock.getNumber() >= 0 : "Number must be assigned by `onNewBlock`";
+    }
+    // Replace the blocks predecessors with the new ones.
+    predecessors.clear();
+    predecessors.addAll(newPredecessors);
+    // Insert a phi for the move-exception value.
+    if (hasMoveException) {
+      Phi phi = new Phi(valueNumberGenerator.next(),
+          -1, this, MoveType.OBJECT, move.getLocalInfo());
+      phi.addOperands(values);
+      move.outValue().replaceUsers(phi);
+    }
+  }
+
+  /** Append catch handlers from another block <code>fromBlock</code> (which must have catch
+   * handlers) to the catch handlers of this block.
+   *
+   * Note that after appending catch handlers their targets are referenced by both
+   * <code>fromBlock</code> and <code>this</code> block, but no phis are inserted. For this reason
+   * this method should only be called from either {@link #moveCatchHandlers} or
+   * {@link #copyCatchHandlers} which know how to handle phis.
+   *
+   * @returns the catch successors that are reused in both blocks after appending.
+   */
+  private List<BasicBlock> appendCatchHandlers(BasicBlock fromBlock) {
+    assert fromBlock.hasCatchHandlers();
+
+    List<Integer> prevCatchTargets = fromBlock.catchHandlers.getAllTargets();
+    List<DexType> prevCatchGuards = fromBlock.catchHandlers.getGuards();
+    List<BasicBlock> catchSuccessors = new ArrayList<>();
+    List<DexType> newCatchGuards = new ArrayList<>();
+    List<Integer> newCatchTargets = new ArrayList<>();
+
+    // First add existing catch handlers to the catch handler list.
+    if (hasCatchHandlers()) {
+      newCatchGuards.addAll(catchHandlers.getGuards());
+      newCatchTargets.addAll(catchHandlers.getAllTargets());
+      for (int newCatchTarget : newCatchTargets) {
+        BasicBlock catchSuccessor = successors.get(newCatchTarget);
+        if (!catchSuccessors.contains(catchSuccessor)) {
+          catchSuccessors.add(catchSuccessor);
+        }
+        int index = catchSuccessors.indexOf(catchSuccessor);
+        assert index == newCatchTarget;
+      }
+    }
+
+    // This is the number of catch handlers which are already successors of this block.
+    int formerCatchHandlersCount = catchSuccessors.size();
+
+    // Then add catch handlers from the other block to the catch handler list.
+    for (int i = 0; i < prevCatchTargets.size(); i++) {
+      int prevCatchTarget = prevCatchTargets.get(i);
+      DexType prevCatchGuard = prevCatchGuards.get(i);
+      // TODO(sgjesse): Check sub-types of guards. Will require AppInfoWithSubtyping.
+      BasicBlock catchSuccessor = fromBlock.successors.get(prevCatchTarget);
+      // We assume that all the catch handlers targets has only one
+      // predecessor and, thus, no phis.
+      assert catchSuccessor.getPredecessors().size() == 1;
+      assert catchSuccessor.getPhis().isEmpty();
+
+      int index = catchSuccessors.indexOf(catchSuccessor);
+      if (index == -1) {
+        catchSuccessors.add(catchSuccessor);
+        index = catchSuccessors.size() - 1;
+      }
+      newCatchGuards.add(prevCatchGuard);
+      newCatchTargets.add(index);
+    }
+
+    // Create the new successors list and link things up.
+    List<BasicBlock> formerSuccessors = new ArrayList<>(successors);
+    successors.clear();
+    List<BasicBlock> sharedCatchSuccessors = new ArrayList<>();
+    for (int i = 0; i < catchSuccessors.size(); i++) {
+      if (i < formerCatchHandlersCount) {
+        // Former catch successors are just copied, as they are already linked.
+        assert catchSuccessors.get(i).getPredecessors().contains(this);
+        successors.add(catchSuccessors.get(i));
+      } else {
+        // New catch successors are linked properly.
+        assert !catchSuccessors.get(i).getPredecessors().contains(this);
+        link(catchSuccessors.get(i));
+        sharedCatchSuccessors.add(catchSuccessors.get(i));
+      }
+    }
+    catchHandlers = new CatchHandlers<>(newCatchGuards, newCatchTargets);
+
+    // Finally add the normal successor if any.
+    int catchSuccessorsCount = successors.size();
+    for (BasicBlock formerSuccessor : formerSuccessors) {
+      if (!successors.contains(formerSuccessor)) {
+        assert !exit().isThrow();
+        successors.add(formerSuccessor);
+      }
+    }
+    assert successors.size() == catchSuccessorsCount || !exit().isThrow();
+
+    return sharedCatchSuccessors;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
new file mode 100644
index 0000000..2ab2a4f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -0,0 +1,397 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+public class BasicBlockInstructionIterator implements InstructionIterator, InstructionListIterator {
+
+  protected final BasicBlock block;
+  protected final ListIterator<Instruction> listIterator;
+  protected Instruction current;
+
+  protected BasicBlockInstructionIterator(BasicBlock block) {
+    this.block = block;
+    this.listIterator = block.getInstructions().listIterator();
+  }
+
+  protected BasicBlockInstructionIterator(BasicBlock block, int index) {
+    this.block = block;
+    this.listIterator = block.getInstructions().listIterator(index);
+  }
+
+  @Override
+  public boolean hasNext() {
+    return listIterator.hasNext();
+  }
+
+  @Override
+  public Instruction next() {
+    current = listIterator.next();
+    return current;
+  }
+
+  @Override
+  public int nextIndex() {
+    return listIterator.nextIndex();
+  }
+
+  @Override
+  public boolean hasPrevious() {
+    return listIterator.hasPrevious();
+  }
+
+  @Override
+  public Instruction previous() {
+    current = listIterator.previous();
+    return current;
+  }
+
+  @Override
+  public int previousIndex() {
+    return listIterator.previousIndex();
+  }
+
+  /**
+   * Adds an instruction to the block. The instruction will be added just before the current
+   * cursor position.
+   *
+   * The instruction will be assigned to the block it is added to.
+   *
+   * @param instruction The instruction to add.
+   */
+  @Override
+  public void add(Instruction instruction) {
+    instruction.setBlock(block);
+    assert instruction.getBlock() == block;
+    listIterator.add(instruction);
+  }
+
+  /**
+   * Replaces the last instruction returned by {@link #next} or {@link #previous} with the
+   * specified instruction.
+   *
+   * The instruction will be assigned to the block it is added to.
+   *
+   * @param instruction The instruction to replace with.
+   */
+  @Override
+  public void set(Instruction instruction) {
+    instruction.setBlock(block);
+    assert instruction.getBlock() == block;
+    listIterator.set(instruction);
+  }
+
+  /**
+   * Remove the current instruction (aka the {@link Instruction} returned by the previous call to
+   * {@link #next}.
+   *
+   * The current instruction will be completely detached from the instruction stream with uses
+   * of its in-values removed.
+   *
+   * If the current instruction produces an out-value this out value must not have any users.
+   */
+  @Override
+  public void remove() {
+    if (current == null) {
+      throw new IllegalStateException();
+    }
+    assert current.outValue() == null || current.outValue().numberOfAllUsers() == 0;
+    for (int i = 0; i < current.inValues().size(); i++) {
+      Value value = current.inValues().get(i);
+      value.removeUser(current);
+    }
+    Value previousLocalValue = current.getPreviousLocalValue();
+    if (previousLocalValue != null) {
+      previousLocalValue.removeDebugUser(current);
+    }
+    listIterator.remove();
+    current = null;
+  }
+
+  @Override
+  public void detach() {
+    if (current == null) {
+      throw new IllegalStateException();
+    }
+    listIterator.remove();
+    current = null;
+  }
+
+  @Override
+  public void replaceCurrentInstruction(Instruction newInstruction) {
+    if (current == null) {
+      throw new IllegalStateException();
+    }
+    for (Value value : current.inValues()) {
+      value.removeUser(current);
+    }
+    if (current.outValue() != null) {
+      assert newInstruction.outValue() != null;
+      current.outValue().replaceUsers(newInstruction.outValue());
+    }
+    newInstruction.setBlock(block);
+    listIterator.remove();
+    listIterator.add(newInstruction);
+    current.clearBlock();
+  }
+
+  private BasicBlock peekPrevious(ListIterator<BasicBlock> blocksIterator) {
+    BasicBlock block = blocksIterator.previous();
+    blocksIterator.next();
+    return block;
+  }
+
+  public BasicBlock split(IRCode code, ListIterator<BasicBlock> blocksIterator) {
+    List<BasicBlock> blocks = code.blocks;
+    assert blocksIterator == null || peekPrevious(blocksIterator) == block;
+
+    int blockNumber = blocks.size();
+    BasicBlock newBlock;
+
+    // Don't allow splitting after the last instruction.
+    assert hasNext();
+
+    // Prepare the new block, placing the exception handlers on the block with the throwing
+    // instruction.
+    boolean keepCatchHandlers = hasPrevious() && peekPrevious().instructionTypeCanThrow();
+    newBlock = block.createSplitBlock(blockNumber, keepCatchHandlers);
+
+    // Add a goto instruction.
+    Goto newGoto = new Goto(block);
+    listIterator.add(newGoto);
+
+    // Move all remaining instructions to the new block.
+    while (listIterator.hasNext()) {
+      Instruction instruction = listIterator.next();
+      newBlock.getInstructions().addLast(instruction);
+      instruction.setBlock(newBlock);
+      listIterator.remove();
+    }
+
+    // If splitting the normal exit block, the new block is now the normal exit block.
+    if (code.getNormalExitBlock() == block) {
+      code.setNormalExitBlock(newBlock);
+    }
+
+    // Insert the new block in the block list right after the current block.
+    if (blocksIterator == null) {
+      blocks.add(blocks.indexOf(block) + 1, newBlock);
+    } else {
+      blocksIterator.add(newBlock);
+    }
+
+    return newBlock;
+  }
+
+  public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blocksIterator) {
+    // Split at the current cursor position.
+    BasicBlock newBlock = split(code, blocksIterator);
+    assert blocksIterator == null || peekPrevious(blocksIterator) == newBlock;
+    // Skip the requested number of instructions and split again.
+    InstructionListIterator iterator = newBlock.listIterator();
+    for (int i = 0; i < instructions; i++) {
+      iterator.next();
+    }
+    iterator.split(code, blocksIterator);
+    // Return the first split block.
+    return newBlock;
+  }
+
+  private boolean canThrow(IRCode code) {
+    Iterator<Instruction> iterator = code.instructionIterator();
+    while (iterator.hasNext()) {
+      boolean throwing = iterator.next().instructionTypeCanThrow();
+      if (throwing) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void splitBlockAndCopyCatchHandlers(IRCode code, BasicBlock invokeBlock,
+      BasicBlock inlinedBlock, ListIterator<BasicBlock> blocksIterator) {
+    // Iterate through the instructions in the inlined block and split into blocks with only
+    // one throwing instruction in each block.
+    // NOTE: This iterator is replaced in the loop below, so that the iteration continues in
+    // the new block after the iterated block is split.
+    InstructionListIterator instructionsIterator = inlinedBlock.listIterator();
+    BasicBlock currentBlock = inlinedBlock;
+    while (currentBlock != null && instructionsIterator.hasNext()) {
+      assert !currentBlock.hasCatchHandlers();
+      Instruction throwingInstruction =
+          instructionsIterator.nextUntil(Instruction::instructionTypeCanThrow);
+      BasicBlock nextBlock;
+      if (throwingInstruction != null) {
+        // If a throwing instruction was found split the block.
+        if (instructionsIterator.hasNext()) {
+          // TODO(sgjesse): No need to split if this is the last non-debug, non-jump
+          // instruction in the block.
+          nextBlock = instructionsIterator.split(code, blocksIterator);
+          assert nextBlock.getPredecessors().size() == 1;
+          assert currentBlock == nextBlock.getPredecessors().get(0);
+          // Back up to before the split before inserting catch handlers.
+          BasicBlock b = blocksIterator.previous();
+          assert b == nextBlock;
+        } else {
+          nextBlock = null;
+        }
+        currentBlock.copyCatchHandlers(code, blocksIterator, invokeBlock);
+        if (nextBlock != null) {
+          BasicBlock b = blocksIterator.next();
+          assert b == nextBlock;
+          // Switch iteration to the split block.
+          instructionsIterator = nextBlock.listIterator();
+        } else {
+          instructionsIterator = null;
+        }
+        currentBlock = nextBlock;
+      } else {
+        assert !instructionsIterator.hasNext();
+        instructionsIterator = null;
+        currentBlock = null;
+      }
+    }
+  }
+
+  private void appendCatchHandlers(IRCode code, BasicBlock invokeBlock,
+      IRCode inlinee, ListIterator<BasicBlock> blocksIterator) {
+    BasicBlock inlineeBlock = null;
+    // Move back through the inlinee blocks added (they are now in the basic blocks list).
+    for (int i = 0; i < inlinee.blocks.size(); i++) {
+      inlineeBlock = blocksIterator.previous();
+    }
+    assert inlineeBlock == inlinee.blocks.getFirst();
+    // Position right after the empty invoke block.
+    inlineeBlock = blocksIterator.next();
+    assert inlineeBlock == inlinee.blocks.getFirst();
+
+    // Iterate through the inlined blocks (they are now in the basic blocks list).
+    Iterator<BasicBlock> inlinedBlocksIterator = inlinee.blocks.iterator();
+    while (inlinedBlocksIterator.hasNext()) {
+      BasicBlock inlinedBlock = inlinedBlocksIterator.next();
+      assert inlineeBlock == inlinedBlock;  // Iterators must be in sync.
+      if (inlinedBlock.hasCatchHandlers()) {
+        // The block already has catch handlers, so it has only one throwing instruction, and no
+        // splitting is required.
+        inlinedBlock.copyCatchHandlers(code, blocksIterator, invokeBlock);
+      } else {
+        // The block does not have catch handlers, so it can have several throwing instructions.
+        // Therefore the block must be split after each throwing instruction, and the catch
+        // handlers must be added to each of these blocks.
+        splitBlockAndCopyCatchHandlers(code, invokeBlock, inlinedBlock, blocksIterator);
+      }
+      // Iterate to the next inlined block (if more inlined blocks).
+      inlineeBlock = blocksIterator.next();
+    }
+  }
+
+  private void removeArgumentInstructions(IRCode inlinee) {
+    int index = 0;
+    InstructionListIterator inlineeIterator = inlinee.blocks.getFirst().listIterator();
+    List<Value> arguments = inlinee.collectArguments();
+    while (inlineeIterator.hasNext()) {
+      Instruction instruction = inlineeIterator.next();
+      if (instruction.isArgument()) {
+        assert instruction.outValue().numberOfAllUsers() == 0;
+        assert instruction.outValue() == arguments.get(index++);
+        inlineeIterator.remove();
+      }
+    }
+  }
+
+  public BasicBlock inlineInvoke(
+      IRCode code, IRCode inlinee, ListIterator<BasicBlock> blocksIterator,
+      List<BasicBlock> blocksToRemove) {
+    assert blocksToRemove != null;
+    boolean inlineeCanThrow = canThrow(inlinee);
+    BasicBlock invokeBlock = split(1, code, blocksIterator);
+    assert invokeBlock.getInstructions().size() == 2;
+    assert invokeBlock.getInstructions().getFirst().isInvoke();
+
+    // Split the invoke instruction into a separate block.
+    Invoke invoke = invokeBlock.getInstructions().getFirst().asInvoke();
+    BasicBlock invokePredecessor = invokeBlock.getPredecessors().get(0);
+    BasicBlock invokeSuccessor = invokeBlock.getSuccessors().get(0);
+
+    // Map all argument values, and remove the arguments instructions in the inlinee.
+    List<Value> arguments = inlinee.collectArguments();
+    assert invoke.inValues().size() == arguments.size();
+    for (int i = 0; i < invoke.inValues().size(); i++) {
+      arguments.get(i).replaceUsers(invoke.inValues().get(i));
+    }
+    removeArgumentInstructions(inlinee);
+
+    // The inline entry is the first block now the argument instructions are gone.
+    BasicBlock inlineEntry = inlinee.blocks.getFirst();
+
+    BasicBlock inlineExit = null;
+    if (inlinee.getNormalExitBlock() == null) {
+      assert inlineeCanThrow;
+      // TODO(sgjesse): Remove this restriction.
+      assert !invokeBlock.hasCatchHandlers();
+      blocksToRemove.addAll(
+          invokePredecessor.unlink(invokeBlock, new DominatorTree(code, blocksToRemove)));
+    } else {
+      // Locate inlinee return.
+      InstructionListIterator inlineeIterator = inlinee.getNormalExitBlock().listIterator();
+      inlineeIterator.nextUntil(Instruction::isReturn);
+      Return ret = inlineeIterator.previous().asReturn();
+
+      // Map return value if used.
+      if (invoke.outValue() != null) {
+        assert !ret.isReturnVoid();
+        invoke.outValue().replaceUsers(ret.returnValue());
+      }
+
+      // Split before return and unlink return.
+      BasicBlock returnBlock = inlineeIterator.split(inlinee);
+      inlineExit = returnBlock.unlinkSinglePredecessor();
+      InstructionListIterator returnBlockIterator = returnBlock.listIterator();
+      returnBlockIterator.next();
+      returnBlockIterator.remove();  // This clears out the users from the return.
+      assert !returnBlockIterator.hasNext();
+      inlinee.blocks.remove(returnBlock);
+
+      // Leaving the invoke block in the graph as an empty block. Still unlink its predecessor as
+      // the exit block of the inlinee will become its new predecessor.
+      invokeBlock.unlinkSinglePredecessor();
+      InstructionListIterator invokeBlockIterator = invokeBlock.listIterator();
+      invokeBlockIterator.next();
+      invokeBlockIterator.remove();
+      invokeSuccessor = invokeBlock;
+    }
+
+    // Link the inlinee into the graph.
+    invokePredecessor.link(inlineEntry);
+    if (inlineExit != null) {
+      inlineExit.link(invokeSuccessor);
+    }
+
+    // Position the block iterator cursor just after the invoke block.
+    if (blocksIterator == null) {
+      // If no block iterator was passed create one for the insertion of the inlinee blocks.
+      blocksIterator = code.blocks.listIterator(code.blocks.indexOf(invokeBlock));
+      blocksIterator.next();
+    } else {
+      // If a blocks iterator was passed, back up to the block with the invoke instruction and
+      // remove it.
+      blocksIterator.previous();
+      blocksIterator.previous();
+    }
+
+    // Insert inlinee blocks into the IR code.
+    inlinee.blocks.forEach(blocksIterator::add);
+
+    // If the invoke block had catch handlers copy those down to all inlined blocks.
+    if (invokeBlock.hasCatchHandlers()) {
+      appendCatchHandlers(code, invokeBlock, inlinee, blocksIterator);
+    }
+
+    return invokeSuccessor;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
new file mode 100644
index 0000000..d9ce088
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.Iterator;
+import java.util.ListIterator;
+
+public class BasicBlockIterator implements ListIterator<BasicBlock> {
+
+  protected final IRCode code;
+  protected final ListIterator<BasicBlock> listIterator;
+  protected BasicBlock current;
+
+  protected BasicBlockIterator(IRCode code) {
+    this.code = code;
+    this.listIterator = code.blocks.listIterator();
+  }
+
+  protected BasicBlockIterator(IRCode code, int index) {
+    this.code = code;
+    this.listIterator = code.blocks.listIterator(index);
+  }
+
+  @Override
+  public boolean hasNext() {
+    return listIterator.hasNext();
+  }
+
+  @Override
+  public BasicBlock next() {
+    current = listIterator.next();
+    return current;
+  }
+
+  @Override
+  public int nextIndex() {
+    return listIterator.nextIndex();
+  }
+
+  @Override
+  public boolean hasPrevious() {
+    return listIterator.hasPrevious();
+  }
+
+  @Override
+  public BasicBlock previous() {
+    current = listIterator.previous();
+    return current;
+  }
+
+  @Override
+  public int previousIndex() {
+    return listIterator.previousIndex();
+  }
+
+  @Override
+  public void add(BasicBlock block) {
+    listIterator.add(block);
+  }
+
+  @Override
+  public void set(BasicBlock block) {
+    listIterator.set(block);
+  }
+
+  /**
+   * Remove the last {@link BasicBlock} that was returned by {@link #next()} or {@link #previous()}.
+   * This call can only be made once per call to {@code next} or {@code previous}.
+   *
+   * All instructions in the block will be completely detached from the instruction stream. Each
+   * instruction will have all uses of its in-values removed. If any instructions in the block
+   * produces an out-value these out values must not have any users.
+   */
+  @Override
+  public void remove() {
+    if (current == null) {
+      throw new IllegalStateException();
+    }
+    // Remove all instructions from the block before removing the block.
+    Iterator<Instruction> iterator = current.iterator();
+    while (iterator.hasNext()) {
+      iterator.next();
+      iterator.remove();
+    }
+    listIterator.remove();
+    if (current == code.getNormalExitBlock()) {
+      code.setNormalExitBlock(null);
+    }
+    current = null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Binop.java b/src/main/java/com/android/tools/r8/ir/code/Binop.java
new file mode 100644
index 0000000..83240bb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Binop.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public abstract class Binop extends Instruction {
+
+  protected final NumericType type;
+
+  public Binop(NumericType type, Value dest, Value left, Value right) {
+    super(dest);
+    this.type = type;
+    addInValue(left);
+    addInValue(right);
+  }
+
+  public NumericType getNumericType() {
+    return type;
+  }
+
+  public Value leftValue() {
+    return inValues.get(0);
+  }
+
+  public Value rightValue() {
+    return inValues.get(1);
+  }
+
+  public abstract boolean isCommutative();
+
+  public boolean isTwoAddr(DexBuilder builder) {
+    if (rightValue().needsRegister() && leftValue().needsRegister()) {
+      int leftRegister = builder.allocatedRegister(leftValue(), getNumber());
+      int rightRegister = builder.allocatedRegister(rightValue(), getNumber());
+      int destRegister = builder.allocatedRegister(outValue, getNumber());
+      return ((leftRegister == destRegister) ||
+          (isCommutative() && rightRegister == destRegister)) &&
+          leftRegister <= U4BIT_MAX &&
+          rightRegister <= U4BIT_MAX;
+    }
+    return false;
+  }
+
+  boolean fitsInDexInstruction(Value value) {
+    return fitsInLit16Instruction(value);
+  }
+
+  boolean fitsInLit16Instruction(Value value) {
+    return type == NumericType.INT &&
+        value.isConstant() &&
+        value.getConstInstruction().asConstNumber().is16Bit();
+  }
+
+  boolean fitsInLit8Instruction(Value value) {
+    return type == NumericType.INT &&
+        value.isConstant() &&
+        value.getConstInstruction().asConstNumber().is8Bit();
+  }
+
+  // The in and out register sizes are the same depending on the size of the literal
+  // involved (is any).
+  int maxInOutValueRegisterSize() {
+    if (fitsInDexInstruction(rightValue())) {
+      return rightValue().getConstInstruction().asConstNumber().is8Bit() ? U8BIT_MAX : U4BIT_MAX;
+    }
+    return U8BIT_MAX;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return maxInOutValueRegisterSize();
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return maxInOutValueRegisterSize();
+  }
+
+  int foldIntegers(int left, int right) {
+    throw new Unreachable("Unsupported integer folding for " + this);
+  }
+
+  long foldLongs(long left, long right) {
+    throw new Unreachable("Unsupported long folding for " + this);
+  }
+
+  float foldFloat(float left, float right) {
+    throw new Unreachable("Unsupported float folding for " + this);
+  }
+
+  double foldDouble(double left, double right) {
+    throw new Unreachable("Unsupported float folding for " + this);
+  }
+
+  @Override
+  public boolean isBinop() {
+    return true;
+  }
+
+  @Override
+  public Binop asBinop() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
new file mode 100644
index 0000000..d67c359
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+
+public class CatchHandlers<T> {
+
+  private final List<DexType> guards;
+  private final List<T> targets;
+  private Set<T> uniqueTargets;
+
+  public static final CatchHandlers<Integer> EMPTY_INDICES = new CatchHandlers<>();
+  public static final CatchHandlers<BasicBlock> EMPTY_BASIC_BLOCK = new CatchHandlers<>();
+
+  private CatchHandlers() {
+    guards = ImmutableList.of();
+    targets = ImmutableList.of();
+  }
+
+  public CatchHandlers(List<DexType> guards, List<T> targets) {
+    assert !guards.isEmpty();
+    assert guards.size() == targets.size();
+    // Guava ImmutableList does not support null elements.
+    this.guards = ImmutableList.copyOf(guards);
+    this.targets = ImmutableList.copyOf(targets);
+  }
+
+  public boolean isEmpty() {
+    return size() == 0;
+  }
+
+  public int size() {
+    assert guards.size() == targets.size();
+    return guards.size();
+  }
+
+  public List<DexType> getGuards() {
+    return guards;
+  }
+
+  public List<T> getAllTargets() {
+    return targets;
+  }
+
+  public Set<T> getUniqueTargets() {
+    if (uniqueTargets == null) {
+      uniqueTargets = ImmutableSet.copyOf(targets);
+    }
+    return uniqueTargets;
+  }
+
+  public boolean hasCatchAll() {
+    return getGuards().size() > 0 &&
+        getGuards().get(getGuards().size() - 1) == DexItemFactory.catchAllType;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof CatchHandlers)) {
+      return false;
+    }
+    CatchHandlers that = (CatchHandlers) o;
+    return guards.equals(that.guards) && targets.equals(that.targets);
+  }
+
+  @Override
+  public int hashCode() {
+    return 31 * guards.hashCode() + targets.hashCode();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
new file mode 100644
index 0000000..7963357
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.MoveObject;
+import com.android.tools.r8.code.MoveObjectFrom16;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class CheckCast extends Instruction {
+
+  private final DexType type;
+
+  // A CheckCast dex instruction takes only one register containing a value and changes
+  // the associated type information for that value. In the IR we let the CheckCast
+  // instruction define a new value. During register allocation we then need to arrange it
+  // so that the source and destination are assigned the same register.
+  public CheckCast(Value dest, Value value, DexType type) {
+    super(dest, value);
+    if (value.isNeverNull()) {
+      dest.markNeverNull();
+    }
+    this.type = type;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    // The check cast instruction in dex doesn't write a new register. Therefore,
+    // if the register allocator could not put input and output in the same register
+    // we have to insert a move before the check cast instruction.
+    int inRegister = builder.allocatedRegister(inValues.get(0), getNumber());
+    int outRegister = builder.allocatedRegister(outValue, getNumber());
+    if (inRegister == outRegister) {
+      builder.add(this, new com.android.tools.r8.code.CheckCast(outRegister, type));
+    } else {
+      com.android.tools.r8.code.CheckCast cast = new com.android.tools.r8.code.CheckCast(outRegister, type);
+      if (outRegister <= Constants.U4BIT_MAX && inRegister <= Constants.U4BIT_MAX) {
+        builder.add(this, new com.android.tools.r8.code.Instruction[]{
+            new MoveObject(outRegister, inRegister), cast});
+      } else {
+        builder.add(this, new com.android.tools.r8.code.Instruction[]{
+            new MoveObjectFrom16(outRegister, inRegister), cast});
+      }
+    }
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asCheckCast().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.slowCompareTo(other.asCheckCast().type);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean isCheckCast() {
+    return true;
+  }
+
+  @Override
+  public CheckCast asCheckCast() {
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; " + type;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
new file mode 100644
index 0000000..c5cc82f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -0,0 +1,196 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.CmpLong;
+import com.android.tools.r8.code.CmpgDouble;
+import com.android.tools.r8.code.CmpgFloat;
+import com.android.tools.r8.code.CmplDouble;
+import com.android.tools.r8.code.CmplFloat;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.LongInterval;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+
+public class Cmp extends Binop {
+
+  public enum Bias {
+    NONE, GT, LT
+  }
+
+  private final Bias bias;
+
+  public Cmp(NumericType type, Bias bias, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+    this.bias = bias;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int dest = builder.allocatedRegister(outValue, getNumber());
+    int left = builder.allocatedRegister(leftValue(), getNumber());
+    int right = builder.allocatedRegister(rightValue(), getNumber());
+    switch (type) {
+      case DOUBLE:
+        assert bias != Bias.NONE;
+        if (bias == Bias.GT) {
+          instruction = new CmpgDouble(dest, left, right);
+        } else {
+          assert bias == Bias.LT;
+          instruction = new CmplDouble(dest, left, right);
+        }
+        break;
+      case FLOAT:
+        assert bias != Bias.NONE;
+        if (bias == Bias.GT) {
+          instruction = new CmpgFloat(dest, left, right);
+        } else {
+          assert bias == Bias.LT;
+          instruction = new CmplFloat(dest, left, right);
+        }
+        break;
+      case LONG:
+        assert bias == Bias.NONE;
+        instruction = new CmpLong(dest, left, right);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  private String biasToString(Bias bias) {
+    switch (bias) {
+      case NONE:
+        return "none";
+      case GT:
+        return "gt";
+      case LT:
+        return "lt";
+      default:
+        throw new Unreachable("Unexpected bias " + bias);
+    }
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append(getClass().getSimpleName());
+    builder.append(" (");
+    switch (type) {
+      case DOUBLE:
+        builder.append("double, ");
+        builder.append(biasToString(bias));
+        break;
+      case FLOAT:
+        builder.append("float, ");
+        builder.append(biasToString(bias));
+        break;
+      case LONG:
+        builder.append("long");
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.append(")");
+    for (int i = builder.length(); i < 20; i++) {
+      builder.append(" ");
+    }
+    if (outValue != null) {
+      builder.append(outValue);
+      builder.append(" <- ");
+    }
+    StringUtils.append(builder, inValues, ", ", BraceType.NONE);
+    return builder.toString();
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asCmp().bias == bias;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return bias.ordinal() - other.asCmp().bias.ordinal();
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  private boolean nonOverlapingRanges() {
+    return type == NumericType.LONG
+        && leftValue().hasValueRange()
+        && rightValue().hasValueRange()
+        && leftValue().getValueRange().doesntOverlapWith(rightValue().getValueRange());
+  }
+
+  @Override
+  public boolean canBeFolded() {
+    return (leftValue().isConstant() && rightValue().isConstant()) || nonOverlapingRanges();
+  }
+
+  @Override
+  public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+    assert canBeFolded();
+    int result;
+    if (type == NumericType.LONG) {
+      if (leftValue().isConstant() && rightValue().isConstant()) {
+        long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
+        long right = rightValue().getConstInstruction().asConstNumber().getLongValue();
+        result = Integer.signum(Long.compare(left, right));
+      } else {
+        assert nonOverlapingRanges();
+        LongInterval leftRange = leftValue().getValueRange();
+        LongInterval rightRange = rightValue().getValueRange();
+        result = Integer.signum(Long.compare(leftRange.getMin(), rightRange.getMin()));
+      }
+    } else if (type == NumericType.FLOAT) {
+      float left = leftValue().getConstInstruction().asConstNumber().getFloatValue();
+      float right = rightValue().getConstInstruction().asConstNumber().getFloatValue();
+      if (Float.isNaN(left) || Float.isNaN(right)) {
+        result = bias == Bias.GT ? 1 : -1;
+      } else {
+        result = (int) Math.signum(left - right);
+      }
+    } else {
+      assert type == NumericType.DOUBLE;
+      double left = leftValue().getConstInstruction().asConstNumber().getDoubleValue();
+      double right = rightValue().getConstInstruction().asConstNumber().getDoubleValue();
+      if (Double.isNaN(left) || Double.isNaN(right)) {
+        result = bias == Bias.GT ? 1 : -1;
+      } else {
+        result = (int) Math.signum(left - right);
+      }
+    }
+    assert result == -1 || result == 0 || result == 1;
+    Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+    return new ConstNumber(ConstType.INT, value, result);
+  }
+
+  @Override
+  public boolean isCmp() {
+    return true;
+  }
+
+  @Override
+  public Cmp asCmp() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
new file mode 100644
index 0000000..9b565a6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstClass extends ConstInstruction {
+
+  private final DexType clazz;
+
+  public ConstClass(Value dest, DexType clazz) {
+    super(dest);
+    this.clazz = clazz;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public DexType getValue() {
+    return clazz;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.ConstClass(dest, clazz));
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "ConstClass has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asConstClass().clazz == clazz;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return clazz.slowCompareTo(other.asConstClass().clazz);
+  }
+
+  @Override
+  public boolean isConstClass() {
+    return true;
+  }
+
+  @Override
+  public ConstClass asConstClass() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
new file mode 100644
index 0000000..657faf7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstInstruction.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+public abstract class ConstInstruction extends Instruction {
+
+  public ConstInstruction(Value out) {
+    super(out);
+  }
+
+  @Override
+  public ConstInstruction getOutConstantConstInstruction() {
+    return this;
+  }
+
+  @Override
+  public boolean isConstInstruction() {
+    return true;
+  }
+
+  @Override
+  public ConstInstruction asConstInstruction() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
new file mode 100644
index 0000000..8aff14a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -0,0 +1,195 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Const;
+import com.android.tools.r8.code.Const16;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstHigh16;
+import com.android.tools.r8.code.ConstWide;
+import com.android.tools.r8.code.ConstWide16;
+import com.android.tools.r8.code.ConstWide32;
+import com.android.tools.r8.code.ConstWideHigh16;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.utils.NumberUtils;
+
+public class ConstNumber extends ConstInstruction {
+
+  public final ConstType type;
+  private final long value;
+
+  public ConstNumber(ConstType type, Value dest, long value) {
+    super(dest);
+    // We create const numbers after register allocation for rematerialization of values. Those
+    // are all for fixed register values. All other values that are used as the destination for
+    // const number instructions should be marked as constants.
+    assert dest.isFixedRegisterValue() || dest.isConstant();
+    assert type != ConstType.OBJECT;
+    this.type = type;
+    this.value = value;
+  }
+
+  public static ConstNumber copyOf(IRCode code, ConstNumber original) {
+    Value newValue =
+        new Value(
+            code.valueNumberGenerator.next(),
+            original.outValue().getOriginalRegister(),
+            original.outType(),
+            original.getDebugInfo());
+    return new ConstNumber(original.type, newValue, original.getRawValue());
+  }
+
+  private boolean preciseTypeUnknown() {
+    return type == ConstType.INT_OR_FLOAT || type == ConstType.LONG_OR_DOUBLE;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public int getIntValue() {
+    assert type == ConstType.INT || type == ConstType.INT_OR_FLOAT;
+    return (int) value;
+  }
+
+  public long getLongValue() {
+    assert type == ConstType.LONG || type == ConstType.LONG_OR_DOUBLE;
+    return value;
+  }
+
+  public float getFloatValue() {
+    assert type == ConstType.FLOAT || type == ConstType.INT_OR_FLOAT;
+    return Float.intBitsToFloat((int) value);
+  }
+
+  public double getDoubleValue() {
+    assert type == ConstType.DOUBLE || type == ConstType.LONG_OR_DOUBLE;
+    return Double.longBitsToDouble(value);
+  }
+
+  public long getRawValue() {
+    return value;
+  }
+
+  public boolean isZero() {
+    return value == 0;
+  }
+
+  public boolean isIntegerNegativeOne(NumericType type) {
+    assert type == NumericType.INT || type == NumericType.LONG;
+    if (type == NumericType.INT) {
+      return getIntValue() == -1;
+    }
+    return getLongValue() == -1;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    if (!dest().needsRegister()) {
+      builder.addNop(this);
+      return;
+    }
+
+    int register = builder.allocatedRegister(dest(), getNumber());
+    if (MoveType.fromConstType(type) == MoveType.SINGLE) {
+      assert NumberUtils.is32Bit(value);
+      if ((register & 0xf) == register && NumberUtils.is4Bit(value)) {
+        builder.add(this, new Const4(register, (int) value));
+      } else if (NumberUtils.is16Bit(value)) {
+        builder.add(this, new Const16(register, (int) value));
+      } else if ((value & 0x0000ffffL) == 0) {
+        builder.add(this, new ConstHigh16(register, ((int) value) >>> 16));
+      } else {
+        builder.add(this, new Const(register, (int) value));
+      }
+    } else {
+      assert MoveType.fromConstType(type) == MoveType.WIDE;
+      if (NumberUtils.is16Bit(value)) {
+        builder.add(this, new ConstWide16(register, (int) value));
+      } else if ((value & 0x0000ffffffffffffL) == 0) {
+        builder.add(this, new ConstWideHigh16(register, (int) (value >>> 48)));
+      } else if (NumberUtils.is32Bit(value)) {
+        builder.add(this, new ConstWide32(register, (int) value));
+      } else {
+        builder.add(this, new ConstWide(register, value));
+      }
+    }
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "Const has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " " + value + " (" + type + ")";
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (preciseTypeUnknown()) {
+      return false;
+    }
+    ConstNumber o = other.asConstNumber();
+    return o.type == type && o.value == value;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    ConstNumber o = other.asConstNumber();
+    int result;
+    result = type.ordinal() - o.type.ordinal();
+    if (result != 0) {
+      return result;
+    }
+    return Long.signum(value - o.value);
+  }
+
+  public boolean is8Bit() {
+    return NumberUtils.is8Bit(value);
+  }
+
+  public boolean negativeIs8Bit() {
+    return NumberUtils.negativeIs8Bit(value);
+  }
+
+  public boolean is16Bit() {
+    return NumberUtils.is16Bit(value);
+  }
+
+  public boolean negativeIs16Bit() {
+    return NumberUtils.negativeIs16Bit(value);
+  }
+
+  @Override
+  public boolean isOutConstant() {
+    return true;
+  }
+
+  @Override
+  public boolean isConstNumber() {
+    return true;
+  }
+
+  @Override
+  public ConstNumber asConstNumber() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
new file mode 100644
index 0000000..82db97a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.ConstStringJumbo;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class ConstString extends ConstInstruction {
+
+  private final DexString value;
+
+  public ConstString(Value dest, DexString value) {
+    super(dest);
+    dest.markNeverNull();
+    this.value = value;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public DexString getValue() {
+    return value;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.registerStringReference(value);
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    if (builder.isJumboString(value)) {
+      builder.add(this, new ConstStringJumbo(dest, value));
+    } else {
+      builder.add(this, new com.android.tools.r8.code.ConstString(dest, value));
+    }
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asConstString().value == value;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return value.slowCompareTo(other.asConstString().value);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "ConstString has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " \"" + value + "\"";
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean isConstString() {
+    return true;
+  }
+
+  @Override
+  public ConstString asConstString() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstType.java b/src/main/java/com/android/tools/r8/ir/code/ConstType.java
new file mode 100644
index 0000000..c6f9d73
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstType.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum ConstType {
+  INT,
+  LONG,
+  FLOAT,
+  DOUBLE,
+  OBJECT,
+  INT_OR_FLOAT,
+  LONG_OR_DOUBLE;
+
+  public static ConstType fromNumericType(NumericType type) {
+    switch (type) {
+      case BYTE:
+      case CHAR:
+      case SHORT:
+      case INT:
+        return INT;
+      case LONG:
+        return LONG;
+      case FLOAT:
+        return FLOAT;
+      case DOUBLE:
+        return DOUBLE;
+      default:
+        throw new Unreachable("Invalid numeric type '" + type + "'");
+    }
+  }
+
+  public static ConstType fromMoveType(MoveType moveType) {
+    switch (moveType) {
+      case SINGLE:
+        return INT_OR_FLOAT;
+      case WIDE:
+        return LONG_OR_DOUBLE;
+      case OBJECT:
+        // Currently constants never have type OBJECT even when it is the null object.
+        return INT;
+      default:
+        throw new Unreachable("Invalid move type '" + moveType + "'");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
new file mode 100644
index 0000000..ee8a279
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+
+/**
+ * Instruction reading an SSA value with attached local information.
+ *
+ * This instruction ensures that a value with a local is kept alive until at least this read.
+ *
+ * This instruction must not be considered dead until live-ranges have been computed for locals
+ * after which it will be removed.
+ */
+public class DebugLocalRead extends Instruction {
+
+  public DebugLocalRead(Value src) {
+    super(null, src);
+    assert src.getLocalInfo() != null;
+  }
+
+  public Value src() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public boolean isDebugLocalRead() {
+    return true;
+  }
+
+  @Override
+  public DebugLocalRead asDebugLocalRead() {
+    return this;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isDebugLocalRead();
+    return true;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isDebugLocalRead();
+    return 0;
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    return false;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U16BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    throw new Unreachable();
+  }
+
+  public void addDebugLocalStart() {
+    src().addDebugLocalStart(this);
+  }
+
+  public void addDebugLocalEnd() {
+    src().addDebugLocalEnd(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
new file mode 100644
index 0000000..beaf508
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalUninitialized.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+/**
+ * Instruction representing the SSA value for an uninitialized local.
+ *
+ * This instruction allows the local information to not necessarily be properly block structured.
+ * For example, Javac will emit local-end labels in blocks that are not dominated by the
+ * introduction of the same local, causing our SSA graph to attempt to reference a local value that
+ * does not exist in all predecessors (eg, for a local declared in an unscoped switch-case).
+ * By emitting this "uninitialized" value in the method prelude, such a reference will instead
+ * become a phi of the uninitialized value and the local.
+ *
+ * This instruction may (and almost always will) be considered dead code and removed.
+ */
+public class DebugLocalUninitialized extends ConstNumber {
+
+  public DebugLocalUninitialized(ConstType type, Value value) {
+    super(type == ConstType.OBJECT ? ConstType.INT : type, value, 0);
+  }
+
+  @Override
+  public boolean isDebugLocalUninitialized() {
+    return true;
+  }
+
+  @Override
+  public DebugLocalUninitialized asDebugLocalUninitialized() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
new file mode 100644
index 0000000..442fb6d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+/**
+ * Instruction introducing an SSA value with attached local information.
+ *
+ * All instructions may have attached local information (defined as the local information of their
+ * outgoing value). This instruction is needed to mark a transition of an existing value (with a
+ * possible local attached) to a new value that has a local (possibly the same one). If all ingoing
+ * values end up having the same local this can be safely removed.
+ *
+ * For valid debug info, this instruction should have at least one user, namely a DebugLocalRead
+ * denoting the end of its range, and thus it should be live.
+ */
+public class DebugLocalWrite extends Move {
+
+  public DebugLocalWrite(Value dest, Value src) {
+    super(dest, src);
+    assert dest.getLocalInfo() != null;
+    assert dest.getLocalInfo() != src.getLocalInfo() || src.isPhi();
+  }
+
+  @Override
+  public boolean isDebugLocalWrite() {
+    return true;
+  }
+
+  @Override
+  public DebugLocalWrite asDebugLocalWrite() {
+    return this;
+  }
+
+  @Override
+  public boolean isOutConstant() {
+    return false;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isDebugLocalWrite();
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
new file mode 100644
index 0000000..4e078a5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableMap;
+
+public class DebugPosition extends Instruction {
+
+  public final int line;
+  public final DexString file;
+  private ImmutableMap<Integer, DebugLocalInfo> locals;
+
+  public DebugPosition(int line, DexString file) {
+    super(null);
+    this.line = line;
+    this.file = file;
+  }
+
+  @Override
+  public boolean isDebugPosition() {
+    return true;
+  }
+
+  @Override
+  public DebugPosition asDebugPosition() {
+    return this;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.addNop(this);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isDebugPosition();
+    return false;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isDebugPosition();
+    return 0;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder(super.toString());
+    if (file != null) {
+      builder.append(file).append(":");
+    }
+    builder.append(line);
+    if (locals != null && !locals.isEmpty()) {
+      builder.append(", locals: ");
+      StringUtils.append(builder, locals.values());
+    }
+    return builder.toString();
+  }
+
+  public void setLocals(ImmutableMap<Integer, DebugLocalInfo> locals) {
+    this.locals = locals;
+  }
+
+  public ImmutableMap<Integer, DebugLocalInfo> getLocals() {
+    return locals;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
new file mode 100644
index 0000000..c8c1c32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.DivDouble;
+import com.android.tools.r8.code.DivDouble2Addr;
+import com.android.tools.r8.code.DivFloat;
+import com.android.tools.r8.code.DivFloat2Addr;
+import com.android.tools.r8.code.DivInt;
+import com.android.tools.r8.code.DivInt2Addr;
+import com.android.tools.r8.code.DivIntLit16;
+import com.android.tools.r8.code.DivIntLit8;
+import com.android.tools.r8.code.DivLong;
+import com.android.tools.r8.code.DivLong2Addr;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Div extends ArithmeticBinop {
+
+  public Div(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isDiv() {
+    return true;
+  }
+
+  @Override
+  public Div asDiv() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new DivInt(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    return new DivLong(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+    return new DivFloat(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+    return new DivDouble(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new DivInt2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new DivLong2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+    return new DivFloat2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+    return new DivDouble2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new DivIntLit8(dest, left, constant);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new DivIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asDiv().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asDiv().type.ordinal();
+  }
+
+  @Override
+  public boolean canBeFolded() {
+    return super.canBeFolded() && !rightValue().getConstInstruction().asConstNumber().isZero();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left / right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left / right;
+  }
+
+  @Override
+  float foldFloat(float left, float right) {
+    return left / right;
+  }
+
+  @Override
+  double foldDouble(double left, double right) {
+    return left / right;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return type != NumericType.DOUBLE && type != NumericType.FLOAT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
new file mode 100644
index 0000000..e814dbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -0,0 +1,175 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class DominatorTree {
+
+  IRCode code;
+  private BasicBlock[] sorted;
+  private BasicBlock[] doms;
+
+  public DominatorTree(IRCode code) {
+    this(code, Collections.emptyList());
+  }
+
+  // TODO(sgjesse) Get rid of this constructor and blocksToIgnore.
+  DominatorTree(IRCode code, List<BasicBlock> blocksToIgnore) {
+    this.code = code;
+    this.sorted = code.topologicallySortedBlocks(blocksToIgnore);
+    numberBlocks();
+    build();
+  }
+
+  /**
+   * Get the immediate dominator block for a block.
+   */
+  public BasicBlock immediateDominator(BasicBlock block) {
+    return doms[block.getNumber()];
+  }
+
+  /**
+   * Check if one basic block is dominated by another basic block.
+   *
+   * @param subject subject to check for domination by {@code dominator}
+   * @param dominator dominator to check against
+   * @return wether {@code subject} is dominated by {@code dominator}
+   */
+  public boolean dominatedBy(BasicBlock subject, BasicBlock dominator) {
+    if (subject == dominator) {
+      return true;
+    }
+    return strictlyDominatedBy(subject, dominator);
+  }
+
+  /**
+   * Check if one basic block is strictly dominated by another basic block.
+   *
+   * @param subject subject to check for domination by {@code dominator}
+   * @param dominator dominator to check against
+   * @return wether {@code subject} is strictly dominated by {@code dominator}
+   */
+  public boolean strictlyDominatedBy(BasicBlock subject, BasicBlock dominator) {
+    if (subject.getNumber() == 0) {
+      return false;
+    }
+    while (true) {
+      BasicBlock idom = immediateDominator(subject);
+      if (idom.getNumber() < dominator.getNumber()) {
+        return false;
+      }
+      if (idom == dominator) {
+        return true;
+      }
+      subject = idom;
+    }
+  }
+
+  /**
+   * Use the dominator tree to find the dominating block that is closest to a set of blocks.
+   *
+   * @param blocks the block for which to find a dominator
+   * @return the closest dominator for the collection of blocks
+   */
+  public BasicBlock closestDominator(Collection<BasicBlock> blocks) {
+    if (blocks.size() == 0) {
+      return null;
+    }
+    Iterator<BasicBlock> it = blocks.iterator();
+    BasicBlock dominator = it.next();
+    while (it.hasNext()) {
+      dominator = intersect(dominator, it.next());
+    }
+    return dominator;
+  }
+
+  public BasicBlock[] getSortedBlocks() {
+    return sorted;
+  }
+
+  private void numberBlocks() {
+    for (int i = 0; i < sorted.length; i++) {
+      sorted[i].setNumber(i);
+    }
+  }
+
+  private boolean postorderCompareLess(BasicBlock b1, BasicBlock b2) {
+    // The topological sort is reverse postorder.
+    return b1.getNumber() > b2.getNumber();
+  }
+
+  // Build dominator tree based on the algorithm described in this paper:
+  //
+  // A Simple, Fast Dominance Algorithm
+  // Cooper, Keith D.; Harvey, Timothy J.; and Kennedy, Ken (2001).
+  // http://www.cs.rice.edu/~keith/EMBED/dom.pdf
+  private void build() {
+    doms = new BasicBlock[sorted.length];
+    doms[0] = sorted[0];
+    boolean changed = true;
+    while (changed) {
+      changed = false;
+      // Run through all nodes in reverse postorder (except start node).
+      for (int i = 1; i < sorted.length; i++) {
+        BasicBlock b = sorted[i];
+        // Pick one processed predecessor.
+        BasicBlock newIDom = null;
+        int picked = -1;
+        for (int j = 0; newIDom == null && j < b.getPredecessors().size(); j++) {
+          BasicBlock p = b.getPredecessors().get(j);
+          if (doms[p.getNumber()] != null) {
+            picked = j;
+            newIDom = p;
+          }
+        }
+        // Run through all other predecessors.
+        for (int j = 0; j < b.getPredecessors().size(); j++) {
+          BasicBlock p = b.getPredecessors().get(j);
+          if (j == picked) {
+            continue;
+          }
+          if (doms[p.getNumber()] != null) {
+            newIDom = intersect(p, newIDom);
+          }
+        }
+        if (doms[b.getNumber()] != newIDom) {
+          doms[b.getNumber()] = newIDom;
+          changed = true;
+        }
+      }
+    }
+  }
+
+  private BasicBlock intersect(BasicBlock b1, BasicBlock b2) {
+    BasicBlock finger1 = b1;
+    BasicBlock finger2 = b2;
+    while (finger1 != finger2) {
+      while (postorderCompareLess(finger1, finger2)) {
+        finger1 = doms[finger1.getNumber()];
+      }
+      while (postorderCompareLess(finger2, finger1)) {
+        finger2 = doms[finger2.getNumber()];
+      }
+    }
+    return finger1;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("Dominators\n");
+    for (BasicBlock block : sorted) {
+      builder.append(block.getNumber());
+      builder.append(": ");
+      builder.append(doms[block.getNumber()].getNumber());
+      builder.append("\n");
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
new file mode 100644
index 0000000..e93f3a5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import java.util.List;
+
+abstract class FieldInstruction extends Instruction {
+
+  protected final MemberType type;
+  protected final DexField field;
+
+  protected FieldInstruction(MemberType type, DexField field, Value dest, Value object) {
+    super(dest, object);
+    assert type != null;
+    assert field != null;
+    this.type = type;
+    this.field = field;
+  }
+
+  protected FieldInstruction(MemberType type, DexField field, Value dest, List<Value> values) {
+    super(dest, values);
+    assert type != null;
+    assert field != null;
+    this.type = type;
+    this.field = field;
+  }
+
+  public MemberType getType() {
+    return type;
+  }
+
+  public DexField getField() {
+    return field;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    // Resolve the field if possible and decide whether the instruction can inlined.
+    DexType fieldHolder = field.getHolder();
+    DexEncodedField target = info.lookupInstanceTarget(fieldHolder, field);
+    DexClass fieldClass = info.definitionFor(fieldHolder);
+    if ((target != null) && (fieldClass != null) && !fieldClass.isLibraryClass()) {
+      if (isInstanceGet() || isInstancePut()) {
+        return Inliner.InliningConstraint.PRIVATE;
+      }
+      DexAccessFlags flags = target.accessFlags;
+      if (flags.isPublic()) {
+        return Inliner.InliningConstraint.ALWAYS;
+      }
+      if (flags.isPrivate() && (fieldHolder == holder)) {
+        return Inliner.InliningConstraint.PRIVATE;
+      }
+      if (flags.isProtected() && (fieldHolder.isSamePackage(holder))) {
+        return Inliner.InliningConstraint.PACKAGE;
+      }
+    }
+    return Inliner.InliningConstraint.NEVER;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FixedRegisterValue.java b/src/main/java/com/android/tools/r8/ir/code/FixedRegisterValue.java
new file mode 100644
index 0000000..c63d323
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/FixedRegisterValue.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+// Value that has a fixed register allocated. These are used for inserting spill, restore, and phi
+// moves in the spilling register allocator.
+public class FixedRegisterValue extends Value {
+  private final int register;
+
+  public FixedRegisterValue(MoveType type, int register) {
+    // Set local info to null since these values are never representatives of live-ranges.
+    super(-1, -1, type, null);
+    setNeedsRegister(true);
+    this.register = register;
+  }
+
+  public int getRegister() {
+    return register;
+  }
+
+  @Override
+  public boolean isFixedRegisterValue() {
+    return true;
+  }
+
+  @Override
+  public FixedRegisterValue asFixedRegisterValue() {
+    return this;
+  }
+
+  @Override
+  public boolean isConstant() {
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    return "r" + register;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
new file mode 100644
index 0000000..5ca9199
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.List;
+
+public class Goto extends JumpInstruction {
+
+  public Goto() {
+    super(null);
+  }
+
+  public Goto(BasicBlock block) {
+    super(null);
+    setBlock(block);
+  }
+
+  public BasicBlock getTarget() {
+    assert getBlock().exit() == this;
+    List<BasicBlock> successors = getBlock().getSuccessors();
+    assert successors.size() >= 1;
+    return successors.get(successors.size() - 1);
+  }
+
+  public void setTarget(BasicBlock nextBlock) {
+    assert getBlock().exit() == this;
+    List<BasicBlock> successors = getBlock().getSuccessors();
+    assert successors.size() >= 1;
+    BasicBlock target = successors.get(successors.size() - 1);
+    target.getPredecessors().remove(getBlock());
+    successors.set(successors.size() - 1, nextBlock);
+    nextBlock.getPredecessors().add(getBlock());
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.addGoto(this);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "Goto has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "Goto defines no values.";
+    return 0;
+  }
+
+  @Override
+  public String toString() {
+    if (getBlock() != null && !getBlock().getSuccessors().isEmpty()) {
+      return super.toString() + "block " + getTarget().getNumber();
+    }
+    return super.toString() + "block <unknown>";
+  }
+
+  public void print(CfgPrinter printer) {
+    super.print(printer);
+    printer.append(" B").append(getTarget().getNumber());
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asGoto().getTarget() == getTarget();
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isGoto();
+    assert false : "Not supported";
+    return 0;
+  }
+
+  @Override
+  public boolean isGoto() {
+    return true;
+  }
+
+  @Override
+  public Goto asGoto() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
new file mode 100644
index 0000000..94eeb55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -0,0 +1,350 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+public class IRCode {
+
+  public final DexEncodedMethod method;
+
+  public LinkedList<BasicBlock> blocks;
+  public final ValueNumberGenerator valueNumberGenerator;
+
+  private BasicBlock normalExitBlock;
+  private boolean numbered = false;
+  private int nextInstructionNumber = 0;
+
+  public IRCode(
+      DexEncodedMethod method,
+      LinkedList<BasicBlock> blocks,
+      BasicBlock normalExitBlock,
+      ValueNumberGenerator valueNumberGenerator) {
+    this.method = method;
+    this.blocks = blocks;
+    this.normalExitBlock = normalExitBlock;
+    this.valueNumberGenerator = valueNumberGenerator;
+  }
+
+  private void ensureBlockNumbering() {
+    if (!numbered) {
+      numbered = true;
+      BasicBlock[] sorted = topologicallySortedBlocks();
+      for (int i = 0; i < sorted.length; i++) {
+        sorted[i].setNumber(i);
+      }
+    }
+  }
+
+  @Override
+  public String toString() {
+    ensureBlockNumbering();
+    StringBuilder builder = new StringBuilder();
+    builder.append("blocks:\n");
+    for (BasicBlock block : blocks) {
+      builder.append(block.toDetailedString());
+      builder.append("\n");
+    }
+    return builder.toString();
+  }
+
+  public void clearMarks() {
+    for (BasicBlock block : blocks) {
+      block.clearMark();
+    }
+  }
+
+  public void removeMarkedBlocks() {
+    ListIterator<BasicBlock> blockIterator = listIterator();
+    while (blockIterator.hasNext()) {
+      BasicBlock block = blockIterator.next();
+      if (block.isMarked()) {
+        blockIterator.remove();
+        if (block == normalExitBlock) {
+          normalExitBlock = null;
+        }
+      }
+    }
+  }
+
+  public void removeBlocks(List<BasicBlock> blocksToRemove) {
+    blocks.removeAll(blocksToRemove);
+    if (blocksToRemove.contains(normalExitBlock)) {
+      normalExitBlock = null;
+    }
+  }
+
+  /**
+   * Compute quasi topologically sorted list of the basic blocks using depth first search.
+   *
+   * TODO(ager): We probably want to compute strongly connected components and topologically
+   * sort strongly connected components instead. However, this is much better than having
+   * no sorting.
+   */
+  public BasicBlock[] topologicallySortedBlocks() {
+    return topologicallySortedBlocks(Collections.emptyList());
+  }
+
+  public BasicBlock[] topologicallySortedBlocks(List<BasicBlock> blocksToIgnore) {
+    clearMarks();
+    int reachableBlocks = blocks.size() - blocksToIgnore.size();
+    BasicBlock[] sorted = new BasicBlock[reachableBlocks];
+    BasicBlock entryBlock = blocks.getFirst();
+    int index = depthFirstSorting(entryBlock, sorted, reachableBlocks - 1);
+    assert index == -1;
+    return sorted;
+  }
+
+  private int depthFirstSorting(BasicBlock block, BasicBlock[] sorted, int index) {
+    if (!block.isMarked()) {
+      block.mark();
+      for (BasicBlock succ : block.getSuccessors()) {
+        index = depthFirstSorting(succ, sorted, index);
+      }
+      assert sorted[index] == null;
+      sorted[index] = block;
+      return index - 1;
+    }
+    return index;
+  }
+
+  public void print(CfgPrinter printer) {
+    ensureBlockNumbering();
+    for (BasicBlock block : blocks) {
+      block.print(printer);
+    }
+  }
+
+  public boolean isConsistentSSA() {
+    assert isConsistentGraph() && consistentDefUseChains() && validThrowingInstructions();
+    return true;
+  }
+
+  public boolean isConsistentGraph() {
+    assert consistentPredecessorSuccessors();
+    assert consistentCatchHandlers();
+    assert normalExitBlock == null || normalExitBlock.exit().isReturn();
+    return true;
+  }
+
+  private boolean consistentDefUseChains() {
+    Set<Value> values = new HashSet<>();
+
+    for (BasicBlock block : blocks) {
+      int predecessorCount = block.getPredecessors().size();
+      // Check that all phi uses are consistent.
+      for (Phi phi : block.getPhis()) {
+        assert phi.getOperands().size() == predecessorCount;
+        values.add(phi);
+        for (Value value : phi.getOperands()) {
+          values.add(value);
+          if (value.isPhi()) {
+            Phi phiOperand = value.asPhi();
+            assert phiOperand.getBlock().getPhis().contains(phiOperand);
+            assert phiOperand.uniquePhiUsers().contains(phi);
+          } else {
+            Instruction definition = value.definition;
+            assert definition.outValue() == value;
+          }
+        }
+      }
+      for (Instruction instruction : block.getInstructions()) {
+        assert instruction.getBlock() == block;
+        Value outValue = instruction.outValue();
+        if (outValue != null) {
+          values.add(outValue);
+          assert outValue.definition == instruction;
+          Value previousLocalValue = outValue.getPreviousLocalValue();
+          if (previousLocalValue != null) {
+            values.add(previousLocalValue);
+            assert previousLocalValue.debugUsers().contains(instruction);
+          }
+        }
+        for (Value value : instruction.inValues()) {
+          values.add(value);
+          assert value.uniqueUsers().contains(instruction);
+          if (value.isPhi()) {
+            Phi phi = value.asPhi();
+            assert phi.getBlock().getPhis().contains(phi);
+          } else {
+            Instruction definition = value.definition;
+            assert definition.outValue() == value;
+          }
+        }
+      }
+    }
+
+    for (Value value : values) {
+      assert consistentValueUses(value);
+    }
+
+    return true;
+  }
+
+  private boolean consistentValueUses(Value value) {
+    for (Instruction user : value.uniqueUsers()) {
+      assert user.inValues().contains(value);
+    }
+    for (Phi phiUser : value.uniquePhiUsers()) {
+      assert phiUser.getOperands().contains(value);
+      assert phiUser.getBlock().getPhis().contains(phiUser);
+    }
+    if (value.debugUsers() != null) {
+      for (Instruction debugUser : value.debugUsers()) {
+        assert debugUser.getPreviousLocalValue() == value;
+      }
+    }
+    return true;
+  }
+
+  private boolean consistentPredecessorSuccessors() {
+    for (BasicBlock block : blocks) {
+      // Check that all successors are distinct.
+      assert new HashSet<>(block.getSuccessors()).size() == block.getSuccessors().size();
+      for (BasicBlock succ : block.getSuccessors()) {
+        // Check that successors are in the block list.
+        assert blocks.contains(succ);
+        // Check that successors have this block as a predecessor.
+        assert succ.getPredecessors().contains(block);
+      }
+      // Check that all predecessors are distinct.
+      assert new HashSet<>(block.getPredecessors()).size() == block.getPredecessors().size();
+      for (BasicBlock pred : block.getPredecessors()) {
+        // Check that predecessors are in the block list.
+        assert blocks.contains(pred);
+        // Check that predecessors have this block as a successor.
+        assert pred.getSuccessors().contains(block);
+      }
+    }
+    return true;
+  }
+
+  private boolean consistentCatchHandlers() {
+    for (BasicBlock block : blocks) {
+      // Check that catch handlers are always the first successors of a block.
+      if (block.hasCatchHandlers()) {
+        assert block.exit().isGoto() || block.exit().isThrow();
+        CatchHandlers<Integer> catchHandlers = block.getCatchHandlersWithSuccessorIndexes();
+        // If there is a catch-all guard it must be the last.
+        List<DexType> guards = catchHandlers.getGuards();
+        int lastGuardIndex = guards.size() - 1;
+        for (int i = 0; i < guards.size(); i++) {
+          assert guards.get(i) != DexItemFactory.catchAllType || i == lastGuardIndex;
+        }
+        // Check that all successors except maybe the last are catch successors.
+        List<Integer> sortedHandlerIndices = new ArrayList<>(catchHandlers.getAllTargets());
+        sortedHandlerIndices.sort(Comparator.naturalOrder());
+        int firstIndex = sortedHandlerIndices.get(0);
+        int lastIndex = sortedHandlerIndices.get(sortedHandlerIndices.size() - 1);
+        assert firstIndex == 0;
+        assert lastIndex < sortedHandlerIndices.size();
+        int lastSuccessorIndex = block.getSuccessors().size() - 1;
+        assert lastIndex == lastSuccessorIndex  // All successors are catch successors.
+            || lastIndex == lastSuccessorIndex - 1; // All but one successors are catch successors.
+        assert lastIndex == lastSuccessorIndex || !block.exit().isThrow();
+      }
+    }
+    return true;
+  }
+
+  private boolean validThrowingInstructions() {
+    for (BasicBlock block : blocks) {
+      if (block.hasCatchHandlers()) {
+        boolean seenThrowing = false;
+        for (Instruction instruction : block.getInstructions()) {
+          if (instruction.instructionTypeCanThrow()) {
+            assert !seenThrowing;
+            seenThrowing = true;
+            continue;
+          }
+          // After the throwing instruction only debug instructions an the final jump
+          // instruction is allowed.
+          // TODO(ager): For now allow const instructions due to the way consts are pushed
+          // towards their use
+          if (seenThrowing) {
+            assert instruction.isDebugInstruction()
+                || instruction.isJumpInstruction()
+                || instruction.isConstInstruction()
+                || instruction.isNewArrayFilledData();
+          }
+        }
+      }
+    }
+    return true;
+  }
+
+  public InstructionIterator instructionIterator() {
+    return new IRCodeInstructionsIterator(this);
+  }
+
+  void setNormalExitBlock(BasicBlock block) {
+    normalExitBlock = block;
+  }
+
+  public BasicBlock getNormalExitBlock() {
+    return normalExitBlock;
+  }
+
+  public ListIterator<BasicBlock> listIterator() {
+    return new BasicBlockIterator(this);
+  }
+
+  public ListIterator<BasicBlock> listIterator(int index) {
+    return new BasicBlockIterator(this, index);
+  }
+
+  public BasicBlock[] numberInstructions() {
+    BasicBlock[] blocks = topologicallySortedBlocks();
+    for (BasicBlock block : blocks) {
+      for (Instruction instruction : block.getInstructions()) {
+        instruction.setNumber(nextInstructionNumber);
+        nextInstructionNumber += LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA;
+      }
+    }
+    return blocks;
+  }
+
+  public int numberRemainingInstructions() {
+    InstructionIterator it = instructionIterator();
+    while (it.hasNext()) {
+      Instruction i = it.next();
+      if (i.getNumber() == -1) {
+        i.setNumber(nextInstructionNumber);
+        nextInstructionNumber += LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA;
+      }
+    }
+    return nextInstructionNumber;
+  }
+
+  public int getNextInstructionNumber() {
+    return nextInstructionNumber;
+  }
+
+  public List<Value> collectArguments() {
+    final List<Value> arguments = new ArrayList<>();
+    Iterator<Instruction> iterator = blocks.get(0).iterator();
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (instruction.isArgument()) {
+        arguments.add(instruction.asArgument().outValue());
+      }
+    }
+    assert arguments.size()
+        == method.method.proto.parameters.values.length + (method.accessFlags.isStatic() ? 0 : 1);
+    return arguments;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
new file mode 100644
index 0000000..760a7ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+public class IRCodeInstructionsIterator implements InstructionIterator {
+
+  private ListIterator<BasicBlock> blockIterator;
+  private InstructionListIterator instructionIterator;
+
+  public IRCodeInstructionsIterator(IRCode code) {
+    blockIterator = code.blocks.listIterator();
+    instructionIterator = blockIterator.next().listIterator();
+  }
+
+  @Override
+  public boolean hasNext() {
+    if (instructionIterator.hasNext()) {
+      return true;
+    }
+    return blockIterator.hasNext();
+  }
+
+  @Override
+  public Instruction next() {
+    if (instructionIterator.hasNext()) {
+      return instructionIterator.next();
+    }
+    if (!blockIterator.hasNext()) {
+      throw new NoSuchElementException();
+    }
+    instructionIterator = blockIterator.next().listIterator();
+    assert instructionIterator.hasNext();
+    return instructionIterator.next();
+  }
+
+  public void add(Instruction instruction) {
+    instructionIterator.add(instruction);
+  }
+
+  @Override
+  public void remove() {
+    instructionIterator.remove();
+  }
+
+  @Override
+  public void replaceCurrentInstruction(Instruction newInstruction) {
+    instructionIterator.replaceCurrentInstruction(newInstruction);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
new file mode 100644
index 0000000..f621b8c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -0,0 +1,167 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.List;
+
+public class If extends JumpInstruction {
+
+  public enum Type {
+    EQ, GE, GT, LE, LT, NE
+  }
+
+  private Type type;
+
+  public If(Type type, Value value) {
+    super(null, value);
+    this.type = type;
+  }
+
+  public If(Type type, List<Value> values) {
+    super(null, values);
+    this.type = type;
+  }
+
+  public boolean isZeroTest() {
+    return inValues.size() == 1;
+  }
+
+  public Type getType() {
+    return type;
+  }
+
+  public void invert() {
+    BasicBlock tmp = getTrueTarget();
+    setTrueTarget(fallthroughBlock());
+    setFallthroughBlock(tmp);
+    switch (type) {
+      case EQ:
+        type = Type.NE;
+        break;
+      case GE:
+        type = Type.LT;
+        break;
+      case GT:
+        type = Type.LE;
+        break;
+      case LE:
+        type = Type.GT;
+        break;
+      case LT:
+        type = Type.GE;
+        break;
+      case NE:
+        type = Type.EQ;
+        break;
+      default:
+        throw new Unreachable("Unknown if condition type.");
+    }
+  }
+
+  public BasicBlock getTrueTarget() {
+    assert getBlock().exit() == this;
+    List<BasicBlock> successors = getBlock().getSuccessors();
+    assert successors.size() >= 2;
+    return successors.get(successors.size() - 2);
+  }
+
+  public void setTrueTarget(BasicBlock block) {
+    assert getBlock().exit() == this;
+    List<BasicBlock> successors = getBlock().getSuccessors();
+    assert successors.size() >= 2;
+    successors.set(successors.size() - 2, block);
+  }
+
+  @Override
+  public BasicBlock fallthroughBlock() {
+    assert getBlock().exit() == this;
+    List<BasicBlock> successors = getBlock().getSuccessors();
+    assert successors.size() >= 2;
+    return successors.get(successors.size() - 1);
+  }
+
+  @Override
+  public void setFallthroughBlock(BasicBlock block) {
+    List<BasicBlock> successors = getBlock().getSuccessors();
+    successors.set(successors.size() - 1, block);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.addIf(this);
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " " + type + " block " + getTrueTarget().getNumber()
+        + " (fallthrough " + fallthroughBlock().getNumber() + ")";
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return isZeroTest() ? U8BIT_MAX : U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "If instructions define no values.";
+    return 0;
+  }
+
+  @Override
+  public void print(CfgPrinter printer) {
+    super.print(printer);
+    printer.append(" B").append(getTrueTarget().getNumber());
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    If o = other.asIf();
+    return o.getTrueTarget() == getTrueTarget()
+        && o.fallthroughBlock() == fallthroughBlock()
+        && o.type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isIf();
+    assert false : "Not supported";
+    return 0;
+  }
+
+
+  public BasicBlock targetFromCondition(int cond) {
+    switch (type) {
+      case EQ:
+        return cond == 0 ? getTrueTarget() : fallthroughBlock();
+      case NE:
+        return cond != 0 ? getTrueTarget() : fallthroughBlock();
+      case GE:
+        return cond >= 0 ? getTrueTarget() : fallthroughBlock();
+      case GT:
+        return cond > 0 ? getTrueTarget() : fallthroughBlock();
+      case LE:
+        return cond <= 0 ? getTrueTarget() : fallthroughBlock();
+      case LT:
+        return cond < 0 ? getTrueTarget() : fallthroughBlock();
+    }
+    throw new Unreachable("Unexpected condition type " + type);
+  }
+
+  @Override
+  public boolean isIf() {
+    return true;
+  }
+
+  @Override
+  public If asIf() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
new file mode 100644
index 0000000..9e5c972
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Iget;
+import com.android.tools.r8.code.IgetBoolean;
+import com.android.tools.r8.code.IgetByte;
+import com.android.tools.r8.code.IgetChar;
+import com.android.tools.r8.code.IgetObject;
+import com.android.tools.r8.code.IgetShort;
+import com.android.tools.r8.code.IgetWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.Arrays;
+
+public class InstanceGet extends FieldInstruction {
+
+  public InstanceGet(MemberType type, Value dest, Value object, DexField field) {
+    super(type, field, dest, object);
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value object() {
+    assert inValues.size() == 1;
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int destRegister = builder.allocatedRegister(dest(), getNumber());
+    int objectRegister = builder.allocatedRegister(object(), getNumber());
+    com.android.tools.r8.code.Instruction instruction;
+    switch (type) {
+      case SINGLE:
+        instruction = new Iget(destRegister, objectRegister, field);
+        break;
+      case WIDE:
+        instruction = new IgetWide(destRegister, objectRegister, field);
+        break;
+      case OBJECT:
+        instruction = new IgetObject(destRegister, objectRegister, field);
+        break;
+      case BOOLEAN:
+        instruction = new IgetBoolean(destRegister, objectRegister, field);
+        break;
+      case BYTE:
+        instruction = new IgetByte(destRegister, objectRegister, field);
+        break;
+      case CHAR:
+        instruction = new IgetChar(destRegister, objectRegister, field);
+        break;
+      case SHORT:
+        instruction = new IgetShort(destRegister, objectRegister, field);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    InstanceGet o = other.asInstanceGet();
+    return o.field == field && o.type == type;
+  }
+
+  public int compareNonValueParts(Instruction other) {
+    InstanceGet o = other.asInstanceGet();
+    int result = field.slowCompareTo(o.field);
+    if (result != 0) {
+      return result;
+    }
+    return type.ordinal() - o.type.ordinal();
+  }
+
+  @Override
+  public boolean isInstanceGet() {
+    return true;
+  }
+
+  @Override
+  public InstanceGet asInstanceGet() {
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; field: " + field.toSourceString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
new file mode 100644
index 0000000..3fe498d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class InstanceOf extends Instruction {
+
+  private final DexType type;
+
+  public InstanceOf(Value dest, Value value, DexType type) {
+    super(dest, value);
+    this.type = type;
+  }
+
+  public DexType type() {
+    return type;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value value() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    int value = builder.allocatedRegister(value(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.InstanceOf(dest, value, type));
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asInstanceOf().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.slowCompareTo(other.asInstanceOf().type);
+  }
+
+  @Override
+  public boolean isInstanceOf() {
+    return true;
+  }
+
+  @Override
+  public InstanceOf asInstanceOf() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    DexClass targetClass = info.definitionFor(type());
+    if (targetClass == null) {
+      return InliningConstraint.NEVER;
+    }
+    if (targetClass.accessFlags.isPublic()) {
+      return InliningConstraint.ALWAYS;
+    }
+    return InliningConstraint.NEVER;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
new file mode 100644
index 0000000..7ec8eaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Iput;
+import com.android.tools.r8.code.IputBoolean;
+import com.android.tools.r8.code.IputByte;
+import com.android.tools.r8.code.IputChar;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.IputShort;
+import com.android.tools.r8.code.IputWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public class InstancePut extends FieldInstruction {
+
+  public InstancePut(MemberType type, List<Value> values, DexField field) {
+    super(type, field, null, values);
+  }
+
+  public Value value() {
+    return inValues.get(0);
+  }
+
+  public Value object() {
+    return inValues.get(1);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int valueRegister = builder.allocatedRegister(value(), getNumber());
+    int objectRegister = builder.allocatedRegister(object(), getNumber());
+    switch (type) {
+      case SINGLE:
+        instruction = new Iput(valueRegister, objectRegister, field);
+        break;
+      case WIDE:
+        instruction = new IputWide(valueRegister, objectRegister, field);
+        break;
+      case OBJECT:
+        instruction = new IputObject(valueRegister, objectRegister, field);
+        break;
+      case BOOLEAN:
+        instruction = new IputBoolean(valueRegister, objectRegister, field);
+        break;
+      case BYTE:
+        instruction = new IputByte(valueRegister, objectRegister, field);
+        break;
+      case CHAR:
+        instruction = new IputChar(valueRegister, objectRegister, field);
+        break;
+      case SHORT:
+        instruction = new IputShort(valueRegister, objectRegister, field);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    InstancePut o = other.asInstancePut();
+    return o.field == field && o.type == type;
+  }
+
+  public int compareNonValueParts(Instruction other) {
+    InstancePut o = other.asInstancePut();
+    int result = field.slowCompareTo(o.field);
+    if (result != 0) {
+      return result;
+    }
+    return type.ordinal() - o.type.ordinal();
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "InstancePut instructions define no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean isInstancePut() {
+    return true;
+  }
+
+  @Override
+  public InstancePut asInstancePut() {
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; field: " + field.toSourceString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
new file mode 100644
index 0000000..d880c72
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -0,0 +1,795 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Value.DebugInfo;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class Instruction {
+
+  protected Value outValue = null;
+  protected final List<Value> inValues = new ArrayList<>();
+  private BasicBlock block = null;
+  private int number = -1;
+
+  protected Instruction(Value outValue) {
+    setOutValue(outValue);
+  }
+
+  protected Instruction(Value outValue, Value inValue) {
+    addInValue(inValue);
+    setOutValue(outValue);
+  }
+
+  protected Instruction(Value outValue, List<? extends Value> inValues) {
+    if (inValues != null) {
+      for (Value v : inValues) {
+        addInValue(v);
+      }
+    }
+    setOutValue(outValue);
+  }
+
+  public List<Value> inValues() {
+    return inValues;
+  }
+
+  protected void addInValue(Value value) {
+    if (value != null) {
+      inValues.add(value);
+      value.addUser(this);
+    }
+  }
+
+  public Value outValue() {
+    return outValue;
+  }
+
+  public void setOutValue(Value value) {
+    assert outValue == null || !outValue.hasUsersInfo() || outValue.numberOfAllUsers() == 0;
+    outValue = value;
+    if (outValue != null) {
+      outValue.definition = this;
+      Value previousLocalValue = getPreviousLocalValue();
+      if (previousLocalValue != null) {
+        previousLocalValue.addDebugUser(this);
+      }
+    }
+  }
+
+  public static void clearUserInfo(Instruction instruction) {
+    if (instruction.outValue != null) {
+      instruction.outValue.clearUsersInfo();
+    }
+    instruction.inValues.forEach(Value::clearUsersInfo);
+  }
+
+  public final MoveType outType() {
+    return outValue.outType();
+  }
+
+  public abstract void buildDex(DexBuilder builder);
+
+  public void replacePhi(Phi phi, Value value) {
+    for (int i = 0; i < inValues.size(); i++) {
+      if (phi == inValues.get(i)) {
+        inValues.set(i, value);
+        value.addUser(this);
+      }
+    }
+  }
+
+  /**
+   * Returns the basic block containing this instruction.
+   */
+  public BasicBlock getBlock() {
+    assert block != null;
+    return block;
+  }
+
+  /**
+   * Set the basic block of this instruction. See IRBuilder.
+   */
+  public void setBlock(BasicBlock block) {
+    assert block != null;
+    this.block = block;
+  }
+
+  /**
+   * Clear the basic block of this instruction. Use when removing an instruction from a block.
+   */
+  public void clearBlock() {
+    assert block != null;
+    block = null;
+  }
+
+  public String getInstructionName() {
+    return getClass().getSimpleName();
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append(getInstructionName());
+    for (int i = builder.length(); i < 20; i++) {
+      builder.append(" ");
+    }
+    builder.append(" ");
+    if (outValue != null) {
+      builder.append(outValue);
+      builder.append(" <- ");
+    }
+    if (!inValues.isEmpty()) {
+      StringUtils.append(builder, inValues, ", ", BraceType.NONE);
+    }
+    return builder.toString();
+  }
+
+  public void print(CfgPrinter printer) {
+    int uses = 0;
+    String value;
+    if (outValue == null) {
+      value = printer.makeUnusedValue();
+    } else {
+      if (outValue.hasUsersInfo()) {
+        uses = outValue.uniqueUsers().size() + outValue.uniquePhiUsers().size();
+      }
+      value = "v" + outValue.getNumber();
+    }
+    printer
+        .print(0)           // bci
+        .sp().append(uses)  // use
+        .sp().append(value) // tid
+        .sp().append(getClass().getSimpleName());
+    for (Value in : inValues) {
+      printer.append(" v").append(in.getNumber());
+    }
+  }
+
+  public void printLIR(CfgPrinter printer) {
+    // TODO(ager): Improve the instruction printing. Use different name for values so that the
+    // HIR and LIR values are not confused in the c1 visualizer.
+    printer.print(number).sp().append(toString());
+  }
+
+  public int getNumber() {
+    return number;
+  }
+
+  public void setNumber(int number) {
+    assert number != -1;
+    this.number = number;
+  }
+
+  /**
+   * Compare equality of two class-equivalent instructions modulo their values.
+   *
+   * <p>It is a precondition to this method that this.getClass() == other.getClass().
+   */
+  public abstract boolean identicalNonValueParts(Instruction other);
+
+  public abstract int compareNonValueParts(Instruction other);
+
+  private boolean identicalAfterRegisterAllocation(
+      Value a, int aInstr, Value b, int bInstr, RegisterAllocator allocator) {
+    if (a.needsRegister() != b.needsRegister()) {
+      return false;
+    }
+    if (a.needsRegister()) {
+      if (allocator.getRegisterForValue(a, aInstr) != allocator.getRegisterForValue(b, bInstr)) {
+        return false;
+      }
+    } else {
+      ConstNumber aNum = a.getConstInstruction().asConstNumber();
+      ConstNumber bNum = b.getConstInstruction().asConstNumber();
+      if (!aNum.identicalNonValueParts(bNum)) {
+        return false;
+      }
+    }
+    if (a.outType() != b.outType()) {
+      return false;
+    }
+    return true;
+  }
+
+  public boolean identicalAfterRegisterAllocation(Instruction other, RegisterAllocator allocator) {
+    if (other.getClass() != getClass()) {
+      return false;
+    }
+    if (!identicalNonValueParts(other)) {
+      return false;
+    }
+    if (isInvokeDirect() && !asInvokeDirect().sameConstructorReceiverValue(other.asInvoke())) {
+      return false;
+    }
+    if (outValue != null) {
+      if (other.outValue == null) {
+        return false;
+      }
+      if (!identicalAfterRegisterAllocation(
+          outValue, getNumber(), other.outValue, other.getNumber(), allocator)) {
+        return false;
+      }
+    } else if (other.outValue != null) {
+      return false;
+    }
+    // Check that all input values have the same type and allocated registers.
+    if (inValues.size() != other.inValues.size()) {
+      return false;
+    }
+    for (int j = 0; j < inValues.size(); j++) {
+      Value in0 = inValues.get(j);
+      Value in1 = other.inValues.get(j);
+      if (!identicalAfterRegisterAllocation(in0, getNumber(), in1, other.getNumber(), allocator)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Returns true if this instruction may throw an exception.
+   */
+  public boolean instructionTypeCanThrow() {
+    return false;
+  }
+
+  public boolean instructionInstanceCanThrow() {
+    return instructionTypeCanThrow();
+  }
+
+  /** Returns true is this instruction can be treated as dead code if its outputs are not used. */
+  public boolean canBeDeadCode(InternalOptions options) {
+    return !instructionInstanceCanThrow();
+  }
+
+  /**
+   * Returns true if this instruction need this value in a register.
+   */
+  public boolean needsValueInRegister(Value value) {
+    return true;
+  }
+
+  /**
+   * Returns true if the out value of this instruction is a constant.
+   *
+   * @return whether the out value of this instruction is a constant.
+   */
+  public boolean isOutConstant() {
+    return false;
+  }
+
+  /**
+   * Returns the ConstInstruction defining the constant out value if the out value is constant.
+   *
+   * @return ConstInstruction or null.
+   */
+  public ConstInstruction getOutConstantConstInstruction() {
+    return null;
+  }
+
+  public abstract int maxInValueRegister();
+
+  public abstract int maxOutValueRegister();
+
+  public DebugInfo getDebugInfo() {
+    return outValue == null ? null : outValue.getDebugInfo();
+  }
+
+  public DebugLocalInfo getLocalInfo() {
+    return outValue == null ? null : outValue.getLocalInfo();
+  }
+
+  public Value getPreviousLocalValue() {
+    return outValue == null ? null : outValue.getPreviousLocalValue();
+  }
+
+  public void replacePreviousLocalValue(Value value) {
+    outValue.replacePreviousLocalValue(value);
+  }
+
+  public boolean isArrayGet() {
+    return false;
+  }
+
+  public ArrayGet asArrayGet() {
+    return null;
+  }
+
+  public boolean isArrayLength() {
+    return false;
+  }
+
+  public ArrayLength asArrayLength() {
+    return null;
+  }
+
+  public boolean isArrayPut() {
+    return false;
+  }
+
+  public ArrayPut asArrayPut() {
+    return null;
+  }
+
+  public boolean isArgument() {
+    return false;
+  }
+
+  public Argument asArgument() {
+    return null;
+  }
+
+  public boolean isArithmeticBinop() {
+    return false;
+  }
+
+  public ArithmeticBinop asArithmeticBinop() {
+    return null;
+  }
+
+  public boolean isBinop() {
+    return false;
+  }
+
+  public Binop asBinop() {
+    return null;
+  }
+
+  public boolean isUnop() {
+    return false;
+  }
+
+  public Unop asUnop() {
+    return null;
+  }
+
+  public boolean isCheckCast() {
+    return false;
+  }
+
+  public CheckCast asCheckCast() {
+    return null;
+  }
+
+  public boolean isConstNumber() {
+    return false;
+  }
+
+  public ConstNumber asConstNumber() {
+    return null;
+  }
+
+  public boolean isConstInstruction() {
+    return false;
+  }
+
+  public ConstInstruction asConstInstruction() {
+    return null;
+  }
+
+  public boolean isConstClass() {
+    return false;
+  }
+
+  public ConstClass asConstClass() {
+    return null;
+  }
+
+  public boolean isConstString() {
+    return false;
+  }
+
+  public ConstString asConstString() {
+    return null;
+  }
+
+  public boolean isCmp() {
+    return false;
+  }
+
+  public Cmp asCmp() {
+    return null;
+  }
+
+  public boolean isJumpInstruction() {
+    return false;
+  }
+
+  public JumpInstruction asJumpInstruction() {
+    return null;
+  }
+
+  public boolean isGoto() {
+    return false;
+  }
+
+  public Goto asGoto() {
+    return null;
+  }
+
+  public boolean isIf() {
+    return false;
+  }
+
+  public If asIf() {
+    return null;
+  }
+
+  public boolean isSwitch() {
+    return false;
+  }
+
+  public Switch asSwitch() {
+    return null;
+  }
+
+  public boolean isInstanceGet() {
+    return false;
+  }
+
+  public InstanceGet asInstanceGet() {
+    return null;
+  }
+
+  public boolean isInstanceOf() {
+    return false;
+  }
+
+  public InstanceOf asInstanceOf() {
+    return null;
+  }
+
+  public boolean isInstancePut() {
+    return false;
+  }
+
+  public InstancePut asInstancePut() {
+    return null;
+  }
+
+  public boolean isInvoke() {
+    return false;
+  }
+
+  public Invoke asInvoke() {
+    return null;
+  }
+
+  public boolean isMonitor() {
+    return false;
+  }
+
+  public Monitor asMonitor() {
+    return null;
+  }
+
+  public boolean isMove() {
+    return false;
+  }
+
+  public Move asMove() {
+    return null;
+  }
+
+  public boolean isNewArrayEmpty() {
+    return false;
+  }
+
+  public NewArrayEmpty asNewArrayEmpty() {
+    return null;
+  }
+
+  public boolean isNewArrayFilledData() {
+    return false;
+  }
+
+  public NewArrayFilledData asNewArrayFilledData() {
+    return null;
+  }
+
+  public boolean isNeg() {
+    return false;
+  }
+
+  public Neg asNeg() {
+    return null;
+  }
+
+  public boolean isNewInstance() {
+    return false;
+  }
+
+  public NewInstance asNewInstance() {
+    return null;
+  }
+
+  public boolean isNot() {
+    return false;
+  }
+
+  public Not asNot() {
+    return null;
+  }
+
+  public boolean isNumberConversion() {
+    return false;
+  }
+
+  public NumberConversion asNumberConversion() {
+    return null;
+  }
+
+  public boolean isReturn() {
+    return false;
+  }
+
+  public Return asReturn() {
+    return null;
+  }
+
+  public boolean isThrow() {
+    return false;
+  }
+
+  public Throw asThrow() {
+    return null;
+  }
+
+  public boolean isStaticGet() {
+    return false;
+  }
+
+  public StaticGet asStaticGet() {
+    return null;
+  }
+
+  public boolean isStaticPut() {
+    return false;
+  }
+
+  public StaticPut asStaticPut() {
+    return null;
+  }
+
+  public boolean isAdd() {
+    return false;
+  }
+
+  public Add asAdd() {
+    return null;
+  }
+
+  public boolean isSub() {
+    return false;
+  }
+
+  public Sub asSub() {
+    return null;
+  }
+
+  public boolean isMul() {
+    return false;
+  }
+
+  public Mul asMul() {
+    return null;
+  }
+
+  public boolean isDiv() {
+    return false;
+  }
+
+  public Div asDiv() {
+    return null;
+  }
+
+  public boolean isRem() {
+    return false;
+  }
+
+  public Rem asRem() {
+    return null;
+  }
+
+  public boolean isLogicalBinop() {
+    return false;
+  }
+
+  public LogicalBinop asLogicalBinop() {
+    return null;
+  }
+
+  public boolean isShl() {
+    return false;
+  }
+
+  public Shl asShl() {
+    return null;
+  }
+
+  public boolean isShr() {
+    return false;
+  }
+
+  public Shr asShr() {
+    return null;
+  }
+
+  public boolean isUshr() {
+    return false;
+  }
+
+  public Ushr asUshr() {
+    return null;
+  }
+
+  public boolean isAnd() {
+    return false;
+  }
+
+  public And asAnd() {
+    return null;
+  }
+
+  public boolean isOr() {
+    return false;
+  }
+
+  public Or asOr() {
+    return null;
+  }
+
+  public boolean isXor() {
+    return false;
+  }
+
+  public Xor asXor() {
+    return null;
+  }
+
+  public boolean isMoveException() {
+    return false;
+  }
+
+  public MoveException asMoveException() {
+    return null;
+  }
+
+  public boolean isDebugInstruction() {
+    return isDebugPosition()
+        || isDebugLocalWrite()
+        || isDebugLocalRead()
+        || isDebugLocalUninitialized();
+  }
+
+  public boolean isDebugPosition() {
+    return false;
+  }
+
+  public DebugPosition asDebugPosition() {
+    return null;
+  }
+
+  public boolean isDebugLocalUninitialized() {
+    return false;
+  }
+
+  public DebugLocalUninitialized asDebugLocalUninitialized() {
+    return null;
+  }
+
+  public boolean isDebugLocalWrite() {
+    return false;
+  }
+
+  public DebugLocalWrite asDebugLocalWrite() {
+    return null;
+  }
+
+  public boolean isDebugLocalRead() {
+    return false;
+  }
+
+  public DebugLocalRead asDebugLocalRead() {
+    return null;
+  }
+
+  public boolean isInvokeMethod() {
+    return false;
+  }
+
+  public InvokeMethod asInvokeMethod() {
+    return null;
+  }
+
+  public boolean isInvokeNewArray() {
+    return false;
+  }
+
+  public InvokeNewArray asInvokeNewArray() {
+    return null;
+  }
+
+  public boolean isInvokeCustom() {
+    return false;
+  }
+
+  public InvokeCustom asInvokeCustom() {
+    return null;
+  }
+
+  public boolean isInvokeDirect() {
+    return false;
+  }
+
+  public InvokeDirect asInvokeDirect() {
+    return null;
+  }
+
+  public boolean isInvokeInterface() {
+    return false;
+  }
+
+  public InvokeInterface asInvokeInterface() {
+    return null;
+  }
+
+  public boolean isInvokeStatic() {
+    return false;
+  }
+
+  public InvokeStatic asInvokeStatic() {
+    return null;
+  }
+
+  public boolean isInvokeSuper() {
+    return false;
+  }
+
+  public InvokeSuper asInvokeSuper() {
+    return null;
+  }
+
+  public boolean isInvokeVirtual() {
+    return false;
+  }
+
+  public InvokeVirtual asInvokeVirtual() {
+    return null;
+  }
+
+  public boolean isInvokePolymorphic() {
+    return false;
+  }
+
+  public InvokePolymorphic asInvokePolymorphic() {
+    return null;
+  }
+
+  public boolean canBeFolded() {
+    return false;
+  }
+
+  public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+    throw new Unreachable("Unsupported folding for " + this);
+  }
+
+  // Returns the inlining constraint for this instruction.
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.NEVER;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
new file mode 100644
index 0000000..997b092
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.Iterator;
+
+public interface InstructionIterator extends Iterator<Instruction> {
+  /**
+   * Replace the current instruction (aka the {@link Instruction} returned by the previous call to
+   * {@link #next} with the passed in <code>newInstruction</code>.
+   * <p>
+   * The current instruction will be completely detached from the instruction stream with uses
+   * of its in-values removed.
+   * <p>
+   * If the current instruction produces an out-value the new instruction must also produce
+   * an out-value, and all uses of the current instructions out-value will be replaced by the
+   * new instructions out-value.
+   * <p>
+   * The debug information of the current instruction will be attached to the new instruction.
+   *
+   * @param newInstruction the instruction to insert instead of the current.
+   */
+  void replaceCurrentInstruction(Instruction newInstruction);
+
+  /**
+   * Adds an instruction. The instruction will be added just before the current
+   * cursor position.
+   *
+   * The instruction will be assigned to the block it is added to.
+   *
+   * @param instruction The instruction to add.
+   */
+  void add(Instruction instruction);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
new file mode 100644
index 0000000..b9815a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.function.Predicate;
+
+public interface InstructionListIterator extends ListIterator<Instruction> {
+
+  /**
+   * Peek the previous instruction.
+   *
+   * @return what will be returned by calling {@link #previous}. If there is no previous instruction
+   * <code>null</code> is returned.
+   */
+  default Instruction peekPrevious() {
+    Instruction previous = null;
+    if (hasPrevious()) {
+      previous = previous();
+      next();
+    }
+    return previous;
+  }
+
+  /**
+   * Peek the next instruction.
+   *
+   * @return what will be returned by calling {@link #next}. If there is no next instruction
+   * <code>null</code> is returned.
+   */
+  default Instruction peekNext() {
+    Instruction next = null;
+    if (hasNext()) {
+      next = next();
+      previous();
+    }
+    return next;
+  }
+
+  /**
+   * Continue to call {@link #next} while {@code predicate} tests {@code false}.
+   *
+   * @returns the instruction that matched the predicate or {@code null} if all instructions fails
+   * the predicate test
+   */
+  default Instruction nextUntil(Predicate<Instruction> predicate) {
+    while (hasNext()) {
+      Instruction instruction = next();
+      if (predicate.test(instruction)) {
+        return instruction;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Remove the current instruction (aka the {@link Instruction} returned by the previous call to
+   * {@link #next}) without updating its def/use chains.
+   * <p>
+   * This is useful for instance when moving an instruction to another block that still dominates
+   * all its uses. In order to do that you would detach the instruction from the original
+   * block and add it to the new block.
+   */
+  void detach();
+
+  /**
+   * Replace the current instruction (aka the {@link Instruction} returned by the previous call to
+   * {@link #next} with the passed in <code>newInstruction</code>.
+   *
+   * The current instruction will be completely detached from the instruction stream with uses
+   * of its in-values removed.
+   *
+   * If the current instruction produces an out-value the new instruction must also produce
+   * an out-value, and all uses of the current instructions out-value will be replaced by the
+   * new instructions out-value.
+   *
+   * @param newInstruction the instruction to insert instead of the current.
+   */
+  void replaceCurrentInstruction(Instruction newInstruction);
+
+  /**
+   * Split the block into two blocks at the point of the {@link ListIterator} cursor. The existing
+   * block will have all the instructions before the cursor, and the new block all the
+   * instructions after the cursor.
+   *
+   * If the current block has catch handlers these catch handlers will be attached to the block
+   * containing the throwing instruction after the split.
+   *
+   * @param code the IR code for the block this iterator originates from.
+   * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
+   * just after the block for which this is the instruction iterator. After this method returns it
+   * will be positioned just after the basic block returned.
+   * @return Returns the new block with the instructions after the cursor.
+   */
+  BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator);
+
+
+  default BasicBlock split(IRCode code) {
+    return split(code, null);
+  }
+
+  /**
+   * Split the block into three blocks. The first split is at the point of the {@link ListIterator}
+   * cursor and the second split is <code>instructions</code> after the cursor. The existing
+   * block will have all the instructions before the cursor, and the two new blocks all the
+   * instructions after the cursor.
+   *
+   * If the current block have catch handlers these catch handlers will be attached to the block
+   * containing the throwing instruction after the split.
+   *
+   * @param instructions the number of instructions to include in the second block.
+   * @param code the IR code for the block this iterator originates from.
+   * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
+   * just after the block for this is the instruction iterator. After this method returns it will be
+   * positioned just after the second block inserted. That is after the successor of the block
+   * returned.
+   * @return Returns the new block with the instructions right after the cursor.
+   */
+  // TODO(sgjesse): Refactor to avoid the need for passing code and blockIterator.
+  BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator);
+
+  /**
+   * See {@link #split(int, IRCode, ListIterator)}.
+   */
+  default BasicBlock split(int instructions, IRCode code) {
+    return split(instructions, code, null);
+  }
+
+  /**
+   * Inline the code in {@code inlinee} into {@code code}, replacing the invoke instruction at the
+   * position after the cursor.
+   *
+   * The instruction at the position after cursor must be an invoke that matches the signature for
+   * the code in {@code inlinee}.
+   *
+   * With one exception (see below) both the calling code and the inlinee can have catch handlers.
+   *
+   * <strong>EXCEPTION:</strong> If the invoke instruction is covered by catch handlers, and the
+   * code for {@code inlinee} always throws (does not have a normal return) inlining is currently
+   * <strong>NOT</strong> supported.
+   *
+   * @param code the IR code for the block this iterator originates from.
+   * @param inlinee the IR code for the block this iterator originates from.
+   * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned
+   * just after the block for which this is the instruction iterator. After this method returns it
+   * will be positioned just after the basic block returned.
+   * @param blocksToRemove list passed where blocks that where detached from the graph, but not
+   * removed are added. When inlining an inlinee that always throws blocks in the <code>code</code>
+   * can be detached, and not simply removed unsing the passed <code>blockIterator</code>. When
+   * iterating using <code>blockIterator</code> after then method returns the blocks in this list
+   * must be skipped when iterating with the active <code>blockIterator</code> and ultimately
+   * removed.
+   * @return the basic block with the instructions right after the inlining. This can be a block
+   * which can also be in the <code>blocksToRemove</code> list.
+   */
+  // TODO(sgjesse): Refactor to avoid the need for passing code.
+  // TODO(sgjesse): Refactor to avoid the need for passing blocksToRemove.
+  // TODO(sgjesse): Maybe don't return a BasicBlock, as it can be in blocksToRemove.
+  // TODO(sgjesse): Maybe find a better place for this method.
+  // TODO(sgjesse): Support inlinee with throwing instructions for invokes with existing handlers.
+  BasicBlock inlineInvoke(IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator,
+      List<BasicBlock> blocksToRemove);
+
+  /**
+   * See {@link #inlineInvoke(IRCode, IRCode, ListIterator, List<BasicBlock>)}.
+   */
+  default BasicBlock inlineInvoke(IRCode code, IRCode inlinee) {
+    List<BasicBlock> blocksToRemove = new ArrayList<>();
+    BasicBlock result = inlineInvoke(code, inlinee, null, blocksToRemove);
+    code.removeBlocks(blocksToRemove);
+    return result;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
new file mode 100644
index 0000000..5b516b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -0,0 +1,193 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultObject;
+import com.android.tools.r8.code.MoveResultWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public abstract class Invoke extends Instruction {
+
+  public enum Type {
+    DIRECT,
+    INTERFACE,
+    STATIC,
+    SUPER,
+    VIRTUAL,
+    NEW_ARRAY,
+    CUSTOM,
+    POLYMORPHIC
+  }
+
+  public Invoke(Value result, List<Value> arguments) {
+    super(result, arguments);
+  }
+
+  public static Invoke create(
+      Type type, DexItem target, DexProto proto, Value result, List<Value> arguments) {
+    switch (type) {
+      case DIRECT:
+        return new InvokeDirect((DexMethod) target, result, arguments);
+      case INTERFACE:
+        return new InvokeInterface((DexMethod) target, result, arguments);
+      case STATIC:
+        return new InvokeStatic((DexMethod) target, result, arguments);
+      case SUPER:
+        return new InvokeSuper((DexMethod) target, result, arguments);
+      case VIRTUAL:
+        return new InvokeVirtual((DexMethod) target, result, arguments);
+      case NEW_ARRAY:
+        return new InvokeNewArray((DexType) target, result, arguments);
+      case CUSTOM:
+        throw new Unreachable("Use InvokeCustom constructor instead");
+      case POLYMORPHIC:
+        return new InvokePolymorphic((DexMethod) target, proto, result, arguments);
+    }
+    throw new Unreachable("Unknown invoke type: " + type);
+  }
+
+  public static Instruction createFromTemplate(
+      Invoke template, Value outValue, List<Value> inValues) {
+    if (template.isInvokeMethod()) {
+      return create(template.getType(),
+          template.asInvokeMethod().getInvokedMethod(),
+          template.isInvokePolymorphic() ? template.asInvokePolymorphic().getProto() : null,
+          outValue,
+          inValues);
+    }
+
+    if (template.isInvokeNewArray()) {
+      return new InvokeNewArray(template.asInvokeNewArray().getArrayType(), outValue, inValues);
+    }
+
+    assert template.isInvokeCustom();
+    InvokeCustom custom = template.asInvokeCustom();
+    return new InvokeCustom(custom.getCallSite(), outValue, inValues);
+  }
+
+  abstract public Type getType();
+
+  public List<Value> arguments() {
+    return inValues;
+  }
+
+  public int requiredArgumentRegisters() {
+    int registers = 0;
+    for (Value inValue : inValues) {
+      registers += inValue.requiredRegisters();
+    }
+    return registers;
+  }
+
+  protected int argumentRegisterValue(int i, DexBuilder builder) {
+    if (i < arguments().size()) {
+      return builder.allocatedRegister(arguments().get(i), getNumber());
+    }
+    return 0;
+  }
+
+  protected int fillArgumentRegisters(DexBuilder builder, int[] registers) {
+    int i = 0;
+    for (Value value : arguments()) {
+      int register = builder.allocatedRegister(value, getNumber());
+      for (int j = 0; j < value.requiredRegisters(); j++) {
+        assert i < 5;
+        registers[i++] = register++;
+      }
+    }
+    return i;
+  }
+
+  protected boolean hasHighArgumentRegister(DexBuilder builder) {
+    for (Value value : arguments()) {
+      if (builder.argumentValueUsesHighRegister(value, getNumber())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  protected boolean argumentsConsecutive(DexBuilder builder) {
+    Value value = arguments().get(0);
+    int next = builder.allocatedRegister(value, getNumber()) + value.requiredRegisters();
+    for (int i = 1; i < arguments().size(); i++) {
+      value = arguments().get(i);
+      assert next == builder.allocatedRegister(value, getNumber());
+      next += value.requiredRegisters();
+    }
+    return true;
+  }
+
+  protected void addInvokeAndMoveResult(com.android.tools.r8.code.Instruction instruction, DexBuilder builder) {
+    if (outValue != null && outValue.needsRegister()) {
+      int register = builder.allocatedRegister(outValue, getNumber());
+      com.android.tools.r8.code.Instruction moveResult;
+      switch (outType()) {
+        case SINGLE:
+          moveResult = new MoveResult(register);
+          break;
+        case WIDE:
+          moveResult = new MoveResultWide(register);
+          break;
+        case OBJECT:
+          moveResult = new MoveResultObject(register);
+          break;
+        default:
+          throw new Unreachable("Unexpected result type " + outType());
+      }
+      builder.add(this, new com.android.tools.r8.code.Instruction[]{instruction, moveResult});
+    } else {
+      builder.add(this, instruction);
+    }
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    if (requiredArgumentRegisters() > 5) {
+      return Constants.U16BIT_MAX;
+    }
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  abstract protected String getTypeString();
+
+  @Override
+  public String getInstructionName() {
+    return "Invoke-" + getTypeString();
+  }
+
+  // This method is used for inlining.
+  // It returns the target method iff this invoke has only one target.
+  abstract public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo);
+
+  @Override
+  public boolean isInvoke() {
+    return true;
+  }
+
+  @Override
+  public Invoke asInvoke() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
new file mode 100644
index 0000000..2c7ea4b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokeCustomRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public final class InvokeCustom extends Invoke {
+
+  private final DexCallSite callSite;
+
+  public InvokeCustom(DexCallSite callSite, Value result, List<Value> arguments) {
+    super(result, arguments);
+    assert callSite != null;
+    this.callSite = callSite;
+  }
+
+  public DexCallSite getCallSite() {
+    return callSite;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.CUSTOM;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Custom";
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; call site: " + callSite.toSourceString();
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    // Target method can not be known at compile time.
+    return null;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokeCustomRange(firstRegister, argumentRegisters, getCallSite());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokeCustom(
+          argumentRegistersCount,
+          getCallSite(),
+          individualArgumentRegisters[0], // C
+          individualArgumentRegisters[1], // D
+          individualArgumentRegisters[2], // E
+          individualArgumentRegisters[3], // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.isInvokeCustom() && callSite == other.asInvokeCustom().callSite;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isInvokeCustom();
+    assert false : "Not supported";
+    return 0;
+  }
+
+  public boolean isInvokeCustom() {
+    return true;
+  }
+
+  public InvokeCustom asInvokeCustom() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
new file mode 100644
index 0000000..26bc116
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeDirect extends InvokeMethod {
+
+  public InvokeDirect(DexMethod target, Value result, List<Value> arguments) {
+    super(target, result, arguments);
+  }
+
+  @Override
+  public Type getType() {
+    return Type.DIRECT;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Direct";
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    return appInfo.lookupDirectTarget(getInvokedMethod());
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokeDirectRange(firstRegister, argumentRegisters, getInvokedMethod());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokeDirect(
+          argumentRegistersCount,
+          getInvokedMethod(),
+          individualArgumentRegisters[0],  // C
+          individualArgumentRegisters[1],  // D
+          individualArgumentRegisters[2],  // E
+          individualArgumentRegisters[3],  // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  /**
+   * Two invokes of a constructor are only allowed to be considered equal if the object
+   * they are initializing is the same. Art rejects code that has objects created by
+   * different new-instance instructions flow to one constructor invoke.
+   */
+  public boolean sameConstructorReceiverValue(Invoke other) {
+    if (!getInvokedMethod().name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+      return true;
+    }
+    return inValues.get(0) == other.inValues.get(0);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokeDirect()) {
+      return false;
+    }
+    return super.identicalNonValueParts(other);
+  }
+
+  @Override
+  public boolean isInvokeDirect() {
+    return true;
+  }
+
+  @Override
+  public InvokeDirect asInvokeDirect() {
+    return this;
+  }
+
+  @Override
+  public InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokeDirect(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
new file mode 100644
index 0000000..4e3a4b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeInterface extends InvokeMethod {
+
+  public InvokeInterface(DexMethod target, Value result, List<Value> arguments) {
+    super(target, result, arguments);
+  }
+
+  @Override
+  public Type getType() {
+    return Type.INTERFACE;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Interface";
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    return appInfo.lookupSingleInterfaceTarget(getInvokedMethod());
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokeInterfaceRange(firstRegister, argumentRegisters, getInvokedMethod());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokeInterface(
+          argumentRegistersCount,
+          getInvokedMethod(),
+          individualArgumentRegisters[0],  // C
+          individualArgumentRegisters[1],  // D
+          individualArgumentRegisters[2],  // E
+          individualArgumentRegisters[3],  // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokeInterface()) {
+      return false;
+    }
+    return super.identicalNonValueParts(other);
+  }
+
+  @Override
+  public boolean isInvokeInterface() {
+    return true;
+  }
+
+  @Override
+  public InvokeInterface asInvokeInterface() {
+    return this;
+  }
+
+  @Override
+  public InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokeInterface(this);
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
new file mode 100644
index 0000000..6435790
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public abstract class InvokeMethod extends Invoke {
+
+  private DexMethod method;
+
+  public InvokeMethod(DexMethod target, Value result, List<Value> arguments) {
+    super(result, arguments);
+    this.method = target;
+  }
+
+  public DexMethod getInvokedMethod() {
+    return method;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return method == other.asInvokeMethod().getInvokedMethod();
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return getInvokedMethod().slowCompareTo(other.asInvokeMethod().getInvokedMethod());
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; method: " + method.toSourceString();
+  }
+
+  @Override
+  public boolean isInvokeMethod() {
+    return true;
+  }
+
+  @Override
+  public InvokeMethod asInvokeMethod() {
+    return this;
+  }
+
+  abstract public InlineAction computeInlining(InliningOracle decider);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
new file mode 100644
index 0000000..0725fed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.FilledNewArray;
+import com.android.tools.r8.code.FilledNewArrayRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import java.util.List;
+
+public class InvokeNewArray extends Invoke {
+
+  private DexType type;
+
+  public InvokeNewArray(DexType type, Value result, List<Value> arguments) {
+    super(result, arguments);
+    this.type = type;
+  }
+
+  public DexType getArrayType() {
+    return type;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.NEW_ARRAY;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "NewArray";
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; type: " + type.toSourceString();
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    return null;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new FilledNewArrayRange(firstRegister, argumentRegisters, type);
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new FilledNewArray(
+          argumentRegistersCount,
+          type,
+          individualArgumentRegisters[0],  // C
+          individualArgumentRegisters[1],  // D
+          individualArgumentRegisters[2],  // E
+          individualArgumentRegisters[3],  // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokeNewArray()) {
+      return false;
+    }
+    return type == other.asInvokeNewArray().type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    if (!other.isInvokeNewArray()) {
+      return -1;
+    }
+    return type.slowCompareTo(other.asInvokeNewArray().type);
+  }
+
+  @Override
+  public boolean isInvokeNewArray() {
+    return true;
+  }
+
+  @Override
+  public InvokeNewArray asInvokeNewArray() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
new file mode 100644
index 0000000..9c87144
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokePolymorphicRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokePolymorphic extends InvokeMethod {
+
+  private final DexProto proto;
+
+  public InvokePolymorphic(DexMethod target, DexProto proto, Value result, List<Value> arguments) {
+    super(target, result, arguments);
+    this.proto = proto;
+  }
+
+  @Override
+  public Type getType() {
+    return Type.POLYMORPHIC;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Polymorphic";
+  }
+
+  public DexProto getProto() {
+    return proto;
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    // Target method can not be known at compile time.
+    return null;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokePolymorphicRange(
+              firstRegister, argumentRegisters, getInvokedMethod(), getProto());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokePolymorphic(
+              argumentRegistersCount,
+              getInvokedMethod(),
+              getProto(),
+              individualArgumentRegisters[0], // C
+              individualArgumentRegisters[1], // D
+              individualArgumentRegisters[2], // E
+              individualArgumentRegisters[3], // F
+              individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokePolymorphic()) {
+      return false;
+    }
+    return proto.equals(((InvokePolymorphic) other).proto) && super.identicalNonValueParts(other);
+  }
+
+  @Override
+  public boolean isInvokePolymorphic() {
+    return true;
+  }
+
+  @Override
+  public InvokePolymorphic asInvokePolymorphic() {
+    return this;
+  }
+
+  @Override
+  public InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokePolymorpic(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
new file mode 100644
index 0000000..0c4fe14
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeStatic extends InvokeMethod {
+
+  public InvokeStatic(DexMethod target, Value result, List<Value> arguments) {
+    super(target, result, arguments);
+  }
+
+  @Override
+  public Type getType() {
+    return Type.STATIC;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Static";
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    return appInfo.lookupStaticTarget(getInvokedMethod());
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokeStaticRange(firstRegister, argumentRegisters, getInvokedMethod());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokeStatic(
+          argumentRegistersCount,
+          getInvokedMethod(),
+          individualArgumentRegisters[0],  // C
+          individualArgumentRegisters[1],  // D
+          individualArgumentRegisters[2],  // E
+          individualArgumentRegisters[3],  // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokeStatic()) {
+      return false;
+    }
+    return super.identicalNonValueParts(other);
+  }
+
+  @Override
+  public boolean isInvokeStatic() {
+    return true;
+  }
+
+  @Override
+  public InvokeStatic asInvokeStatic() {
+    return this;
+  }
+
+  @Override
+  public InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokeStatic(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
new file mode 100644
index 0000000..2e1e3e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeSuper extends InvokeMethod {
+
+  public InvokeSuper(DexMethod target, Value result, List<Value> arguments) {
+    super(target, result, arguments);
+  }
+
+  @Override
+  public Type getType() {
+    return Type.SUPER;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Super";
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    return appInfo.lookupDirectTarget(getInvokedMethod());
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokeSuperRange(firstRegister, argumentRegisters, getInvokedMethod());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokeSuper(
+          argumentRegistersCount,
+          getInvokedMethod(),
+          individualArgumentRegisters[0],  // C
+          individualArgumentRegisters[1],  // D
+          individualArgumentRegisters[2],  // E
+          individualArgumentRegisters[3],  // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokeSuper()) {
+      return false;
+    }
+    return super.identicalNonValueParts(other);
+  }
+
+  @Override
+  public boolean isInvokeSuper() {
+    return true;
+  }
+
+  @Override
+  public InvokeSuper asInvokeSuper() {
+    return this;
+  }
+
+  @Override
+  public InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokeSuper(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
new file mode 100644
index 0000000..15ec76e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.ir.optimize.InliningOracle;
+import java.util.List;
+
+public class InvokeVirtual extends InvokeMethod {
+
+  public InvokeVirtual(DexMethod target, Value result, List<Value> arguments) {
+    super(target, result, arguments);
+  }
+
+  @Override
+  public Type getType() {
+    return Type.VIRTUAL;
+  }
+
+  @Override
+  protected String getTypeString() {
+    return "Virtual";
+  }
+
+  @Override
+  public DexEncodedMethod computeSingleTarget(AppInfoWithSubtyping appInfo) {
+    return appInfo.lookupSingleVirtualTarget(getInvokedMethod());
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int argumentRegisters = requiredArgumentRegisters();
+    builder.requestOutgoingRegisters(argumentRegisters);
+    if (argumentRegisters > 5 || hasHighArgumentRegister(builder)) {
+      assert argumentsConsecutive(builder);
+      int firstRegister = argumentRegisterValue(0, builder);
+      instruction = new InvokeVirtualRange(firstRegister, argumentRegisters, getInvokedMethod());
+    } else {
+      int[] individualArgumentRegisters = new int[5];
+      int argumentRegistersCount = fillArgumentRegisters(builder, individualArgumentRegisters);
+      instruction = new com.android.tools.r8.code.InvokeVirtual(
+          argumentRegistersCount,
+          getInvokedMethod(),
+          individualArgumentRegisters[0],  // C
+          individualArgumentRegisters[1],  // D
+          individualArgumentRegisters[2],  // E
+          individualArgumentRegisters[3],  // F
+          individualArgumentRegisters[4]); // G
+    }
+    addInvokeAndMoveResult(instruction, builder);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (!other.isInvokeVirtual()) {
+      return false;
+    }
+    return super.identicalNonValueParts(other);
+  }
+
+  @Override
+  public boolean isInvokeVirtual() {
+    return true;
+  }
+
+  @Override
+  public InvokeVirtual asInvokeVirtual() {
+    return this;
+  }
+
+  @Override
+  public InlineAction computeInlining(InliningOracle decider) {
+    return decider.computeForInvokeVirtual(this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
new file mode 100644
index 0000000..2a02218
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/JumpInstruction.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+
+public abstract class JumpInstruction extends Instruction {
+
+  public JumpInstruction(Value out) {
+    super(null);
+  }
+
+  public JumpInstruction(Value out, Value in) {
+    super(out, in);
+  }
+
+  public JumpInstruction(Value out, List<? extends Value> ins) {
+    super(out, ins);
+  }
+
+  public BasicBlock fallthroughBlock() {
+    return null;
+  }
+
+  public void setFallthroughBlock(BasicBlock block) {
+    assert false : "We should not change the fallthrough of a JumpInstruction with no fallthrough.";
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    return false;
+  }
+
+  @Override
+  public boolean isJumpInstruction() {
+    return true;
+  }
+
+  @Override
+  public JumpInstruction asJumpInstruction() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
new file mode 100644
index 0000000..dc16d43
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public abstract class LogicalBinop extends Binop {
+
+  public LogicalBinop(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  public abstract com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right);
+
+  public abstract Instruction CreateLong(int dest, int left, int right);
+
+  public abstract Instruction CreateInt2Addr(int left, int right);
+
+  public abstract Instruction CreateLong2Addr(int left, int right);
+
+  public abstract Instruction CreateIntLit8(int dest, int left, int constant);
+
+  public abstract Instruction CreateIntLit16(int dest, int left, int constant);
+
+  @Override
+  public boolean canBeFolded() {
+    return leftValue().isConstant() && rightValue().isConstant();
+  }
+
+  public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+    assert canBeFolded();
+    if (type == NumericType.INT) {
+      int left = leftValue().getConstInstruction().asConstNumber().getIntValue();
+      int right = rightValue().getConstInstruction().asConstNumber().getIntValue();
+      int result = foldIntegers(left, right);
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+      return new ConstNumber(ConstType.INT, value, result);
+    } else {
+      assert type == NumericType.LONG;
+      long left = leftValue().getConstInstruction().asConstNumber().getLongValue();
+      long right;
+      if (isShl() || isShr() || isUshr()) {
+        // Right argument for shl, shr and ushr is always of type single.
+        right = rightValue().getConstInstruction().asConstNumber().getIntValue();
+      } else {
+        right = rightValue().getConstInstruction().asConstNumber().getLongValue();
+      }
+      long result = foldLongs(left, right);
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+      return new ConstNumber(ConstType.LONG, value, result);
+    }
+  }
+
+  public boolean needsValueInRegister(Value value) {
+    // Always require the left value in a register. If left and right are the same value, then
+    // both will use its register.
+    if (value == leftValue()) {
+      return true;
+    }
+    assert value == rightValue();
+    return !fitsInDexInstruction(value);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    // TODO(ager, sgjesse): For now the left operand is always a value. With constant propagation
+    // that will change.
+    int left = builder.allocatedRegister(leftValue(), getNumber());
+    int dest = builder.allocatedRegister(outValue, getNumber());
+    Instruction instruction;
+    if (isTwoAddr(builder)) {
+      int right = builder.allocatedRegister(rightValue(), getNumber());
+      if (left != dest) {
+        assert isCommutative();
+        assert right == dest;
+        right = left;
+      }
+      switch (type) {
+        case INT:
+          instruction = CreateInt2Addr(dest, right);
+          break;
+        case LONG:
+          instruction = CreateLong2Addr(dest, right);
+          break;
+        default:
+          throw new Unreachable("Unexpected type " + type);
+      }
+    } else if (!rightValue().needsRegister()) {
+      assert fitsInDexInstruction(rightValue());
+      ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+      if (right.is8Bit()) {
+        instruction = CreateIntLit8(dest, left, right.getIntValue());
+      } else {
+        assert right.is16Bit();
+        instruction = CreateIntLit16(dest, left, right.getIntValue());
+      }
+    } else {
+      int right = builder.allocatedRegister(rightValue(), getNumber());
+      switch (type) {
+        case INT:
+          instruction = CreateInt(dest, left, right);
+          break;
+        case LONG:
+          instruction = CreateLong(dest, left, right);
+          break;
+        default:
+          throw new Unreachable("Unexpected type " + type);
+      }
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean isLogicalBinop() {
+    return true;
+  }
+
+  @Override
+  public LogicalBinop asLogicalBinop() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MemberType.java b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
new file mode 100644
index 0000000..5a992ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/MemberType.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+
+public enum MemberType {
+  SINGLE,
+  WIDE,
+  OBJECT,
+  BOOLEAN,
+  BYTE,
+  CHAR,
+  SHORT;
+
+  public static MoveType moveTypeFor(MemberType type) {
+    switch (type) {
+      case BOOLEAN:
+      case BYTE:
+      case CHAR:
+      case SHORT:
+      case SINGLE:
+        return MoveType.SINGLE;
+      case WIDE:
+        return MoveType.WIDE;
+      case OBJECT:
+        return MoveType.OBJECT;
+    }
+    return null;
+  }
+
+  public static MemberType fromTypeDescriptorChar(char descriptor) {
+    switch (descriptor) {
+      case 'L':  // object
+      case '[':  // array
+        return MemberType.OBJECT;
+      case 'Z':  // boolean
+        return MemberType.BOOLEAN;
+      case 'B':  // byte
+        return MemberType.BYTE;
+      case 'S':  // short
+        return MemberType.SHORT;
+      case 'C':  // char
+        return MemberType.CHAR;
+      case 'I':  // int
+      case 'F':  // float
+        return MemberType.SINGLE;
+      case 'J':  // long
+      case 'D':  // double
+        return MemberType.WIDE;
+      case 'V':
+        throw new InternalCompilerError("No member type for void type.");
+      default:
+        throw new Unreachable("Invalid descriptor char '" + descriptor + "'");
+    }
+  }
+
+  public static MemberType fromDexType(DexType type) {
+    return fromTypeDescriptorChar((char) type.descriptor.content[0]);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
new file mode 100644
index 0000000..869d1d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
+
+import com.android.tools.r8.code.MonitorEnter;
+import com.android.tools.r8.code.MonitorExit;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Monitor extends Instruction {
+
+  public enum Type {
+    ENTER, EXIT
+  }
+
+  private final Type type;
+
+  public Monitor(Type type, Value object) {
+    super(null, object);
+    this.type = type;
+  }
+
+  private Value object() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int object = builder.allocatedRegister(object(), getNumber());
+    if (type == Type.ENTER) {
+      builder.add(this, new MonitorEnter(object));
+    } else {
+      builder.add(this, new MonitorExit(object));
+    }
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asMonitor().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asMonitor().type.ordinal();
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "Monitor defines no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean isMonitor() {
+    return true;
+  }
+
+  @Override
+  public Monitor asMonitor() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Move.java b/src/main/java/com/android/tools/r8/ir/code/Move.java
new file mode 100644
index 0000000..6f70c57
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Move.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Move extends Instruction {
+
+  public Move(Value dest, Value src) {
+    super(dest, src);
+    if (src.isNeverNull()) {
+      dest.markNeverNull();
+    }
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value src() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.addMove(this);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U16BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U16BIT_MAX;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isMove();
+    return true;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isMove();
+    return 0;
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " (" + outType() + ")";
+  }
+
+  @Override
+  public boolean isOutConstant() {
+    return src().isConstant();
+  }
+
+  @Override
+  public ConstInstruction getOutConstantConstInstruction() {
+    assert isOutConstant();
+    return src().definition.getOutConstantConstInstruction();
+  }
+
+  @Override
+  public boolean isMove() {
+    return true;
+  }
+
+  @Override
+  public Move asMove() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
new file mode 100644
index 0000000..3e63e50
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class MoveException extends Instruction {
+
+  public MoveException(Value dest) {
+    super(dest);
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.MoveException(dest));
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "MoveException has no register arguments.";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isMoveException();
+    return true;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isMoveException();
+    return 0;
+  }
+
+  @Override
+  public boolean isMoveException() {
+    return true;
+  }
+
+  @Override
+  public MoveException asMoveException() {
+    return this;
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    return !options.debug;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveType.java b/src/main/java/com/android/tools/r8/ir/code/MoveType.java
new file mode 100644
index 0000000..18f5632
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveType.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+
+public enum MoveType {
+  SINGLE,
+  WIDE,
+  OBJECT;
+
+  public int requiredRegisters() {
+    return this == MoveType.WIDE ? 2 : 1;
+  }
+
+  public static MoveType fromMemberType(MemberType type) {
+    switch (type) {
+      case BOOLEAN:
+      case BYTE:
+      case CHAR:
+      case SHORT:
+      case SINGLE:
+        return MoveType.SINGLE;
+      case WIDE:
+        return MoveType.WIDE;
+      case OBJECT:
+        return MoveType.OBJECT;
+    }
+    assert false;
+    return null;
+  }
+
+  public static MoveType fromTypeDescriptorChar(char descriptor) {
+    switch (descriptor) {
+      case 'L':  // object
+      case '[':  // array
+        return MoveType.OBJECT;
+      case 'Z':  // boolean
+      case 'B':  // byte
+      case 'S':  // short
+      case 'C':  // char
+      case 'I':  // int
+      case 'F':  // float
+        return MoveType.SINGLE;
+      case 'J':  // long
+      case 'D':  // double
+        return MoveType.WIDE;
+      case 'V':
+        throw new InternalCompilerError("No move type for void type.");
+      default:
+        throw new Unreachable("Invalid descriptor char '" + descriptor + "'");
+    }
+  }
+
+  public static MoveType fromDexType(DexType type) {
+    return fromTypeDescriptorChar((char) type.descriptor.content[0]);
+  }
+
+  public static MoveType fromConstType(ConstType type) {
+    switch (type) {
+      case INT:
+      case FLOAT:
+      case INT_OR_FLOAT:
+        return MoveType.SINGLE;
+      case OBJECT:
+        return MoveType.OBJECT;
+      case LONG:
+      case DOUBLE:
+      case LONG_OR_DOUBLE:
+        return MoveType.WIDE;
+      default:
+        throw new Unreachable("Invalid const type '" + type + "'");
+    }
+  }
+
+  public static MoveType fromNumericType(NumericType type) {
+    switch (type) {
+      case BYTE:
+      case CHAR:
+      case SHORT:
+      case INT:
+      case FLOAT:
+        return MoveType.SINGLE;
+      case LONG:
+      case DOUBLE:
+        return MoveType.WIDE;
+      default:
+        throw new Unreachable("Invalid numeric type '" + type + "'");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Mul.java b/src/main/java/com/android/tools/r8/ir/code/Mul.java
new file mode 100644
index 0000000..19024f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Mul.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.MulDouble;
+import com.android.tools.r8.code.MulDouble2Addr;
+import com.android.tools.r8.code.MulFloat;
+import com.android.tools.r8.code.MulFloat2Addr;
+import com.android.tools.r8.code.MulInt;
+import com.android.tools.r8.code.MulInt2Addr;
+import com.android.tools.r8.code.MulIntLit16;
+import com.android.tools.r8.code.MulIntLit8;
+import com.android.tools.r8.code.MulLong;
+import com.android.tools.r8.code.MulLong2Addr;
+
+public class Mul extends ArithmeticBinop {
+
+  public Mul(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return true;
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new MulInt(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    return new MulLong(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+    return new MulFloat(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+    return new MulDouble(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new MulInt2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new MulLong2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+    return new MulFloat2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+    return new MulDouble2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new MulIntLit8(dest, left, constant);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new MulIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asMul().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asMul().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left * right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left * right;
+  }
+
+  @Override
+  float foldFloat(float left, float right) {
+    return left * right;
+  }
+
+  @Override
+  double foldDouble(double left, double right) {
+    return left * right;
+  }
+
+  @Override
+  public boolean isMul() {
+    return true;
+  }
+
+  @Override
+  public Mul asMul() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
new file mode 100644
index 0000000..6066439
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.NegDouble;
+import com.android.tools.r8.code.NegFloat;
+import com.android.tools.r8.code.NegInt;
+import com.android.tools.r8.code.NegLong;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Neg extends Unop {
+
+  public final NumericType type;
+
+  public Neg(NumericType type, Value dest, Value source) {
+    super(dest, source);
+    this.type = type;
+  }
+
+  @Override
+  public boolean canBeFolded() {
+    return (type == NumericType.INT || type == NumericType.LONG || type == NumericType.FLOAT
+            || type == NumericType.DOUBLE)
+        && source().isConstant();
+  }
+
+  @Override
+  public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+    assert canBeFolded();
+    if (type == NumericType.INT) {
+      int result = -source().getConstInstruction().asConstNumber().getIntValue();
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+      return new ConstNumber(ConstType.INT, value, result);
+    } else if (type == NumericType.LONG) {
+      long result = -source().getConstInstruction().asConstNumber().getLongValue();
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+      return new ConstNumber(ConstType.LONG, value, result);
+    } else if (type == NumericType.FLOAT) {
+      float result = -source().getConstInstruction().asConstNumber().getFloatValue();
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+      return new ConstNumber(ConstType.FLOAT, value, Float.floatToIntBits(result));
+    } else {
+      assert type == NumericType.DOUBLE;
+      double result = -source().getConstInstruction().asConstNumber().getDoubleValue();
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+      return new ConstNumber(ConstType.DOUBLE, value, Double.doubleToLongBits(result));
+    }
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asNeg().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asNeg().type.ordinal();
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    int src = builder.allocatedRegister(source(), getNumber());
+    switch (type) {
+      case INT:
+        instruction = new NegInt(dest, src);
+        break;
+      case LONG:
+        instruction = new NegLong(dest, src);
+        break;
+      case FLOAT:
+        instruction = new NegFloat(dest, src);
+        break;
+      case DOUBLE:
+        instruction = new NegDouble(dest, src);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean isNeg() {
+    return true;
+  }
+
+  @Override
+  public Neg asNeg() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
new file mode 100644
index 0000000..f7b0367
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.NewArray;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class NewArrayEmpty extends Instruction {
+
+  public final DexType type;
+
+  public NewArrayEmpty(Value dest, Value size, DexType type) {
+    super(dest, size);
+    dest.markNeverNull();
+    this.type = type;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value size() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int size = builder.allocatedRegister(size(), getNumber());
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    builder.add(this, new NewArray(dest, size, type));
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    // new-array throws if its size is negative, but can also potentially throw on out-of-memory.
+    return true;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asNewArrayEmpty().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.slowCompareTo(other.asNewArrayEmpty().type);
+  }
+
+  @Override
+  public boolean isNewArrayEmpty() {
+    return true;
+  }
+
+  @Override
+  public NewArrayEmpty asNewArrayEmpty() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
new file mode 100644
index 0000000..b7d05e8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayFilledData.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+
+public class NewArrayFilledData extends Instruction {
+
+  public final int element_width;
+  public final long size;
+  public final short[] data;
+
+  // Primitive array with fill-array-data. The type is not known from the original Dex instruction.
+  public NewArrayFilledData(Value src, int element_width, long size, short[] data) {
+    super(null, src);
+    this.element_width = element_width;
+    this.size = size;
+    this.data = data;
+  }
+
+  public Value src() {
+    return inValues.get(0);
+  }
+
+  public FillArrayDataPayload createPayload() {
+    return new FillArrayDataPayload(element_width, size, data);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int src = builder.allocatedRegister(src(), getNumber());
+    builder.addFillArrayData(this, new FillArrayData(src));
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    NewArrayFilledData o = other.asNewArrayFilledData();
+    return o.element_width == element_width
+        && o.size == size
+        && Arrays.equals(o.data, data);
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    NewArrayFilledData o = other.asNewArrayFilledData();
+    int result;
+    result = element_width - o.element_width;
+    if (result != 0) {
+      return result;
+    }
+    result = Long.signum(size - o.size);
+    if (result != 0) {
+      return result;
+    }
+    assert data.length == o.data.length;
+    for (int i = 0; i < data.length; i++) {
+      result = data[i] - o.data[i];
+      if (result != 0) {
+        return result;
+      }
+    }
+    return 0;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "NewArrayFilledData defines no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean canBeDeadCode(InternalOptions options) {
+    // Side-effects its input values.
+    return false;
+  }
+
+  @Override
+  public boolean isNewArrayFilledData() {
+    return true;
+  }
+
+  @Override
+  public NewArrayFilledData asNewArrayFilledData() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
new file mode 100644
index 0000000..1aa2cbb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class NewInstance extends Instruction {
+
+  public final DexType clazz;
+
+  public NewInstance(DexType clazz, Value dest) {
+    super(dest);
+    dest.markNeverNull();
+    assert clazz != null;
+    this.clazz = clazz;
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    builder.add(this, new com.android.tools.r8.code.NewInstance(dest, clazz));
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + " " + clazz;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asNewInstance().clazz == clazz;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return clazz.slowCompareTo(other.asNewInstance().clazz);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    assert false : "NewInstance has no register arguments";
+    return 0;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    // Creating a new instance can throw if the type is not found, or on out-of-memory.
+    return true;
+  }
+
+  @Override
+  public boolean isNewInstance() {
+    return true;
+  }
+
+  @Override
+  public NewInstance asNewInstance() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
new file mode 100644
index 0000000..9e41f3e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.NotInt;
+import com.android.tools.r8.code.NotLong;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Not extends Unop {
+
+  public final NumericType type;
+
+  public Not(NumericType type, Value dest, Value source) {
+    super(dest, source);
+    this.type = type;
+  }
+
+  @Override
+  public boolean canBeFolded() {
+    return source().isConstant();
+  }
+
+  @Override
+  public ConstInstruction fold(ValueNumberGenerator valueNumberGenerator) {
+    assert canBeFolded();
+    if (type == NumericType.INT) {
+      int result = ~(source().getConstInstruction().asConstNumber().getIntValue());
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.SINGLE, getDebugInfo());
+      return new ConstNumber(ConstType.INT, value, result);
+    } else {
+      assert type == NumericType.LONG;
+      long result = ~source().getConstInstruction().asConstNumber().getLongValue();
+      Value value = new Value(valueNumberGenerator.next(), -1, MoveType.WIDE, getDebugInfo());
+      return new ConstNumber(ConstType.LONG, value, result);
+    }
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    int src = builder.allocatedRegister(source(), getNumber());
+    switch (type) {
+      case INT:
+        instruction = new NotInt(dest, src);
+        break;
+      case LONG:
+        instruction = new NotLong(dest, src);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asNot().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asNot().type.ordinal();
+  }
+
+  @Override
+  public boolean isNot() {
+    return true;
+  }
+
+  @Override
+  public Not asNot() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
new file mode 100644
index 0000000..23cbb69
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.DoubleToFloat;
+import com.android.tools.r8.code.DoubleToInt;
+import com.android.tools.r8.code.DoubleToLong;
+import com.android.tools.r8.code.FloatToDouble;
+import com.android.tools.r8.code.FloatToInt;
+import com.android.tools.r8.code.FloatToLong;
+import com.android.tools.r8.code.IntToByte;
+import com.android.tools.r8.code.IntToChar;
+import com.android.tools.r8.code.IntToDouble;
+import com.android.tools.r8.code.IntToFloat;
+import com.android.tools.r8.code.IntToLong;
+import com.android.tools.r8.code.IntToShort;
+import com.android.tools.r8.code.LongToDouble;
+import com.android.tools.r8.code.LongToFloat;
+import com.android.tools.r8.code.LongToInt;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class NumberConversion extends Unop {
+
+  public final NumericType from;
+  public final NumericType to;
+
+  public NumberConversion(NumericType from, NumericType to, Value dest, Value source) {
+    super(dest, source);
+    this.from = from;
+    this.to = to;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    int src = builder.allocatedRegister(source(), getNumber());
+    switch (from) {
+      case INT:
+        switch (to) {
+          case BYTE:
+            instruction = new IntToByte(dest, src);
+            break;
+          case CHAR:
+            instruction = new IntToChar(dest, src);
+            break;
+          case SHORT:
+            instruction = new IntToShort(dest, src);
+            break;
+          case LONG:
+            instruction = new IntToLong(dest, src);
+            break;
+          case FLOAT:
+            instruction = new IntToFloat(dest, src);
+            break;
+          case DOUBLE:
+            instruction = new IntToDouble(dest, src);
+            break;
+          default:
+            throw new Unreachable("Unexpected types " + from + ", " + to);
+        }
+        break;
+      case LONG:
+        switch (to) {
+          case INT:
+            instruction = new LongToInt(dest, src);
+            break;
+          case FLOAT:
+            instruction = new LongToFloat(dest, src);
+            break;
+          case DOUBLE:
+            instruction = new LongToDouble(dest, src);
+            break;
+          default:
+            throw new Unreachable("Unexpected types " + from + ", " + to);
+        }
+        break;
+      case FLOAT:
+        switch (to) {
+          case INT:
+            instruction = new FloatToInt(dest, src);
+            break;
+          case LONG:
+            instruction = new FloatToLong(dest, src);
+            break;
+          case DOUBLE:
+            instruction = new FloatToDouble(dest, src);
+            break;
+          default:
+            throw new Unreachable("Unexpected types " + from + ", " + to);
+        }
+        break;
+      case DOUBLE:
+        switch (to) {
+          case INT:
+            instruction = new DoubleToInt(dest, src);
+            break;
+          case LONG:
+            instruction = new DoubleToLong(dest, src);
+            break;
+          case FLOAT:
+            instruction = new DoubleToFloat(dest, src);
+            break;
+          default:
+            throw new Unreachable("Unexpected types " + from + ", " + to);
+        }
+        break;
+      default:
+        throw new Unreachable("Unexpected types " + from + ", " + to);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    NumberConversion o = other.asNumberConversion();
+    return o.from == from && o.to == to;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    NumberConversion o = other.asNumberConversion();
+    int result;
+    result = from.ordinal() - o.from.ordinal();
+    if (result != 0) {
+      return result;
+    }
+    return to.ordinal() - o.to.ordinal();
+  }
+
+  @Override
+  public boolean isNumberConversion() {
+    return true;
+  }
+
+  @Override
+  public NumberConversion asNumberConversion() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumericType.java b/src/main/java/com/android/tools/r8/ir/code/NumericType.java
new file mode 100644
index 0000000..765bfe1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NumericType.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+
+public enum NumericType {
+  BYTE,
+  CHAR,
+  SHORT,
+  INT,
+  LONG,
+  FLOAT,
+  DOUBLE;
+
+  public MoveType moveTypeFor() {
+    if (this == NumericType.DOUBLE || this == NumericType.LONG) {
+      return MoveType.WIDE;
+    }
+    return MoveType.SINGLE;
+  }
+
+  public DexType dexTypeFor(DexItemFactory factory) {
+    switch (this) {
+      case BYTE:
+        return factory.byteType;
+      case CHAR:
+        return factory.charType;
+      case SHORT:
+        return factory.shortType;
+      case INT:
+        return factory.intType;
+      case LONG:
+        return factory.longType;
+      case FLOAT:
+        return factory.floatType;
+      case DOUBLE:
+        return factory.doubleType;
+      default:
+        throw new Unreachable("Invalid numeric type '" + this + "'");
+    }
+  }
+
+  public static NumericType fromDexType(DexType type) {
+    switch (type.descriptor.content[0]) {
+      case 'B':  // byte
+        return NumericType.BYTE;
+      case 'S':  // short
+        return NumericType.SHORT;
+      case 'C':  // char
+        return NumericType.CHAR;
+      case 'I':  // int
+        return NumericType.INT;
+      case 'F':  // float
+        return NumericType.FLOAT;
+      case 'J':  // long
+        return NumericType.LONG;
+      case 'D':  // double
+        return NumericType.DOUBLE;
+      default:
+        return null;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
new file mode 100644
index 0000000..bfa69f4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.OrInt;
+import com.android.tools.r8.code.OrInt2Addr;
+import com.android.tools.r8.code.OrIntLit16;
+import com.android.tools.r8.code.OrIntLit8;
+import com.android.tools.r8.code.OrLong;
+import com.android.tools.r8.code.OrLong2Addr;
+
+public class Or extends LogicalBinop {
+
+  public Or(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isOr() {
+    return true;
+  }
+
+  @Override
+  public Or asOr() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return true;
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new OrInt(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+    // the first part of the result long before reading the second part of the input longs.
+    // Therefore, there can be no overlap of the second part of an input long and the first
+    // part of the output long.
+    assert dest != left + 1;
+    assert dest != right + 1;
+    return new OrLong(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new OrInt2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new OrLong2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new OrIntLit8(dest, left, constant);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new OrIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asOr().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asOr().type.ordinal();
+  }
+
+  int foldIntegers(int left, int right) {
+    return left | right;
+  }
+
+  long foldLongs(long left, long right) {
+    return left | right;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
new file mode 100644
index 0000000..0d74321
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -0,0 +1,313 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.code.BasicBlock.EdgeType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class Phi extends Value {
+
+  private final BasicBlock block;
+  private final List<Value> operands = new ArrayList<>();
+
+  // Trivial phis are eliminated during IR construction. When a trivial phi is eliminated
+  // we need to update all references to it. A phi can be referenced from phis, instructions
+  // and current definition mappings. This list contains the current definitions mappings that
+  // contain this phi.
+  private List<Map<Integer, Value>> definitionUsers = new ArrayList<>();
+
+  // The computed out type is not always the same as 'this.type' because of the type
+  // confusion around null and constant zero. The null object can be used in a single
+  // context (if tests) and the single 0 can be used as null. A phi can therefore
+  // have either of the creation types 'single' and 'object' depending on the use that
+  // triggered the creation of the phi. We therefore have to delay the output type
+  // computation of the phi until all operands are known.
+  private MoveType outType = null;
+
+  public Phi(int number, int register, BasicBlock block, MoveType type, DebugLocalInfo local) {
+    super(number, register, type, local == null ? null : new DebugInfo(local, null));
+    this.block = block;
+    block.addPhi(this);
+  }
+
+  @Override
+  public boolean isPhi() {
+    return true;
+  }
+
+  @Override
+  public Phi asPhi() {
+    return this;
+  }
+
+  public BasicBlock getBlock() {
+    return block;
+  }
+
+  public void addOperands(IRBuilder builder) {
+    // Phi operands are only filled in once to complete the phi. Some phis are incomplete for a
+    // period of time to break cycles. When the cycle has been resolved they are completed
+    // exactly once by adding the operands.
+    assert operands.isEmpty();
+    boolean canBeNull = false;
+    for (BasicBlock pred : block.getPredecessors()) {
+      EdgeType edgeType = pred.getEdgeType(block);
+      // Since this read has been delayed we must provide the local info for the value.
+      Value operand = builder.readRegister(
+          getOriginalRegister(), pred, edgeType, type, getLocalInfo());
+      canBeNull |= operand.canBeNull();
+      appendOperand(operand);
+    }
+    if (!canBeNull) {
+      markNeverNull();
+    }
+    removeTrivialPhi();
+  }
+
+  public void addOperands(List<Value> operands) {
+    // Phi operands are only filled in once to complete the phi. Some phis are incomplete for a
+    // period of time to break cycles. When the cycle has been resolved they are completed
+    // exactly once by adding the operands.
+    assert this.operands.isEmpty();
+    boolean canBeNull = false;
+    for (Value operand : operands) {
+      canBeNull |= operand.canBeNull();
+      appendOperand(operand);
+    }
+    if (!canBeNull) {
+      markNeverNull();
+    }
+    removeTrivialPhi();
+  }
+
+  private void appendOperand(Value operand) {
+    operands.add(operand);
+    operand.addPhiUser(this);
+  }
+
+  public Value getOperand(int predIndex) {
+    return operands.get(predIndex);
+  }
+
+  public List<Value> getOperands() {
+    return operands;
+  }
+
+  public void removeOperand(int index) {
+    operands.get(index).removePhiUser(this);
+    operands.remove(index);
+  }
+
+  public void removeOperandsByIndex(List<Integer> operandsToRemove) {
+    if (operandsToRemove.isEmpty()) {
+      return;
+    }
+    List<Value> copy = new ArrayList<>(operands);
+    operands.clear();
+    int current = 0;
+    for (int i : operandsToRemove) {
+      operands.addAll(copy.subList(current, i));
+      copy.get(i).removePhiUser(this);
+      current = i + 1;
+    }
+    operands.addAll(copy.subList(current, copy.size()));
+  }
+
+  public void replace(int predIndex, Value newValue) {
+    Value current = operands.get(predIndex);
+    operands.set(predIndex, newValue);
+    newValue.addPhiUser(this);
+    current.removePhiUser(this);
+  }
+
+  // Removing the phi user from the current value leads to concurrent modification errors
+  // during trivial phi elimination. It is safe to not remove the phi user from current
+  // since current will be unreachable after trivial phi elimination.
+  // TODO(ager): can we unify the these replace methods and avoid the concurrent modification
+  // issue?
+  private void replaceTrivialPhi(Value current, Value newValue) {
+    for (int i = 0; i < operands.size(); i++) {
+      if (operands.get(i) == current) {
+        operands.set(i, newValue);
+        newValue.addPhiUser(this);
+      }
+    }
+  }
+
+  public boolean isTrivialPhi() {
+    Value same = null;
+    for (Value op : operands) {
+      if (op == same || op == this) {
+        // Have only seen one value other than this.
+        continue;
+      }
+      if (same != null) {
+        // Merged at least two values and is therefore not trivial.
+        return false;
+      }
+      same = op;
+    }
+    return true;
+  }
+
+  public void removeTrivialPhi() {
+    Value same = null;
+    for (Value op : operands) {
+      if (op == same || op == this) {
+        // Have only seen one value other than this.
+        continue;
+      }
+      if (same != null) {
+        // Merged at least two values and is therefore not trivial.
+        assert !isTrivialPhi();
+        return;
+      }
+      same = op;
+    }
+    assert isTrivialPhi();
+    if (same == null) {
+      same = Value.UNDEFINED;
+    }
+    // Removing this phi, so get rid of it as a phi user from all of the operands to avoid
+    // recursively getting back here with the same phi. If the phi has itself as an operand
+    // that also removes the self-reference.
+    for (Value op : operands) {
+      op.removePhiUser(this);
+    }
+    // Replace this phi with the unique value in all users.
+    for (Instruction user : uniqueUsers()) {
+      user.replacePhi(this, same);
+    }
+    for (Phi user : uniquePhiUsers()) {
+      user.replaceTrivialPhi(this, same);
+    }
+    if (debugUsers() != null) {
+      for (Instruction user : debugUsers()) {
+        if (this == user.getPreviousLocalValue()) {
+          if (same.getDebugInfo() == null) {
+            user.replacePreviousLocalValue(null);
+          } else {
+            user.replacePreviousLocalValue(same);
+            same.addDebugUser(user);
+          }
+        }
+      }
+    }
+    // If IR construction is taking place, update the definition users.
+    if (definitionUsers != null) {
+      for (Map<Integer, Value> user : definitionUsers) {
+        for (Map.Entry<Integer, Value> entry : user.entrySet()) {
+          if (entry.getValue() == this) {
+            entry.setValue(same);
+            if (same.isPhi()) {
+              same.asPhi().addDefinitionsUser(user);
+            }
+          }
+        }
+      }
+    }
+    // Try to simplify phi users that might now have become trivial.
+    for (Phi user : uniquePhiUsers()) {
+      user.removeTrivialPhi();
+    }
+    // Get rid of the phi itself.
+    block.removePhi(this);
+  }
+
+  public String printPhi() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("v");
+    builder.append(number);
+    builder.append(" <- phi");
+    StringUtils.append(builder, ListUtils.map(operands, (Value operand) -> "v" + operand.number));
+    return builder.toString();
+  }
+
+  public void print(CfgPrinter printer) {
+    int uses = numberOfPhiUsers() + numberOfUsers();
+    printer
+        .print("0 ")                 // bci
+        .append(uses)                // use
+        .append(" v").append(number) // tid
+        .append(" Phi");
+    for (Value operand : operands) {
+      printer.append(" v").append(operand.number);
+    }
+  }
+
+  public void addDefinitionsUser(Map<Integer, Value> currentDefinitions) {
+    definitionUsers.add(currentDefinitions);
+  }
+
+  public void removeDefinitionsUser(Map<Integer, Value> currentDefinitions) {
+    definitionUsers.remove(currentDefinitions);
+  }
+
+  public void clearDefinitionsUsers() {
+    definitionUsers = null;
+  }
+
+  private boolean isSingleConstZero(Value value) {
+    return value.definition != null && value.definition.isConstNumber() &&
+        value.definition.asConstNumber().isZero() &&
+        value.outType() == MoveType.SINGLE;
+  }
+
+  private MoveType computeOutType(Set<Phi> active) {
+    if (outType != null) {
+      return outType;
+    }
+    active.add(this);
+    // Go through non-phi operands first to determine if we have an operand that dictates the type.
+    for (Value operand : operands) {
+      // Since a constant zero can be either an integer or an Object (null) we skip them
+      // when computing types and rely on other operands to specify the actual type.
+      if (!operand.isPhi() && !isSingleConstZero(operand)) {
+        return operand.outType();
+      }
+    }
+    // We did not find a non-phi operand that dictates the type. Recurse on phi arguments.
+    for (Value operand : operands) {
+      if (operand.isPhi() && !active.contains(operand)) {
+        MoveType phiType = operand.asPhi().computeOutType(active);
+        // TODO(zerny): If we had a CONST_ZERO type element, we could often avoid going through
+        // all phis. We would only have to recurse until we got a non CONST_ZERO out type.
+        if (phiType != MoveType.SINGLE) {
+          return phiType;
+        }
+      }
+    }
+    // All operands were the constant zero or phis with out type SINGLE and the out type is either
+    // object or single depending on the use. Since all inputs have out type SINGLE it is safe to
+    // return MoveType.SINGLE here.
+    assert type == MoveType.SINGLE || type == MoveType.OBJECT;
+    return MoveType.SINGLE;
+  }
+
+  @Override
+  public MoveType outType() {
+    if (outType != null) {
+      return outType;
+    }
+    return computeOutType(new HashSet<>());
+  }
+
+  @Override
+  public boolean isConstant() {
+    return false;
+  }
+
+  public boolean needsRegister() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Rem.java b/src/main/java/com/android/tools/r8/ir/code/Rem.java
new file mode 100644
index 0000000..6521ae7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Rem.java
@@ -0,0 +1,118 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.RemDouble;
+import com.android.tools.r8.code.RemDouble2Addr;
+import com.android.tools.r8.code.RemFloat;
+import com.android.tools.r8.code.RemFloat2Addr;
+import com.android.tools.r8.code.RemInt;
+import com.android.tools.r8.code.RemInt2Addr;
+import com.android.tools.r8.code.RemIntLit16;
+import com.android.tools.r8.code.RemIntLit8;
+import com.android.tools.r8.code.RemLong;
+import com.android.tools.r8.code.RemLong2Addr;
+
+public class Rem extends ArithmeticBinop {
+
+  public Rem(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isRem() {
+    return true;
+  }
+
+  @Override
+  public Rem asRem() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new RemInt(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    return new RemLong(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+    return new RemFloat(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+    return new RemDouble(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new RemInt2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new RemLong2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+    return new RemFloat2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+    return new RemDouble2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new RemIntLit8(dest, left, constant);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new RemIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asRem().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asRem().type.ordinal();
+  }
+
+  @Override
+  public boolean canBeFolded() {
+    return super.canBeFolded() && !rightValue().getConstInstruction().asConstNumber().isZero();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left % right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left % right;
+  }
+
+  @Override
+  float foldFloat(float left, float right) {
+    return left % right;
+  }
+
+  @Override
+  double foldDouble(double left, double right) {
+    return left % right;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return type != NumericType.DOUBLE && type != NumericType.FLOAT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
new file mode 100644
index 0000000..2819c58
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Return extends JumpInstruction {
+
+  // Need to keep track of the original return type, as a null value will have MoveType.SINGLE.
+  final private MoveType returnType;
+
+  public Return() {
+    super(null);
+    returnType = null;
+  }
+
+  public Return(Value value, MoveType returnType) {
+    super(null, value);
+    this.returnType = returnType;
+  }
+
+  public boolean isReturnVoid() {
+    return inValues.size() == 0;
+  }
+
+  public MoveType getReturnType() {
+    return returnType;
+  }
+
+  public Value returnValue() {
+    assert !isReturnVoid();
+    return inValues.get(0);
+  }
+
+  public com.android.tools.r8.code.Instruction createDexInstruction(DexBuilder builder) {
+    if (isReturnVoid()) {
+      return new ReturnVoid();
+    } else {
+      switch (returnValue().type) {
+        case OBJECT:
+          assert returnType == MoveType.OBJECT;
+          return new ReturnObject(builder.allocatedRegister(returnValue(), getNumber()));
+        case SINGLE:
+          if (returnType == MoveType.OBJECT) {
+            return new ReturnObject(builder.allocatedRegister(returnValue(), getNumber()));
+          } else {
+            assert returnType == MoveType.SINGLE;
+            return new com.android.tools.r8.code.Return(builder.allocatedRegister(returnValue(), getNumber()));
+          }
+        case WIDE:
+          assert returnType == MoveType.WIDE;
+          return new ReturnWide(builder.allocatedRegister(returnValue(), getNumber()));
+        default:
+          throw new Unreachable();
+      }
+    }
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.add(this, createDexInstruction(builder));
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    if (isReturnVoid()) {
+      return other.asReturn().isReturnVoid();
+    } else {
+      return other.asReturn().returnValue().type == returnValue().type;
+    }
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    if (isReturnVoid()) {
+      return other.asReturn().isReturnVoid() ? 0 : -1;
+    } else {
+      return returnValue().type.ordinal() - other.asReturn().returnValue().type.ordinal();
+    }
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "Return defines no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean isReturn() {
+    return true;
+  }
+
+  @Override
+  public Return asReturn() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shl.java b/src/main/java/com/android/tools/r8/ir/code/Shl.java
new file mode 100644
index 0000000..8236891
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Shl.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.ShlInt;
+import com.android.tools.r8.code.ShlInt2Addr;
+import com.android.tools.r8.code.ShlIntLit8;
+import com.android.tools.r8.code.ShlLong;
+import com.android.tools.r8.code.ShlLong2Addr;
+import com.android.tools.r8.errors.Unreachable;
+
+public class Shl extends LogicalBinop {
+
+  public Shl(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  boolean fitsInDexInstruction(Value value) {
+    // The shl instruction only has the /lit8 variant.
+    return fitsInLit8Instruction(value);
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  @Override
+  public boolean isShl() {
+    return true;
+  }
+
+  @Override
+  public Shl asShl() {
+    return this;
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new ShlInt(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    return new ShlLong(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new ShlInt2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new ShlLong2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new ShlIntLit8(dest, left, constant);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    throw new Unreachable("Unsupported instruction ShlIntLit16");
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asShl().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asShl().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left << right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left << right;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Shr.java b/src/main/java/com/android/tools/r8/ir/code/Shr.java
new file mode 100644
index 0000000..e4bd97c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Shr.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.ShrInt;
+import com.android.tools.r8.code.ShrInt2Addr;
+import com.android.tools.r8.code.ShrIntLit8;
+import com.android.tools.r8.code.ShrLong;
+import com.android.tools.r8.code.ShrLong2Addr;
+import com.android.tools.r8.errors.Unreachable;
+
+public class Shr extends LogicalBinop {
+
+  public Shr(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  boolean fitsInDexInstruction(Value value) {
+    // The shr instruction only has the /lit8 variant.
+    return fitsInLit8Instruction(value);
+  }
+
+  @Override
+  public boolean isShr() {
+    return true;
+  }
+
+  @Override
+  public Shr asShr() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new ShrInt(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    return new ShrLong(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new ShrInt2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new ShrLong2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new ShrIntLit8(dest, left, constant);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    throw new Unreachable("Unsupported instruction ShrIntLit16");
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asShr().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asShr().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left >> right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left >> right;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/SingleConstant.java b/src/main/java/com/android/tools/r8/ir/code/SingleConstant.java
new file mode 100644
index 0000000..2fc8e0b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/SingleConstant.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+public interface SingleConstant {
+
+  int decodedValue();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
new file mode 100644
index 0000000..ad340c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Sget;
+import com.android.tools.r8.code.SgetBoolean;
+import com.android.tools.r8.code.SgetByte;
+import com.android.tools.r8.code.SgetChar;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.code.SgetShort;
+import com.android.tools.r8.code.SgetWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class StaticGet extends FieldInstruction {
+
+  public StaticGet(MemberType type, Value dest, DexField field) {
+    super(type, field, dest, (Value) null);
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int dest = builder.allocatedRegister(dest(), getNumber());
+    switch (type) {
+      case SINGLE:
+        instruction = new Sget(dest, field);
+        break;
+      case WIDE:
+        instruction = new SgetWide(dest, field);
+        break;
+      case OBJECT:
+        instruction = new SgetObject(dest, field);
+        break;
+      case BOOLEAN:
+        instruction = new SgetBoolean(dest, field);
+        break;
+      case BYTE:
+        instruction = new SgetByte(dest, field);
+        break;
+      case CHAR:
+        instruction = new SgetChar(dest, field);
+        break;
+      case SHORT:
+        instruction = new SgetShort(dest, field);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    // This can cause <clinit> to run.
+    return true;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    StaticGet o = other.asStaticGet();
+    return o.field == field && o.type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    StaticGet o = other.asStaticGet();
+    int result;
+    result = field.slowCompareTo(o.field);
+    if (result != 0) {
+      return result;
+    }
+    return type.ordinal() - o.type.ordinal();
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; field: " + field.toSourceString();
+  }
+
+  @Override
+  public boolean isStaticGet() {
+    return true;
+  }
+
+  @Override
+  public StaticGet asStaticGet() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
new file mode 100644
index 0000000..e8a1fed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Sput;
+import com.android.tools.r8.code.SputBoolean;
+import com.android.tools.r8.code.SputByte;
+import com.android.tools.r8.code.SputChar;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.code.SputShort;
+import com.android.tools.r8.code.SputWide;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class StaticPut extends FieldInstruction {
+
+  public StaticPut(MemberType type, Value source, DexField field) {
+    super(type, field, null, source);
+  }
+
+  public Value inValue() {
+    assert inValues.size() == 1;
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    com.android.tools.r8.code.Instruction instruction;
+    int src = builder.allocatedRegister(inValue(), getNumber());
+    switch (type) {
+      case SINGLE:
+        instruction = new Sput(src, field);
+        break;
+      case WIDE:
+        instruction = new SputWide(src, field);
+        break;
+      case OBJECT:
+        instruction = new SputObject(src, field);
+        break;
+      case BOOLEAN:
+        instruction = new SputBoolean(src, field);
+        break;
+      case BYTE:
+        instruction = new SputByte(src, field);
+        break;
+      case CHAR:
+        instruction = new SputChar(src, field);
+        break;
+      case SHORT:
+        instruction = new SputShort(src, field);
+        break;
+      default:
+        throw new Unreachable("Unexpected type " + type);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    // This can cause <clinit> to run.
+    return true;
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "StaticPut instructions define no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    StaticPut o = other.asStaticPut();
+    return o.field == field && o.type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    StaticPut o = other.asStaticPut();
+    int result;
+    result = field.slowCompareTo(o.field);
+    if (result != 0) {
+      return result;
+    }
+    return type.ordinal() - o.type.ordinal();
+  }
+
+  @Override
+  public String toString() {
+    return super.toString() + "; field: " + field.toSourceString();
+  }
+
+  @Override
+  public boolean isStaticPut() {
+    return true;
+  }
+
+  @Override
+  public StaticPut asStaticPut() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
new file mode 100644
index 0000000..8470070
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -0,0 +1,204 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.AddIntLit16;
+import com.android.tools.r8.code.AddIntLit8;
+import com.android.tools.r8.code.RsubInt;
+import com.android.tools.r8.code.RsubIntLit8;
+import com.android.tools.r8.code.SubDouble;
+import com.android.tools.r8.code.SubDouble2Addr;
+import com.android.tools.r8.code.SubFloat;
+import com.android.tools.r8.code.SubFloat2Addr;
+import com.android.tools.r8.code.SubInt;
+import com.android.tools.r8.code.SubInt2Addr;
+import com.android.tools.r8.code.SubLong;
+import com.android.tools.r8.code.SubLong2Addr;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+
+public class Sub extends ArithmeticBinop {
+
+  public Sub(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new SubInt(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+    // the first part of the result long before reading the second part of the input longs.
+    // Therefore, there can be no overlap of the second part of an input long and the first
+    // part of the output long.
+    assert dest != left + 1;
+    assert dest != right + 1;
+    return new SubLong(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
+    return new SubFloat(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
+    return new SubDouble(dest, left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new SubInt2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new SubLong2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
+    return new SubFloat2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
+    return new SubDouble2Addr(left, right);
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    // The sub instructions with constants are rsub, and is handled below.
+    throw new Unreachable("Unsupported instruction SubIntLit8");
+  }
+
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    // The sub instructions with constants are rsub, and is handled below.
+    throw new Unreachable("Unsupported instruction SubIntLit16");
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asSub().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asSub().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left - right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left - right;
+  }
+
+  @Override
+  float foldFloat(float left, float right) {
+    return left - right;
+  }
+
+  @Override
+  double foldDouble(double left, double right) {
+    return left - right;
+  }
+
+  boolean negativeFitsInDexInstruction(Value value) {
+    return type == NumericType.INT &&
+        value.isConstant() &&
+        value.getConstInstruction().asConstNumber().negativeIs16Bit();
+  }
+
+  // This is overridden to give the correct value when adding the negative constant.
+  @Override
+  int maxInOutValueRegisterSize() {
+    if (!leftValue().needsRegister()) {
+      assert fitsInDexInstruction(leftValue());
+      ConstNumber left = leftValue().getConstInstruction().asConstNumber();
+      return left.is8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
+    } else if (!rightValue().needsRegister()) {
+      assert negativeFitsInDexInstruction(rightValue());
+      ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+      return right.negativeIs8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
+    }
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public boolean needsValueInRegister(Value value) {
+    if (leftValue() == rightValue()) {
+      // We cannot distinguish the the two values, so both must end up in registers no matter what.
+      return true;
+    }
+    if (value == leftValue()) {
+      // If the left value fits in the dex instruction no register is needed for that (rsub
+      // instruction).
+      return !fitsInDexInstruction(value);
+    } else {
+      assert value == rightValue();
+      // If the negative right value fits in the dex instruction no register is needed for that (add
+      // instruction with the negative value), unless the left is taking that place.
+      return !negativeFitsInDexInstruction(value) || fitsInDexInstruction(leftValue());
+    }
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    // Handle two address and non-int case through the generic arithmetic binop.
+    if (isTwoAddr(builder) || type != NumericType.INT) {
+      super.buildDex(builder);
+      return;
+    }
+
+    com.android.tools.r8.code.Instruction instruction = null;
+    if (!leftValue().needsRegister()) {
+      // Sub instructions with small left constant is emitted as rsub.
+      assert fitsInDexInstruction(leftValue());
+      ConstNumber left = leftValue().getConstInstruction().asConstNumber();
+      int right = builder.allocatedRegister(rightValue(), getNumber());
+      int dest = builder.allocatedRegister(outValue, getNumber());
+      if (left.is8Bit()) {
+        instruction = new RsubIntLit8(dest, right, left.getIntValue());
+      } else {
+        assert left.is16Bit();
+        instruction = new RsubInt(dest, right, left.getIntValue());
+      }
+    } else if (!rightValue().needsRegister()) {
+      // Sub instructions with small right constant are emitted as add of the negative constant.
+      assert negativeFitsInDexInstruction(rightValue());
+      int dest = builder.allocatedRegister(outValue, getNumber());
+      assert leftValue().needsRegister();
+      int left = builder.allocatedRegister(leftValue(), getNumber());
+      ConstNumber right = rightValue().getConstInstruction().asConstNumber();
+      if (right.negativeIs8Bit()) {
+        instruction = new AddIntLit8(dest, left, -right.getIntValue());
+      } else {
+        assert right.negativeIs16Bit();
+        instruction = new AddIntLit16(dest, left, -right.getIntValue());
+      }
+    } else {
+      assert type == NumericType.INT;
+      int left = builder.allocatedRegister(leftValue(), getNumber());
+      int right = builder.allocatedRegister(rightValue(), getNumber());
+      int dest = builder.allocatedRegister(outValue, getNumber());
+      instruction = CreateInt(dest, left, right);
+    }
+    builder.add(this, instruction);
+  }
+
+  @Override
+  public boolean isSub() {
+    return true;
+  }
+
+  @Override
+  public Sub asSub() {
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Switch.java b/src/main/java/com/android/tools/r8/ir/code/Switch.java
new file mode 100644
index 0000000..96e00f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Switch.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.Nop;
+import com.android.tools.r8.code.PackedSwitch;
+import com.android.tools.r8.code.PackedSwitchPayload;
+import com.android.tools.r8.code.SparseSwitch;
+import com.android.tools.r8.code.SparseSwitchPayload;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.utils.CfgPrinter;
+
+public class Switch extends JumpInstruction {
+
+  public enum Type {
+    PACKED, SPARSE
+  }
+
+  private final Type type;
+  private final int[] keys;
+  private final int[] targetBlockIndices;
+  private int fallthroughBlockIndex;
+
+  public Switch(
+      Type type,
+      Value value,
+      int[] keys,
+      int[] targetBlockIndices,
+      int fallthroughBlockIndex) {
+    super(null, value);
+    this.type = type;
+    this.keys = keys;
+    this.targetBlockIndices = targetBlockIndices;
+    this.fallthroughBlockIndex = fallthroughBlockIndex;
+  }
+
+  private Value value() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public boolean isSwitch() {
+    return true;
+  }
+
+  @Override
+  public Switch asSwitch() {
+    return this;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isSwitch();
+    return false;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isSwitch();
+    return 0;
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    int value = builder.allocatedRegister(value(), getNumber());
+    if (type == Type.PACKED) {
+      builder.addSwitch(this, new PackedSwitch(value));
+    } else {
+      builder.addSwitch(this, new SparseSwitch(value));
+    }
+  }
+
+  private int numberOfKeys() {
+    return targetBlockIndices.length;
+  }
+
+  public int[] targetBlockIndices() {
+    return targetBlockIndices;
+  }
+
+  @Override
+  public BasicBlock fallthroughBlock() {
+    return getBlock().getSuccessors().get(fallthroughBlockIndex);
+  }
+
+  public int getFallthroughBlockIndex() {
+    return fallthroughBlockIndex;
+  }
+
+  public void setFallthroughBlockIndex(int i) {
+    fallthroughBlockIndex = i;
+  }
+
+  public BasicBlock targetBlock(int index) {
+    return getBlock().getSuccessors().get(targetBlockIndices()[index]);
+  }
+
+  @Override
+  public void setFallthroughBlock(BasicBlock block) {
+    getBlock().getSuccessors().set(fallthroughBlockIndex, block);
+  }
+
+  public Nop buildPayload(int[] targets) {
+    if (type == Type.PACKED) {
+      return new PackedSwitchPayload(numberOfKeys(), keys[0], targets);
+    } else {
+      return new SparseSwitchPayload(numberOfKeys(), keys, targets);
+    }
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder(
+        super.toString() + " (" + (type == Type.PACKED ? "PACKED" : "SPARSE") + ")\n");
+    for (int i = 0; i < numberOfKeys(); i++) {
+      builder.append("          ");
+      if (type == Type.PACKED) {
+        builder.append(keys[0] + i);
+      } else {
+        builder.append(keys[i]);
+      }
+      builder.append(" -> ");
+      builder.append(targetBlock(i).getNumber());
+      builder.append("\n");
+    }
+    builder.append("          F -> ");
+    builder.append(fallthroughBlock().getNumber());
+    return builder.toString();
+  }
+
+  @Override
+  public void print(CfgPrinter printer) {
+    super.print(printer);
+    for (int index : targetBlockIndices) {
+      BasicBlock target = getBlock().getSuccessors().get(index);
+      printer.append(" B").append(target.getNumber());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
new file mode 100644
index 0000000..e0ba866
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+public class Throw extends JumpInstruction {
+
+  public Throw(Value exception) {
+    super(null, exception);
+  }
+
+  public Value exception() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public void buildDex(DexBuilder builder) {
+    builder.add(this, new com.android.tools.r8.code.Throw(builder.allocatedRegister(exception(), getNumber())));
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U8BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    assert false : "Throw defines no values.";
+    return 0;
+  }
+
+  @Override
+  public boolean instructionTypeCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    assert other.isThrow();
+    return true;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    assert other.isThrow();
+    return 0;
+  }
+
+  @Override
+  public boolean isThrow() {
+    return true;
+  }
+
+  @Override
+  public Throw asThrow() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Unop.java b/src/main/java/com/android/tools/r8/ir/code/Unop.java
new file mode 100644
index 0000000..ff1744a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Unop.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+
+abstract public class Unop extends Instruction {
+
+  public Unop(Value dest, Value source) {
+    super(dest, source);
+  }
+
+  public Value dest() {
+    return outValue;
+  }
+
+  public Value source() {
+    return inValues.get(0);
+  }
+
+  @Override
+  public int maxInValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public int maxOutValueRegister() {
+    return Constants.U4BIT_MAX;
+  }
+
+  @Override
+  public boolean isUnop() {
+    return true;
+  }
+
+  @Override
+  public Unop asUnop() {
+    return this;
+  }
+
+  @Override
+  public InliningConstraint inliningConstraint(AppInfo info, DexType holder) {
+    return InliningConstraint.ALWAYS;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Ushr.java b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
new file mode 100644
index 0000000..d90ce4a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Ushr.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.UshrInt;
+import com.android.tools.r8.code.UshrInt2Addr;
+import com.android.tools.r8.code.UshrIntLit8;
+import com.android.tools.r8.code.UshrLong;
+import com.android.tools.r8.code.UshrLong2Addr;
+import com.android.tools.r8.errors.Unreachable;
+
+public class Ushr extends LogicalBinop {
+
+  public Ushr(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  boolean fitsInDexInstruction(Value value) {
+    // The ushr instruction only has the /lit8 variant.
+    return fitsInLit8Instruction(value);
+  }
+
+  @Override
+  public boolean isUshr() {
+    return true;
+  }
+
+  @Override
+  public Ushr asUshr() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return false;
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new UshrInt(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    return new UshrLong(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new UshrInt2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new UshrLong2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new UshrIntLit8(dest, left, constant);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    throw new Unreachable("Unsupported instruction ShrIntLit16");
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asUshr().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asUshr().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left >>> right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left >>> right;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
new file mode 100644
index 0000000..2b7c647
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -0,0 +1,530 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.regalloc.LiveIntervals;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.LongInterval;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class Value {
+
+  /**
+   * Immutable view of the debug info associated with an SSA value.
+   *
+   * Used during IR building and to construct replacement values.
+   */
+  public static class DebugInfo {
+    private final DebugLocalInfo local;
+    private final Value previousLocalValue;
+
+    public DebugInfo(DebugLocalInfo local, Value previousLocalValue) {
+      assert local != null;
+      this.local = local;
+      this.previousLocalValue = previousLocalValue;
+    }
+  }
+
+  // Actual internal data for the debug information of locals.
+  // This is wrapped in a class to avoid multiple pointers in the value structure.
+  private static class DebugData {
+    final DebugLocalInfo local;
+    Value previousLocalValue;
+    Set<Instruction> debugUsers = new HashSet<>();
+    List<DebugLocalRead> localStarts = new ArrayList<>();
+    List<DebugLocalRead> localEnds = new ArrayList<>();
+
+    DebugData(DebugInfo info) {
+      this(info.local, info.previousLocalValue);
+    }
+
+    DebugData(DebugLocalInfo local, Value previousLocalValue) {
+      assert previousLocalValue == null || !previousLocalValue.isUninitializedLocal();
+      this.local = local;
+      this.previousLocalValue = previousLocalValue;
+    }
+  }
+
+  public static final Value UNDEFINED = new Value(-1, -1, MoveType.OBJECT, null);
+
+  protected final int number;
+  protected MoveType type;
+  private int originalRegister;
+  public Instruction definition = null;
+  private LinkedList<Instruction> users = new LinkedList<>();
+  private Set<Instruction> uniqueUsers = null;
+  private LinkedList<Phi> phiUsers = new LinkedList<>();
+  private Set<Phi> uniquePhiUsers = null;
+  private Value nextConsecutive = null;
+  private Value previousConsecutive = null;
+  private LiveIntervals liveIntervals;
+  private int needsRegister = -1;
+  private boolean neverNull = false;
+  private boolean isThis = false;
+  private boolean isArgument = false;
+  private LongInterval valueRange;
+  private DebugData debugData;
+
+  public Value(int number, int originalRegister, MoveType type, DebugInfo debugInfo) {
+    this.number = number;
+    this.type = type;
+    this.originalRegister = originalRegister;
+    this.debugData = debugInfo == null ? null : new DebugData(debugInfo);
+  }
+
+  public boolean isFixedRegisterValue() {
+    return false;
+  }
+
+  public FixedRegisterValue asFixedRegisterValue() {
+    return null;
+  }
+
+  public int getNumber() {
+    return number;
+  }
+
+  public int getOriginalRegister() {
+    return originalRegister;
+  }
+
+  public int requiredRegisters() {
+    return type.requiredRegisters();
+  }
+
+  public DebugInfo getDebugInfo() {
+    return debugData == null ? null : new DebugInfo(debugData.local, debugData.previousLocalValue);
+  }
+
+  public DebugLocalInfo getLocalInfo() {
+    return debugData == null ? null : debugData.local;
+  }
+
+  public Value getPreviousLocalValue() {
+    return debugData == null ? null : debugData.previousLocalValue;
+  }
+
+  public void replacePreviousLocalValue(Value value) {
+    if (value == null || value.isUninitializedLocal()) {
+      debugData.previousLocalValue = null;
+    } else {
+      debugData.previousLocalValue = value;
+    }
+  }
+
+  public List<DebugLocalRead> getDebugLocalStarts() {
+    return debugData.localStarts;
+  }
+
+  public List<DebugLocalRead> getDebugLocalEnds() {
+    return debugData.localEnds;
+  }
+
+  public void addDebugLocalStart(DebugLocalRead start) {
+    assert start != null;
+    debugData.localStarts.add(start);
+  }
+
+  public void addDebugLocalEnd(DebugLocalRead end) {
+    assert end != null;
+    debugData.localEnds.add(end);
+  }
+
+  public void linkTo(Value other) {
+    assert nextConsecutive == null || nextConsecutive == other;
+    assert other.previousConsecutive == null || other.previousConsecutive == this;
+    other.previousConsecutive = this;
+    nextConsecutive = other;
+  }
+
+  public void replaceLink(Value newArgument) {
+    assert isLinked();
+    if (previousConsecutive != null) {
+      previousConsecutive.nextConsecutive = newArgument;
+      newArgument.previousConsecutive = previousConsecutive;
+      previousConsecutive = null;
+    }
+    if (nextConsecutive != null) {
+      nextConsecutive.previousConsecutive = newArgument;
+      newArgument.nextConsecutive = nextConsecutive;
+      nextConsecutive = null;
+    }
+  }
+
+  public boolean isLinked() {
+    return nextConsecutive != null || previousConsecutive != null;
+  }
+
+  public Value getStartOfConsecutive() {
+    Value current = this;
+    while (current.getPreviousConsecutive() != null) {
+      current = current.getPreviousConsecutive();
+    }
+    return current;
+  }
+
+  public Value getNextConsecutive() {
+    return nextConsecutive;
+  }
+
+  public Value getPreviousConsecutive() {
+    return previousConsecutive;
+  }
+
+  public Set<Instruction> uniqueUsers() {
+    if (uniqueUsers != null) {
+      return uniqueUsers;
+    }
+    return uniqueUsers = ImmutableSet.copyOf(users);
+  }
+
+  public Set<Phi> uniquePhiUsers() {
+    if (uniquePhiUsers != null) {
+      return uniquePhiUsers;
+    }
+    return uniquePhiUsers = ImmutableSet.copyOf(phiUsers);
+  }
+
+  public Set<Instruction> debugUsers() {
+    if (debugData == null) {
+      return null;
+    }
+    return Collections.unmodifiableSet(debugData.debugUsers);
+  }
+
+  public int numberOfUsers() {
+    int size = users.size();
+    if (size <= 1) {
+      return size;
+    }
+    return uniqueUsers().size();
+  }
+
+  public int numberOfPhiUsers() {
+    int size = phiUsers.size();
+    if (size <= 1) {
+      return size;
+    }
+    return uniquePhiUsers().size();
+  }
+
+  public int numberOfDebugUsers() {
+    return debugData == null ? 0 : debugData.debugUsers.size();
+  }
+
+  public int numberOfAllUsers() {
+    return numberOfUsers() + numberOfPhiUsers() + numberOfDebugUsers();
+  }
+
+  public void addUser(Instruction user) {
+    users.add(user);
+    uniqueUsers = null;
+  }
+
+  public void removeUser(Instruction user) {
+    users.remove(user);
+    uniqueUsers = null;
+  }
+
+  public void clearUsers() {
+    users.clear();
+    uniqueUsers = null;
+    phiUsers.clear();
+    uniquePhiUsers = null;
+    if (debugData != null) {
+      debugData.debugUsers.clear();
+    }
+  }
+
+  public void addPhiUser(Phi user) {
+    phiUsers.add(user);
+    uniquePhiUsers = null;
+  }
+
+  public void removePhiUser(Phi user) {
+    phiUsers.remove(user);
+    uniquePhiUsers = null;
+  }
+
+  public void addDebugUser(Instruction user) {
+    if (isUninitializedLocal()) {
+      return;
+    }
+    assert !debugData.debugUsers.contains(user);
+    debugData.debugUsers.add(user);
+  }
+
+  public boolean isUninitializedLocal() {
+    return definition != null && definition.isDebugLocalUninitialized();
+  }
+
+  public boolean isInitializedLocal() {
+    return !isUninitializedLocal();
+  }
+
+  public void removeDebugUser(Instruction user) {
+    debugData.debugUsers.remove(user);
+  }
+
+  public boolean hasUsersInfo() {
+    return users != null;
+  }
+
+  public void clearUsersInfo() {
+    users = null;
+    uniqueUsers = null;
+    phiUsers = null;
+    uniquePhiUsers = null;
+    if (debugData != null) {
+      debugData.debugUsers = null;
+    }
+  }
+
+  public void replaceUsers(Value newValue) {
+    if (this == newValue) {
+      return;
+    }
+    for (Instruction user : uniqueUsers()) {
+      user.inValues.replaceAll(v -> {
+        if (v == this) {
+          newValue.addUser(user);
+          return newValue;
+        }
+        return v;
+      });
+    }
+    for (Phi user : uniquePhiUsers()) {
+      user.getOperands().replaceAll(v -> {
+        if (v == this) {
+          newValue.addPhiUser(user);
+          return newValue;
+        }
+        return v;
+      });
+    }
+    if (debugData != null) {
+      for (Instruction user : debugUsers()) {
+        if (user.getPreviousLocalValue() == this) {
+          newValue.addDebugUser(user);
+          user.replacePreviousLocalValue(newValue);
+        }
+      }
+    }
+    clearUsers();
+  }
+
+  public void setLiveIntervals(LiveIntervals intervals) {
+    assert liveIntervals == null;
+    liveIntervals = intervals;
+  }
+
+  public LiveIntervals getLiveIntervals() {
+    return liveIntervals;
+  }
+
+  public boolean needsRegister() {
+    assert needsRegister >= 0;
+    assert !hasUsersInfo() || (needsRegister > 0) == internalComputeNeedsRegister();
+    return needsRegister > 0;
+  }
+
+  public void setNeedsRegister(boolean value) {
+    assert needsRegister == -1 || (needsRegister > 0) == value;
+    needsRegister = value ? 1 : 0;
+  }
+
+  public void computeNeedsRegister() {
+    assert needsRegister < 0;
+    setNeedsRegister(internalComputeNeedsRegister());
+  }
+
+  public boolean internalComputeNeedsRegister() {
+    if (getLocalInfo() != null) {
+      return true;
+    }
+    if (!isConstant()) {
+      return true;
+    }
+    if (numberOfPhiUsers() > 0) {
+      return true;
+    }
+    for (Instruction user : uniqueUsers()) {
+      if (user.needsValueInRegister(this)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return number;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("v");
+    builder.append(number);
+    builder.append("(");
+    if (definition != null && isConstant()) {
+      ConstNumber constNumber = getConstInstruction().asConstNumber();
+      if (constNumber.outType() == MoveType.SINGLE) {
+        builder.append((int) constNumber.getRawValue());
+      } else {
+        builder.append(constNumber.getRawValue());
+      }
+    } else {
+      if (originalRegister >= 0) {
+        builder.append("r");
+        builder.append(originalRegister);
+      } else {
+        builder.append("_");
+      }
+    }
+    if (getLocalInfo() != null) {
+      builder.append(", ").append(getLocalInfo());
+    }
+    builder.append(")");
+    if (valueRange != null) {
+      builder.append(valueRange);
+    }
+    return builder.toString();
+  }
+
+  public MoveType outType() {
+    return type;
+  }
+
+  public ConstInstruction getConstInstruction() {
+    assert definition.isOutConstant();
+    return definition.getOutConstantConstInstruction();
+  }
+
+  public boolean isConstant() {
+    return definition.isOutConstant();
+  }
+
+  public boolean isPhi() {
+    return false;
+  }
+
+  public Phi asPhi() {
+    return null;
+  }
+
+  public void markNeverNull() {
+    assert !neverNull;
+    neverNull = true;
+  }
+
+  /**
+   * Returns whether this value is know to never be <code>null</code>.
+   */
+  public boolean isNeverNull() {
+    return neverNull;
+  }
+
+  public boolean canBeNull() {
+    return !neverNull;
+  }
+
+  public void markAsArgument() {
+    assert !isArgument;
+    assert !isThis;
+    isArgument = true;
+  }
+
+  public boolean isArgument() {
+    return isArgument;
+  }
+
+  public void markAsThis() {
+    assert isArgument;
+    assert !isThis;
+    isThis = true;
+    markNeverNull();
+  }
+
+  /**
+   * Returns whether this value is known to be the receiver (this argument) in a method body.
+   * <p>
+   * For a receiver value {@link #isNeverNull()} is guarenteed to be <code>true</code> as well.
+   */
+  public boolean isThis() {
+    return isThis;
+  }
+
+  public void setValueRange(LongInterval range) {
+    valueRange = range;
+  }
+
+  public boolean hasValueRange() {
+    return valueRange != null || isConstant();
+  }
+
+  public boolean isValueInRange(int value) {
+    if (isConstant()) {
+      return value == getConstInstruction().asConstNumber().getIntValue();
+    } else {
+      return valueRange != null && valueRange.containsValue(value);
+    }
+  }
+
+  public LongInterval getValueRange() {
+    if (isConstant()) {
+      if (type == MoveType.SINGLE) {
+        int value = getConstInstruction().asConstNumber().getIntValue();
+        return new LongInterval(value, value);
+      } else {
+        assert type == MoveType.WIDE;
+        long value = getConstInstruction().asConstNumber().getLongValue();
+        return new LongInterval(value, value);
+      }
+    } else {
+      return valueRange;
+    }
+  }
+
+  public boolean isDead(InternalOptions options) {
+    // Totally unused values are trivially dead.
+    return numberOfAllUsers() == 0 || isDead(new HashSet<>(), options);
+  }
+
+  protected boolean isDead(Set<Value> active, InternalOptions options) {
+    // If the value has debug users we cannot eliminate it since it represents a value in a local
+    // variable that should be visible in the debugger.
+    if (numberOfDebugUsers() != 0) {
+      return false;
+    }
+    // This is a candidate for a dead value. Guard against looping by adding it to the set of
+    // currently active values.
+    active.add(this);
+    for (Instruction instruction : uniqueUsers()) {
+      if (!instruction.canBeDeadCode(options)) {
+        return false;
+      }
+      Value outValue = instruction.outValue();
+      // Instructions with no out value cannot be dead code by the current definition
+      // (unused out value). They typically side-effect input values or deals with control-flow.
+      assert outValue != null;
+      if (!active.contains(outValue) && !outValue.isDead(active, options)) {
+        return false;
+      }
+    }
+    for (Phi phi : uniquePhiUsers()) {
+      if (!active.contains(phi) && !phi.isDead(active, options)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueNumberGenerator.java b/src/main/java/com/android/tools/r8/ir/code/ValueNumberGenerator.java
new file mode 100644
index 0000000..ac07f11
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueNumberGenerator.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+public class ValueNumberGenerator {
+  private int nextValueNumber = 0;
+
+  public int next() {
+    return nextValueNumber++;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/WideConstant.java b/src/main/java/com/android/tools/r8/ir/code/WideConstant.java
new file mode 100644
index 0000000..e77d038
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/WideConstant.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+public interface WideConstant {
+
+  long decodedValue();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
new file mode 100644
index 0000000..dfe47bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.code.XorInt;
+import com.android.tools.r8.code.XorInt2Addr;
+import com.android.tools.r8.code.XorIntLit16;
+import com.android.tools.r8.code.XorIntLit8;
+import com.android.tools.r8.code.XorLong;
+import com.android.tools.r8.code.XorLong2Addr;
+
+public class Xor extends LogicalBinop {
+
+  public Xor(NumericType type, Value dest, Value left, Value right) {
+    super(type, dest, left, right);
+  }
+
+  @Override
+  public boolean isXor() {
+    return true;
+  }
+
+  @Override
+  public Xor asXor() {
+    return this;
+  }
+
+  @Override
+  public boolean isCommutative() {
+    return true;
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
+    return new XorInt(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
+    // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+    // the first part of the result long before reading the second part of the input longs.
+    // Therefore, there can be no overlap of the second part of an input long and the first
+    // part of the output long.
+    assert dest != left + 1;
+    assert dest != right + 1;
+    return new XorLong(dest, left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
+    return new XorInt2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
+    return new XorLong2Addr(left, right);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit8(int dest, int left, int constant) {
+    return new XorIntLit8(dest, left, constant);
+  }
+
+  @Override
+  public com.android.tools.r8.code.Instruction CreateIntLit16(int dest, int left, int constant) {
+    return new XorIntLit16(dest, left, constant);
+  }
+
+  @Override
+  public boolean identicalNonValueParts(Instruction other) {
+    return other.asXor().type == type;
+  }
+
+  @Override
+  public int compareNonValueParts(Instruction other) {
+    return type.ordinal() - other.asXor().type.ordinal();
+  }
+
+  @Override
+  int foldIntegers(int left, int right) {
+    return left ^ right;
+  }
+
+  @Override
+  long foldLongs(long left, long right) {
+    return left ^ right;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ArrayFilledDataPayloadResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/ArrayFilledDataPayloadResolver.java
new file mode 100644
index 0000000..4f91695
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ArrayFilledDataPayloadResolver.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for resolving payload information during IR construction.
+ */
+public class ArrayFilledDataPayloadResolver {
+
+  private static class PayloadData {
+    public int element_width;
+    public long size;
+    public short[] data;
+  }
+
+  private final Map<Integer, FillArrayDataPayload> unresolvedPayload = new HashMap<>();
+  private final Map<Integer, PayloadData> payloadToData = new HashMap<>();
+
+  public void addPayloadUser(FillArrayData dex) {
+    int offset = dex.getOffset();
+    int payloadOffset = offset + dex.getPayloadOffset();
+    assert !payloadToData.containsKey(payloadOffset);
+    payloadToData.put(payloadOffset, new PayloadData());
+    if (unresolvedPayload.containsKey(payloadOffset)) {
+      FillArrayDataPayload payload = unresolvedPayload.remove(payloadOffset);
+      resolve(payload);
+    }
+  }
+
+  public void resolve(FillArrayDataPayload payload) {
+    int payloadOffset = payload.getOffset();
+    PayloadData data = payloadToData.get(payloadOffset);
+    if (data == null) {
+      unresolvedPayload.put(payloadOffset, payload);
+      return;
+    }
+
+    data.element_width = payload.element_width;
+    data.size = payload.size;
+    data.data = payload.data;
+  }
+
+  public int getElementWidth(int payloadOffset) {
+    return payloadToData.get(payloadOffset).element_width;
+  }
+
+  public long getSize(int payloadOffset) {
+    return payloadToData.get(payloadOffset).size;
+  }
+
+  public short[] getData(int payloadOffset) {
+    return payloadToData.get(payloadOffset).data;
+  }
+
+  public void clear() {
+    payloadToData.clear();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
new file mode 100644
index 0000000..bd48134
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -0,0 +1,494 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Call graph representation.
+ * <p>
+ * Each node in the graph contain the methods called and the calling methods. For virtual and
+ * interface calls all potential calls from subtypes are recorded.
+ * <p>
+ * Only methods in the program - not library methods - are represented.
+ * <p>
+ * The directional edges are represented as sets of nodes in each node (called methods and callees).
+ * <p>
+ * A call from method <code>a</code> to method <code>b</code> is only present once no matter how
+ * many calls of <code>a</code> there are in <code>a</code>.
+ * <p>
+ * Recursive calls are not present.
+ */
+public class CallGraph {
+
+  private class Node {
+
+    public final DexEncodedMethod method;
+    private int invokeCount = 0;
+    private boolean isRecursive = false;
+
+    // Outgoing calls from this method.
+    public final Set<Node> calls = new LinkedHashSet<>();
+
+    // Incoming calls to this method.
+    public final Set<Node> callees = new LinkedHashSet<>();
+
+    private Node(DexEncodedMethod method) {
+      this.method = method;
+    }
+
+    public boolean isBridge() {
+      return method.accessFlags.isBridge();
+    }
+
+    private void addCalls(Node method) {
+      calls.add(method);
+    }
+
+    private void addCaller(Node method) {
+      callees.add(method);
+    }
+
+    boolean isRecursive() {
+      return isRecursive;
+    }
+
+    boolean isLeaf() {
+      return calls.isEmpty();
+    }
+
+    int callDegree() {
+      return calls.size();
+    }
+
+    @Override
+    public int hashCode() {
+      return method.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      return this == obj;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append("MethodNode for: ");
+      builder.append(method.qualifiedName());
+      builder.append(" (");
+      builder.append(calls.size());
+      builder.append(" calls, ");
+      builder.append(callees.size());
+      builder.append(" callees");
+      if (isBridge()) {
+        builder.append(", bridge");
+      }
+      if (isRecursive()) {
+        builder.append(", recursive");
+      }
+      builder.append(", invoke count " + invokeCount);
+      builder.append(").\n");
+      if (calls.size() > 0) {
+        builder.append("Calls:\n");
+        for (Node call : calls) {
+          builder.append("  ");
+          builder.append(call.method.qualifiedName());
+          builder.append("\n");
+        }
+      }
+      if (callees.size() > 0) {
+        builder.append("Callees:\n");
+        for (Node callee : callees) {
+          builder.append("  ");
+          builder.append(callee.method.qualifiedName());
+          builder.append("\n");
+        }
+      }
+      return builder.toString();
+    }
+  }
+
+  public class Leaves {
+
+    private final List<DexEncodedMethod> leaves;
+    private final boolean brokeCycles;
+
+    private Leaves(List<DexEncodedMethod> leaves, boolean brokeCycles) {
+      this.leaves = leaves;
+      this.brokeCycles = brokeCycles;
+    }
+
+    public int size() {
+      return leaves.size();
+    }
+
+    public List<DexEncodedMethod> getLeaves() {
+      return leaves;
+    }
+
+    public boolean brokeCycles() {
+      return brokeCycles;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append("Leaves: ");
+      builder.append(leaves.size());
+      builder.append("\n");
+      builder.append(brokeCycles ? "Cycles broken" : "No cycles broken");
+      return builder.toString();
+    }
+  }
+
+  private final GraphLense graphLense;
+  private final Map<DexEncodedMethod, Node> nodes = new LinkedHashMap<>();
+  private List<Node> leaves = null;
+  private Set<DexEncodedMethod> singleCallSite = Sets.newIdentityHashSet();
+  private Set<DexEncodedMethod> doubleCallSite = Sets.newIdentityHashSet();
+
+  private CallGraph(GraphLense graphLense) {
+    this.graphLense = graphLense;
+  }
+
+  public static CallGraph build(DexApplication application, AppInfoWithSubtyping appInfo,
+      GraphLense graphLense) {
+
+    CallGraph graph = new CallGraph(graphLense);
+
+    for (DexClass clazz : application.classes()) {
+      for (DexEncodedMethod method : clazz.directMethods()) {
+        Node node = graph.ensureMethodNode(method);
+        InvokeExtractor extractor = new InvokeExtractor(appInfo, graphLense, node, graph);
+        method.registerReachableDefinitions(extractor);
+      }
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        Node node = graph.ensureMethodNode(method);
+        InvokeExtractor extractor = new InvokeExtractor(appInfo, graphLense, node, graph);
+        method.registerReachableDefinitions(extractor);
+      }
+    }
+
+    assert allMethodsExists(application, graph);
+
+    graph.fillCallSiteSets(appInfo);
+    graph.fillInitialLeaves();
+    return graph;
+  }
+
+  /**
+   * Check if the <code>method</code> is guaranteed to only have a single call site.
+   * <p>
+   * For pinned methods (methods kept through Proguard keep rules) this will always answer
+   * <code>false</code>.
+   */
+  public boolean hasSingleCallSite(DexEncodedMethod method) {
+    return singleCallSite.contains(method);
+  }
+
+  public boolean hasDoubleCallSite(DexEncodedMethod method) {
+    return doubleCallSite.contains(method);
+  }
+
+  private void fillCallSiteSets(AppInfoWithSubtyping appInfo) {
+    assert singleCallSite.isEmpty();
+    AppInfoWithLiveness liveAppInfo = appInfo.withLiveness();
+    if (liveAppInfo == null) {
+      return;
+    }
+    for (Node value : nodes.values()) {
+      // For non-pinned methods we know the exact number of call sites.
+      if (!appInfo.withLiveness().pinnedItems.contains(value.method)) {
+        if (value.invokeCount == 1) {
+          singleCallSite.add(value.method);
+        } else if (value.invokeCount == 2) {
+          doubleCallSite.add(value.method);
+        }
+      }
+    }
+  }
+
+  private void fillInitialLeaves() {
+    assert leaves == null;
+    leaves = new ArrayList<>();
+    for (Node node : nodes.values()) {
+      if (node.isLeaf()) {
+        leaves.add(node);
+      }
+    }
+  }
+
+  private static boolean allMethodsExists(DexApplication application, CallGraph graph) {
+    for (DexProgramClass clazz : application.classes()) {
+      for (DexEncodedMethod method : clazz.directMethods()) {
+        assert graph.nodes.get(method) != null;
+      }
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        assert graph.nodes.get(method) != null;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Remove all leaves (nodes with an call (outgoing) degree of 0).
+   * <p>
+   *
+   * @return List of {@link DexEncodedMethod} of the leaves removed.
+   */
+  private List<DexEncodedMethod> removeLeaves() {
+    List<DexEncodedMethod> result = new ArrayList<>();
+    List<Node> newLeaves = new ArrayList<>();
+    for (Node leaf : leaves) {
+      assert nodes.containsKey(leaf.method) && nodes.get(leaf.method).calls.isEmpty();
+      remove(leaf, newLeaves);
+      result.add(leaf.method);
+    }
+    leaves = newLeaves;
+    return result;
+  }
+
+  /**
+   * Pick the next set of leaves (nodes with an call (outgoing) degree of 0) if any.
+   * <p>
+   * If the graph has no leaves then some cycles in the graph will be broken to create a set of
+   * leaves. See {@link #breakCycles} on how cycles are broken. This ensures that at least one
+   * leave is returned if the graph is not empty.
+   * <p>
+   *
+   * @return object with the leaves as a List of {@link DexEncodedMethod} and <code>boolean</code>
+   * indication of whether cycels where broken to produce leaves. <code>null</code> if the graph is
+   * empty.
+   */
+  public Leaves pickLeaves() {
+    boolean cyclesBroken = false;
+    if (isEmpty()) {
+      return null;
+    }
+    List<DexEncodedMethod> leaves = removeLeaves();
+    if (leaves.size() == 0) {
+      // The graph had no more leaves, so break cycles to construct leaves.
+      breakCycles();
+      cyclesBroken = true;
+      leaves = removeLeaves();
+    }
+    assert leaves.size() > 0;
+    for (DexEncodedMethod leaf : leaves) {
+      assert !leaf.isProcessed();
+    }
+    return new Leaves(leaves, cyclesBroken);
+  }
+
+  /**
+   * Break some cycles in the graph by removing edges.
+   * <p>
+   * This will find the lowest call (outgoing) degree in the graph. Then go through all nodes with
+   * that call (outgoing) degree, and remove all call (outgoing) edges from nodes with that call
+   * (outgoing) degree.
+   * <p>
+   * It will avoid removing edges from bridge-methods if possible.
+   */
+  private void breakCycles() {
+    // Break non bridges with degree 1.
+    int minDegree = nodes.size();
+    for (Node node : nodes.values()) {
+      // Break cycles and add all leaves created in the process.
+      if (!node.isBridge() && node.callDegree() <= 1) {
+        assert node.callDegree() == 1;
+        removeAllCalls(node);
+        leaves.add(node);
+      } else {
+        minDegree = Integer.min(minDegree, node.callDegree());
+      }
+    }
+
+    // Return if new leaves where created.
+    if (leaves.size() > 0) {
+      return;
+    }
+
+    // Break methods with the found minimum degree and add all leaves created in the process.
+    for (Node node : nodes.values()) {
+      if (node.callDegree() <= minDegree) {
+        assert node.callDegree() == minDegree;
+        removeAllCalls(node);
+        leaves.add(node);
+      }
+    }
+    assert leaves.size() > 0;
+  }
+
+  synchronized private Node ensureMethodNode(DexEncodedMethod method) {
+    return nodes.computeIfAbsent(method, k -> new Node(method));
+  }
+
+  synchronized private void addCall(Node caller, Node callee) {
+    assert caller != null;
+    assert callee != null;
+    if (caller != callee) {
+      caller.addCalls(callee);
+      callee.addCaller(caller);
+    } else {
+      caller.isRecursive = true;
+    }
+    callee.invokeCount++;
+  }
+
+  private void removeAllCalls(Node node) {
+    for (Node call : node.calls) {
+      call.callees.remove(node);
+    }
+    node.calls.clear();
+  }
+
+  private void remove(Node node, List<Node> leaves) {
+    assert node != null;
+    for (Node callee : node.callees) {
+      boolean removed = callee.calls.remove(node);
+      if (callee.isLeaf()) {
+        leaves.add(callee);
+      }
+      assert removed;
+    }
+    nodes.remove(node.method);
+  }
+
+  public boolean isEmpty() {
+    return nodes.size() == 0;
+  }
+
+  public void dump() {
+    nodes.forEach((m, n) -> System.out.println(n + "\n"));
+  }
+
+  private static class InvokeExtractor extends UseRegistry {
+
+    AppInfoWithSubtyping appInfo;
+    GraphLense graphLense;
+    Node caller;
+    CallGraph graph;
+
+    InvokeExtractor(AppInfoWithSubtyping appInfo, GraphLense graphLense, Node caller,
+        CallGraph graph) {
+      this.appInfo = appInfo;
+      this.graphLense = graphLense;
+      this.caller = caller;
+      this.graph = graph;
+    }
+
+    private void processInvoke(DexEncodedMethod source, Invoke.Type type, DexMethod method) {
+      method = graphLense.lookupMethod(method, source);
+      DexEncodedMethod definition = appInfo.lookup(type, method);
+      if (definition != null) {
+        assert !source.accessFlags.isBridge() || definition != caller.method;
+        DexType definitionHolder = definition.method.getHolder();
+        assert definitionHolder.isClassType();
+        if (!appInfo.definitionFor(definitionHolder).isLibraryClass()) {
+          Node callee = graph.ensureMethodNode(definition);
+          graph.addCall(caller, callee);
+          // For virtual and interface calls add all potential targets that could be called.
+          if (type == Type.VIRTUAL || type == Type.INTERFACE) {
+            Set<DexEncodedMethod> possibleTargets;
+            if (definitionHolder.isInterface()) {
+              possibleTargets = appInfo.lookupInterfaceTargets(definition.method);
+            } else {
+              possibleTargets = appInfo.lookupVirtualTargets(definition.method);
+            }
+            for (DexEncodedMethod possibleTarget : possibleTargets) {
+              if (possibleTarget != definition) {
+                DexClass possibleTargetClass =
+                    appInfo.definitionFor(possibleTarget.method.getHolder());
+                if (possibleTargetClass != null && !possibleTargetClass.isLibraryClass()) {
+                  callee = graph.ensureMethodNode(possibleTarget);
+                  graph.addCall(caller, callee);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    @Override
+    public boolean registerInvokeVirtual(DexMethod method) {
+      processInvoke(caller.method, Type.VIRTUAL, method);
+      return false;
+    }
+
+    @Override
+    public boolean registerInvokeDirect(DexMethod method) {
+      processInvoke(caller.method, Type.DIRECT, method);
+      return false;
+    }
+
+    @Override
+    public boolean registerInvokeStatic(DexMethod method) {
+      processInvoke(caller.method, Type.STATIC, method);
+      return false;
+    }
+
+    @Override
+    public boolean registerInvokeInterface(DexMethod method) {
+      processInvoke(caller.method, Type.INTERFACE, method);
+      return false;
+    }
+
+    @Override
+    public boolean registerInvokeSuper(DexMethod method) {
+      processInvoke(caller.method, Type.SUPER, method);
+      return false;
+    }
+
+    @Override
+    public boolean registerInstanceFieldWrite(DexField field) {
+      return false;
+    }
+
+    @Override
+    public boolean registerInstanceFieldRead(DexField field) {
+      return false;
+    }
+
+    @Override
+    public boolean registerNewInstance(DexType type) {
+      return false;
+    }
+
+    @Override
+    public boolean registerStaticFieldRead(DexField field) {
+      return false;
+    }
+
+    @Override
+    public boolean registerStaticFieldWrite(DexField field) {
+      return false;
+    }
+
+    @Override
+    public boolean registerTypeReference(DexType type) {
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
new file mode 100644
index 0000000..511f36b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -0,0 +1,1127 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.code.Format31t;
+import com.android.tools.r8.code.Goto;
+import com.android.tools.r8.code.Goto16;
+import com.android.tools.r8.code.Goto32;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.IfGe;
+import com.android.tools.r8.code.IfGez;
+import com.android.tools.r8.code.IfGt;
+import com.android.tools.r8.code.IfGtz;
+import com.android.tools.r8.code.IfLe;
+import com.android.tools.r8.code.IfLez;
+import com.android.tools.r8.code.IfLt;
+import com.android.tools.r8.code.IfLtz;
+import com.android.tools.r8.code.IfNe;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.Move16;
+import com.android.tools.r8.code.MoveFrom16;
+import com.android.tools.r8.code.MoveObject;
+import com.android.tools.r8.code.MoveObject16;
+import com.android.tools.r8.code.MoveObjectFrom16;
+import com.android.tools.r8.code.MoveWide;
+import com.android.tools.r8.code.MoveWide16;
+import com.android.tools.r8.code.MoveWideFrom16;
+import com.android.tools.r8.code.Nop;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEventBuilder;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Builder object for constructing dex bytecode from the high-level IR.
+ */
+public class DexBuilder {
+
+  // The IR representation of the code to build.
+  private final IRCode ir;
+
+  // The register allocator providing register assignments for the code to build.
+  private final RegisterAllocator registerAllocator;
+
+  // List of information about switch payloads that have to be created at the end of the
+  // dex code.
+  private final List<SwitchPayloadInfo> switchPayloadInfos = new ArrayList<>();
+
+  // List of generated FillArrayData dex instructions.
+  private final List<FillArrayDataInfo> fillArrayDataInfos = new ArrayList<>();
+
+  // First jumbo string if known.
+  private final DexString firstJumboString;
+
+  // Set of if instructions that have offsets that are so large that they cannot be encoded in
+  // the if instruction format.
+  private Set<BasicBlock> ifsNeedingRewrite = Sets.newIdentityHashSet();
+
+  // Running bounds on offsets.
+  private int maxOffset = 0;
+  private int minOffset = 0;
+
+  // Mapping from IR instructions to info for computing the dex translation. Use the
+  // getInfo/setInfo methods to access the mapping.
+  private Info[] instructionToInfo;
+
+  // The number of ingoing and outgoing argument registers for the code.
+  private int inRegisterCount = 0;
+  private int outRegisterCount = 0;
+
+  // The string reference in the code with the highest index.
+  private DexString highestSortingReferencedString = null;
+
+  BasicBlock nextBlock;
+
+  public DexBuilder(IRCode ir, RegisterAllocator registerAllocator) {
+    assert ir != null;
+    assert registerAllocator != null;
+    this.ir = ir;
+    this.registerAllocator = registerAllocator;
+    this.firstJumboString = null;
+  }
+
+  public DexBuilder(IRCode ir, RegisterAllocator registerAllocator, DexString firstJumboString) {
+    assert ir != null;
+    assert registerAllocator != null;
+    this.ir = ir;
+    this.registerAllocator = registerAllocator;
+    this.firstJumboString = firstJumboString;
+  }
+
+  private void reset() {
+    switchPayloadInfos.clear();
+    fillArrayDataInfos.clear();
+    ifsNeedingRewrite.clear();
+    maxOffset = 0;
+    minOffset = 0;
+    instructionToInfo = new Info[instructionNumberToIndex(ir.numberRemainingInstructions())];
+    inRegisterCount = 0;
+    outRegisterCount = 0;
+    highestSortingReferencedString = null;
+    nextBlock = null;
+  }
+
+  public boolean isJumboString(DexString string) {
+    if (firstJumboString == null) {
+      return false;
+    }
+    // We have to use compareTo here, as slowCompareTo will return the wrong order when minification
+    // is used.
+    return firstJumboString.compareTo(string) <= 0;
+  }
+
+  /**
+   * Build the dex instructions added to this builder.
+   *
+   * This is a two pass construction that will first compute concrete offsets and then construct
+   * the concrete instructions.
+   */
+  public DexCode build(int numberOfArguments) {
+    int numberOfInstructions;
+    int offset;
+
+    do {
+      // Rewrite ifs that are know from the previous iteration to have offsets that are too
+      // large for the if encoding.
+      rewriteIfs();
+
+      // Reset the state of the builder to start from scratch.
+      reset();
+
+      // Populate the builder info objects.
+      numberOfInstructions = 0;
+      ListIterator<BasicBlock> iterator = ir.listIterator();
+      assert iterator.hasNext();
+      BasicBlock block = iterator.next();
+      do {
+        nextBlock = iterator.hasNext() ? iterator.next() : null;
+        block.buildDex(this);
+        block = nextBlock;
+      } while (block != null);
+
+      // Compute offsets.
+      offset = 0;
+      InstructionIterator it = ir.instructionIterator();
+      while (it.hasNext()) {
+        Info info = getInfo(it.next());
+        info.setOffset(offset);
+        offset += info.computeSize(this);
+        ++numberOfInstructions;
+      }
+    } while (!ifsNeedingRewrite.isEmpty());
+
+    // Build instructions.
+    DexDebugEventBuilder debugEventBuilder = new DexDebugEventBuilder(ir.method.method);
+    List<Instruction> dexInstructions = new ArrayList<>(numberOfInstructions);
+    int instructionOffset = 0;
+    InstructionIterator instructionIterator = ir.instructionIterator();
+    int lastMoveExceptionOffset = -1;
+    while (instructionIterator.hasNext()) {
+      com.android.tools.r8.ir.code.Instruction ir = instructionIterator.next();
+      Info info = getInfo(ir);
+      int previousInstructionCount = dexInstructions.size();
+      info.addInstructions(this, dexInstructions);
+
+      if (ir.isArgument()) {
+        int register = registerAllocator.getRegisterForValue(ir.outValue(), ir.getNumber());
+        debugEventBuilder.startArgument(register, ir.getLocalInfo(), ir.outValue().isThis());
+      } else if (ir.isDebugPosition()) {
+        int pc = lastMoveExceptionOffset >= 0 ? lastMoveExceptionOffset : instructionOffset;
+        debugEventBuilder.setPosition(pc, ir.asDebugPosition());
+      }
+      lastMoveExceptionOffset = ir.isMoveException() ? instructionOffset : -1;
+
+      if (previousInstructionCount < dexInstructions.size()) {
+        while (previousInstructionCount < dexInstructions.size()) {
+          Instruction instruction = dexInstructions.get(previousInstructionCount++);
+          instruction.setOffset(instructionOffset);
+          instructionOffset += instruction.getSize();
+        }
+      }
+    }
+
+    // Compute switch payloads.
+    for (SwitchPayloadInfo switchPayloadInfo : switchPayloadInfos) {
+      // Align payloads at even addresses.
+      if (offset % 2 != 0) {
+        Nop nop = new Nop();
+        nop.setOffset(offset++);
+        dexInstructions.add(nop);
+      }
+      // Create payload and add it to the instruction stream.
+      Nop payload = createSwitchPayload(switchPayloadInfo, offset);
+      payload.setOffset(offset);
+      offset += payload.getSize();
+      dexInstructions.add(payload);
+    }
+
+    // Compute fill array data payloads.
+    for (FillArrayDataInfo info : fillArrayDataInfos) {
+      // Align payloads at even addresses.
+      if (offset % 2 != 0) {
+        Nop nop = new Nop();
+        nop.setOffset(offset++);
+        dexInstructions.add(nop);
+      }
+      // Create payload and add it to the instruction stream.
+      FillArrayDataPayload payload = info.ir.createPayload();
+      payload.setOffset(offset);
+      info.dex.setPayloadOffset(offset - info.dex.getOffset());
+      offset += payload.getSize();
+      dexInstructions.add(payload);
+    }
+
+    // Construct try-catch info.
+    TryInfo tryInfo = computeTryInfo();
+
+    // Return the dex code.
+    DexCode code = new DexCode(
+        registerAllocator.registersUsed(),
+        inRegisterCount,
+        outRegisterCount,
+        dexInstructions.toArray(new Instruction[dexInstructions.size()]), tryInfo.tries,
+        tryInfo.handlers,
+        debugEventBuilder.build(),
+        highestSortingReferencedString);
+
+    return code;
+  }
+
+  // Rewrite ifs with offsets that are too large for the if encoding. The rewriting transforms:
+  //
+  //
+  // BB0: if condition goto BB_FAR_AWAY
+  // BB1: ...
+  //
+  // to:
+  //
+  // BB0: if !condition goto BB1
+  // BB2: goto BB_FAR_AWAY
+  // BB1: ...
+  private void rewriteIfs() {
+    if (ifsNeedingRewrite.isEmpty()) {
+      return;
+    }
+    ListIterator<BasicBlock> it = ir.blocks.listIterator();
+    while (it.hasNext()) {
+      BasicBlock block = it.next();
+      if (ifsNeedingRewrite.contains(block)) {
+        If theIf = block.exit().asIf();
+        BasicBlock trueTarget = theIf.getTrueTarget();
+        BasicBlock newBlock = BasicBlock.createGotoBlock(trueTarget, ir.blocks.size());
+        theIf.setTrueTarget(newBlock);
+        theIf.invert();
+        it.add(newBlock);
+      }
+    }
+  }
+
+  private void needsIfRewriting(BasicBlock block) {
+    ifsNeedingRewrite.add(block);
+  }
+
+  public void registerStringReference(DexString string) {
+    if (highestSortingReferencedString == null
+        || string.slowCompareTo(highestSortingReferencedString) > 0) {
+      highestSortingReferencedString = string;
+    }
+  }
+
+  public void requestOutgoingRegisters(int requiredRegisterCount) {
+    if (requiredRegisterCount > outRegisterCount) {
+      outRegisterCount = requiredRegisterCount;
+    }
+  }
+
+  public int allocatedRegister(Value value, int instructionNumber) {
+    return registerAllocator.getRegisterForValue(value, instructionNumber);
+  }
+
+  public boolean argumentValueUsesHighRegister(Value value, int instructionNumber) {
+    return registerAllocator.argumentValueUsesHighRegister(value, instructionNumber);
+  }
+
+  public void addGoto(com.android.tools.r8.ir.code.Goto jump) {
+    if (jump.getTarget() != nextBlock) {
+      add(jump, new GotoInfo(jump));
+    } else {
+      addNop(jump);
+    }
+  }
+
+  public void addIf(If branch) {
+    assert nextBlock == branch.fallthroughBlock();
+    add(branch, new IfInfo(branch));
+  }
+
+  public void addMove(Move move) {
+    add(move, new MoveInfo(move));
+  }
+
+  public void addNop(com.android.tools.r8.ir.code.Instruction instruction) {
+    add(instruction, new FallThroughInfo(instruction));
+  }
+
+  public void add(com.android.tools.r8.ir.code.Instruction ir, Instruction dex) {
+    assert !ir.isGoto();
+    add(ir, new FixedSizeInfo(ir, dex));
+  }
+
+  public void add(com.android.tools.r8.ir.code.Instruction ir, Instruction[] dex) {
+    assert !ir.isGoto();
+    add(ir, new MultiFixedSizeInfo(ir, dex));
+  }
+
+  public void addSwitch(Switch s, Format31t dex) {
+    assert nextBlock == s.fallthroughBlock();
+    switchPayloadInfos.add(new SwitchPayloadInfo(s, dex));
+    add(s, dex);
+  }
+
+  public void addFillArrayData(NewArrayFilledData nafd, FillArrayData dex) {
+    fillArrayDataInfos.add(new FillArrayDataInfo(nafd, dex));
+    add(nafd, dex);
+  }
+
+  public void addArgument(Argument argument) {
+    inRegisterCount += argument.outValue().requiredRegisters();
+    add(argument, new FallThroughInfo(argument));
+  }
+
+  private void add(com.android.tools.r8.ir.code.Instruction ir, Info info) {
+    assert ir != null;
+    assert info != null;
+    assert getInfo(ir) == null;
+    info.setMinOffset(minOffset);
+    info.setMaxOffset(maxOffset);
+    minOffset += info.minSize();
+    maxOffset += info.maxSize();
+    setInfo(ir, info);
+  }
+
+  private static int instructionNumberToIndex(int instructionNumber) {
+    return instructionNumber / LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA;
+  }
+
+  // Helper used by the info objects.
+  private Info getInfo(com.android.tools.r8.ir.code.Instruction instruction) {
+    return instructionToInfo[instructionNumberToIndex(instruction.getNumber())];
+  }
+
+  private void setInfo(com.android.tools.r8.ir.code.Instruction instruction, Info info) {
+    instructionToInfo[instructionNumberToIndex(instruction.getNumber())] = info;
+  }
+
+  private Info getTargetInfo(BasicBlock block) {
+    InstructionIterator iterator = block.iterator();
+    com.android.tools.r8.ir.code.Instruction instruction = null;
+    while (iterator.hasNext()) {
+      instruction = iterator.next();
+      Info info = getInfo(instruction);
+      if (!(info instanceof FallThroughInfo)) {
+        return info;
+      }
+    }
+    assert instruction != null && instruction.isGoto();
+    return getTargetInfo(instruction.asGoto().getTarget());
+  }
+
+  // Helper for computing switch payloads.
+  private Nop createSwitchPayload(SwitchPayloadInfo info, int offset) {
+    Switch ir = info.ir;
+    // Patch the payload offset in the generated switch instruction now
+    // that the location is known.
+    info.dex.setPayloadOffset(offset - getInfo(ir).getOffset());
+    // Compute target offset for each of the keys based on the offset of the
+    // first instruction in the block that the switch goes to for that key.
+    int[] targetBlockIndices = ir.targetBlockIndices();
+    int[] targets = new int[targetBlockIndices.length];
+    for (int i = 0; i < targetBlockIndices.length; i++) {
+      BasicBlock targetBlock = ir.targetBlock(i);
+      com.android.tools.r8.ir.code.Instruction targetInstruction = targetBlock.entry();
+      targets[i] = getInfo(targetInstruction).getOffset() - getInfo(ir).getOffset();
+    }
+    return ir.buildPayload(targets);
+  }
+
+  // Helpers for computing the try items and handlers.
+
+  private TryInfo computeTryInfo() {
+    // Canonical map of handlers.
+    BiMap<CatchHandlers<BasicBlock>, Integer> canonicalHandlers = HashBiMap.create();
+    // Compute the list of try items and their handlers.
+    List<TryItem> tryItems = computeTryItems(canonicalHandlers);
+    // Compute handler sets before dex items which depend on the handler index.
+    Try[] tries = getDexTryItems(tryItems, canonicalHandlers);
+    TryHandler[] handlers = getDexTryHandlers(canonicalHandlers.inverse());
+    return new TryInfo(tries, handlers);
+  }
+
+  private List<TryItem> computeTryItems(
+      BiMap<CatchHandlers<BasicBlock>, Integer> handlerToIndex) {
+    BiMap<Integer, CatchHandlers<BasicBlock>> indexToHandler = handlerToIndex.inverse();
+    List<TryItem> tryItems = new ArrayList<>();
+    List<BasicBlock> blocksWithHandlers = new ArrayList<>();
+    TryItem currentTryItem = null;
+    // Create try items with maximal ranges to get as much coalescing as possible. After coalescing
+    // the try ranges are trimmed.
+    for (BasicBlock block : ir.blocks) {
+      CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
+      // If this assert is hit, then the block contains no instruction that can throw. This is most
+      // likely due to dead-code elimination or other optimizations that might now work on a refined
+      // notion of what can throw. If so, the trivial blocks should either be removed or their catch
+      // handlers deleted to reflect the simpler graph prior to building the dex code.
+      assert handlers.isEmpty() || block.canThrow();
+      if (!handlers.isEmpty()) {
+        if (handlerToIndex.containsKey(handlers)) {
+          handlers = indexToHandler.get(handlerToIndex.get(handlers));
+        } else {
+          handlerToIndex.put(handlers, handlerToIndex.size());
+        }
+        Info startInfo = getInfo(block.entry());
+        Info endInfo = getInfo(block.exit());
+        int start = startInfo.getOffset();
+        int end = endInfo.getOffset() + endInfo.getSize();
+        currentTryItem = new TryItem(handlers, start, end);
+        tryItems.add(currentTryItem);
+        blocksWithHandlers.add(block);
+      } else if (currentTryItem != null && !block.canThrow()) {
+        Info endInfo = getInfo(block.exit());
+        // If the block only contains a goto there might not be an info for the exit instruction.
+        if (endInfo != null) {
+          currentTryItem.end = endInfo.getOffset() + endInfo.getSize();
+        }
+      } else {
+        currentTryItem = null;
+      }
+    }
+
+    // If there are no try items it is trivially coalesced.
+    if (tryItems.isEmpty()) {
+      return tryItems;
+    }
+
+    // Coalesce try blocks.
+    tryItems.sort(TryItem::compareTo);
+    List<TryItem> coalescedTryItems = new ArrayList<>(tryItems.size());
+    for (int i = 0; i < tryItems.size(); ) {
+      TryItem item = tryItems.get(i);
+      coalescedTryItems.add(item);
+      // Trim the range start for non-throwing instructions when starting a new range.
+      List<com.android.tools.r8.ir.code.Instruction> instructions = blocksWithHandlers.get(i).getInstructions();
+      for (com.android.tools.r8.ir.code.Instruction insn : instructions) {
+        if (insn.instructionTypeCanThrow()) {
+          item.start = getInfo(insn).getOffset();
+          break;
+        }
+      }
+      // Append all consecutive ranges that define the same handlers.
+      ++i;
+      while (i < tryItems.size()) {
+        TryItem next = tryItems.get(i);
+        if (item.end != next.start || !item.handlers.equals(next.handlers)) {
+          item.end = trimEnd(blocksWithHandlers.get(i - 1));
+          break;
+        }
+        item.end = next.end;
+        ++i;
+      }
+    }
+    // Trim the last try range.
+    int lastIndex = tryItems.size() - 1;
+    TryItem lastItem = tryItems.get(lastIndex);
+    lastItem.end = trimEnd(blocksWithHandlers.get(lastIndex));
+    return coalescedTryItems;
+  }
+
+  private int trimEnd(BasicBlock block) {
+    // Trim the range end for non-throwing instructions when end has been computed.
+    List<com.android.tools.r8.ir.code.Instruction> instructions = block.getInstructions();
+    for (com.android.tools.r8.ir.code.Instruction insn : Lists.reverse(instructions)) {
+      if (insn.instructionTypeCanThrow()) {
+        Info info = getInfo(insn);
+        return info.getOffset() + info.getSize();
+      }
+    }
+    throw new Unreachable("Expected to find a possibly throwing instruction");
+  }
+
+  private static Try[] getDexTryItems(List<TryItem> tryItems,
+      Map<CatchHandlers<BasicBlock>, Integer> catchHandlers) {
+    Try[] tries = new Try[tryItems.size()];
+    for (int i = 0; i < tries.length; ++i) {
+      TryItem item = tryItems.get(i);
+      Try dexTry = new Try(item.start, item.end - item.start, -1);
+      dexTry.handlerIndex = catchHandlers.get(item.handlers);
+      tries[i] = dexTry;
+    }
+    return tries;
+  }
+
+  private TryHandler[] getDexTryHandlers(Map<Integer, CatchHandlers<BasicBlock>> catchHandlers) {
+    TryHandler[] handlers = new TryHandler[catchHandlers.size()];
+    for (int j = 0; j < catchHandlers.size(); j++) {
+      CatchHandlers<BasicBlock> handlerGroup = catchHandlers.get(j);
+      int catchAllOffset = TryHandler.NO_HANDLER;
+      List<TypeAddrPair> pairs = new ArrayList<>();
+      for (int i = 0; i < handlerGroup.getGuards().size(); i++) {
+        DexType type = handlerGroup.getGuards().get(i);
+        BasicBlock target = handlerGroup.getAllTargets().get(i);
+        int targetOffset = getInfo(target.entry()).getOffset();
+        if (type == DexItemFactory.catchAllType) {
+          assert i == handlerGroup.getGuards().size() - 1;
+          catchAllOffset = targetOffset;
+        } else {
+          pairs.add(new TypeAddrPair(type, targetOffset, -1));
+        }
+      }
+      TypeAddrPair[] pairsArray = pairs.toArray(new TypeAddrPair[pairs.size()]);
+      handlers[j] = new TryHandler(pairsArray, catchAllOffset);
+    }
+    return handlers;
+  }
+
+  // Dex instruction wrapper with information to compute instruction sizes and offsets for jumps.
+  private static abstract class Info {
+
+    private final com.android.tools.r8.ir.code.Instruction ir;
+    // Concrete final offset of the instruction.
+    private int offset = -1;
+    // Lower and upper bound of the final offset.
+    private int minOffset = -1;
+    private int maxOffset = -1;
+
+    public Info(com.android.tools.r8.ir.code.Instruction ir) {
+      assert ir != null;
+      this.ir = ir;
+    }
+
+    // Computes the final size of the instruction.
+    // All instruction offsets up-to and including this instruction will be defined at this point.
+    public abstract int computeSize(DexBuilder builder);
+
+    // Materialize the actual construction.
+    // All instruction offsets are known at this point.
+    public abstract void addInstructions(DexBuilder builder, List<Instruction> instructions);
+
+    // Lower bound on the size of the instruction.
+    public abstract int minSize();
+
+    // Upper bound on the size of the instruction.
+    public abstract int maxSize();
+
+    public abstract int getSize();
+
+    public int getOffset() {
+      assert offset >= 0 : this;
+      return offset;
+    }
+
+    public void setOffset(int offset) {
+      assert offset >= 0;
+      this.offset = offset;
+    }
+
+    public int getMinOffset() {
+      assert minOffset >= 0;
+      return minOffset;
+    }
+
+    public void setMinOffset(int minOffset) {
+      assert minOffset >= 0;
+      this.minOffset = minOffset;
+    }
+
+    public int getMaxOffset() {
+      assert maxOffset >= 0;
+      return maxOffset;
+    }
+
+    public void setMaxOffset(int maxOffset) {
+      assert maxOffset >= 0;
+      this.maxOffset = maxOffset;
+    }
+
+    public com.android.tools.r8.ir.code.Instruction getIR() {
+      return ir;
+    }
+  }
+
+  private static class FixedSizeInfo extends Info {
+
+    private Instruction instruction;
+
+    public FixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, Instruction instruction) {
+      super(ir);
+      this.instruction = instruction;
+    }
+
+    @Override
+    public int getSize() {
+      return instruction.getSize();
+    }
+
+    @Override
+    public int minSize() {
+      return instruction.getSize();
+    }
+
+    @Override
+    public int maxSize() {
+      return instruction.getSize();
+    }
+
+    @Override
+    public int computeSize(DexBuilder builder) {
+      instruction.setOffset(getOffset()); // for better printing of the dex code.
+      return instruction.getSize();
+    }
+
+    @Override
+    public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+      instructions.add(instruction);
+    }
+  }
+
+  private static class MultiFixedSizeInfo extends Info {
+
+    private Instruction[] instructions;
+    private final int size;
+
+    public MultiFixedSizeInfo(com.android.tools.r8.ir.code.Instruction ir, Instruction[] instructions) {
+      super(ir);
+      this.instructions = instructions;
+      int size = 0;
+      for (Instruction instruction : instructions) {
+        size += instruction.getSize();
+      }
+      this.size = size;
+    }
+
+    @Override
+    public int computeSize(DexBuilder builder) {
+      return size;
+    }
+
+    @Override
+    public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+      int offset = getOffset();
+      for (Instruction instruction : this.instructions) {
+        instructions.add(instruction);
+        instruction.setOffset(offset);
+        offset += instruction.getSize();
+      }
+    }
+
+    @Override
+    public int minSize() {
+      return size;
+    }
+
+    @Override
+    public int maxSize() {
+      return size;
+    }
+
+    @Override
+    public int getSize() {
+      return size;
+    }
+  }
+
+  private static class FallThroughInfo extends Info {
+
+    public FallThroughInfo(com.android.tools.r8.ir.code.Instruction ir) {
+      super(ir);
+    }
+
+    @Override
+    public int getSize() {
+      return 0;
+    }
+
+    @Override
+    public int computeSize(DexBuilder builder) {
+      return 0;
+    }
+
+    @Override
+    public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+    }
+
+    @Override
+    public int minSize() {
+      return 0;
+    }
+
+    @Override
+    public int maxSize() {
+      return 0;
+    }
+  }
+
+  private static class GotoInfo extends Info {
+
+    private int size = -1;
+
+    public GotoInfo(com.android.tools.r8.ir.code.Goto jump) {
+      super(jump);
+    }
+
+    private com.android.tools.r8.ir.code.Goto getJump() {
+      return (com.android.tools.r8.ir.code.Goto) getIR();
+    }
+
+    @Override
+    public int getSize() {
+      assert size > 0;
+      return size;
+    }
+
+    @Override
+    public int minSize() {
+      assert new Goto(42).getSize() == 1;
+      return 1;
+    }
+
+    @Override
+    public int maxSize() {
+      assert new Goto32(0).getSize() == 3;
+      return 3;
+    }
+
+    @Override
+    public int computeSize(DexBuilder builder) {
+      assert size < 0;
+      com.android.tools.r8.ir.code.Goto jump = getJump();
+      Info targetInfo = builder.getTargetInfo(jump.getTarget());
+      // Trivial loop will be emitted as: nop & goto -1
+      if (jump == targetInfo.getIR()) {
+        size = 2;
+        return size;
+      }
+      int maxOffset = getMaxOffset();
+      int maxTargetOffset = targetInfo.getMaxOffset();
+      int delta;
+      if (maxTargetOffset < maxOffset) {
+        // Backward branch: compute exact size (the target offset is set).
+        delta = getOffset() - targetInfo.getOffset();
+      } else {
+        // Forward branch: over estimate the distance, but take into account the sizes
+        // of instructions generated so far. That way the over estimation is only for the
+        // instructions between this one and the target.
+        int maxOverEstimation = maxOffset - getOffset();
+        delta = (maxTargetOffset - maxOverEstimation) - getOffset();
+      }
+      if (delta <= Byte.MAX_VALUE) {
+        size = 1;
+      } else if (delta <= Short.MAX_VALUE) {
+        size = 2;
+      } else {
+        size = 3;
+      }
+      if (targetInfo.getIR().isReturn()) {
+        // Set the size to the min of the size of the return and the size of the goto. When
+        // adding instructions, we use the return if the computed size matches the size of the
+        // return.
+        size = Math.min(targetInfo.getSize(), size);
+      }
+      return size;
+    }
+
+    @Override
+    public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+      com.android.tools.r8.ir.code.Goto jump = getJump();
+      int source = builder.getInfo(jump).getOffset();
+      Info targetInfo = builder.getTargetInfo(jump.getTarget());
+      int relativeOffset = targetInfo.getOffset() - source;
+      // We should never generate a goto to the following instruction or two consecutive returns.
+      // TODO(b/34726595): We might have a goto to the following instruction if we fail to DCE.
+      // assert relativeOffset != size;
+      Instruction dex;
+      // Emit a return if the target is a return and the size of the return is the computed
+      // size of this instruction.
+      if (targetInfo.getIR().isReturn() && size == targetInfo.getSize()) {
+        dex = targetInfo.getIR().asReturn().createDexInstruction(builder);
+      } else {
+        switch (size) {
+          case 1:
+            assert relativeOffset != 0;
+            dex = new Goto(relativeOffset);
+            break;
+          case 2:
+            if (relativeOffset == 0) {
+              Nop nop = new Nop();
+              instructions.add(nop);
+              dex = new Goto(-nop.getSize());
+            } else {
+              dex = new Goto16(relativeOffset);
+            }
+            break;
+          case 3:
+            dex = new Goto32(relativeOffset);
+            break;
+          default:
+            throw new Unreachable("Unexpected size for goto instruction: " + size);
+        }
+      }
+      dex.setOffset(getOffset()); // for better printing of the dex code.
+      instructions.add(dex);
+    }
+  }
+
+  public static class IfInfo extends Info {
+
+    private int size = -1;
+
+    public IfInfo(If branch) {
+      super(branch);
+    }
+
+    private If getBranch() {
+      return (If) getIR();
+    }
+
+    private boolean branchesToSelf(DexBuilder builder) {
+      If branch = getBranch();
+      Info trueTargetInfo = builder.getTargetInfo(branch.getTrueTarget());
+      return branch == trueTargetInfo.getIR();
+    }
+
+    private boolean offsetOutOfRange(DexBuilder builder) {
+      Info targetInfo = builder.getTargetInfo(getBranch().getTrueTarget());
+      int maxOffset = getMaxOffset();
+      int maxTargetOffset = targetInfo.getMaxOffset();
+      if (maxTargetOffset < maxOffset) {
+        return getOffset() - targetInfo.getOffset() < Short.MIN_VALUE;
+      }
+      // Forward branch: over estimate the distance, but take into account the sizes
+      // of instructions generated so far. That way the over estimation is only for the
+      // instructions between this one and the target.
+      int maxOverEstimation = maxOffset - getOffset();
+      return (maxTargetOffset - maxOverEstimation) - getOffset() > Short.MAX_VALUE;
+    }
+
+    @Override
+    public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+      If branch = getBranch();
+      int source = builder.getInfo(branch).getOffset();
+      int target = builder.getInfo(branch.getTrueTarget().entry()).getOffset();
+      int relativeOffset = target - source;
+      int register1 = builder.allocatedRegister(branch.inValues().get(0), branch.getNumber());
+      if (size == 3) {
+        assert branchesToSelf(builder);
+        Nop nop = new Nop();
+        relativeOffset -= nop.getSize();
+        instructions.add(nop);
+      }
+      assert relativeOffset != 0;
+      Instruction instruction = null;
+      if (branch.isZeroTest()) {
+        switch (getBranch().getType()) {
+          case EQ:
+            instruction = new IfEqz(register1, relativeOffset);
+            break;
+          case GE:
+            instruction = new IfGez(register1, relativeOffset);
+            break;
+          case GT:
+            instruction = new IfGtz(register1, relativeOffset);
+            break;
+          case LE:
+            instruction = new IfLez(register1, relativeOffset);
+            break;
+          case LT:
+            instruction = new IfLtz(register1, relativeOffset);
+            break;
+          case NE:
+            instruction = new IfNez(register1, relativeOffset);
+            break;
+        }
+      } else {
+        int register2 = builder.allocatedRegister(branch.inValues().get(1), branch.getNumber());
+        switch (getBranch().getType()) {
+          case EQ:
+            instruction = new IfEq(register1, register2, relativeOffset);
+            break;
+          case GE:
+            instruction = new IfGe(register1, register2, relativeOffset);
+            break;
+          case GT:
+            instruction = new IfGt(register1, register2, relativeOffset);
+            break;
+          case LE:
+            instruction = new IfLe(register1, register2, relativeOffset);
+            break;
+          case LT:
+            instruction = new IfLt(register1, register2, relativeOffset);
+            break;
+          case NE:
+            instruction = new IfNe(register1, register2, relativeOffset);
+            break;
+        }
+      }
+      instruction.setOffset(getOffset());
+      instructions.add(instruction);
+    }
+
+    @Override
+    public int computeSize(DexBuilder builder) {
+      if (offsetOutOfRange(builder)) {
+        builder.needsIfRewriting(getBranch().getBlock());
+      }
+      size = branchesToSelf(builder) ? 3 : 2;
+      return size;
+    }
+
+    @Override
+    public int minSize() {
+      return 2;
+    }
+
+    @Override
+    public int maxSize() {
+      return 3;
+    }
+
+    @Override
+    public int getSize() {
+      return size;
+    }
+  }
+
+  public static class MoveInfo extends Info {
+
+    private int size = -1;
+
+    public MoveInfo(Move move) {
+      super(move);
+    }
+
+    private Move getMove() {
+      return (Move) getIR();
+    }
+
+    @Override
+    public int computeSize(DexBuilder builder) {
+      Move move = getMove();
+      int srcRegister = builder.allocatedRegister(move.src(), move.getNumber());
+      int destRegister = builder.allocatedRegister(move.dest(), move.getNumber());
+      if (srcRegister == destRegister) {
+        size = 1;
+      } else if (srcRegister <= Constants.U4BIT_MAX && destRegister <= Constants.U4BIT_MAX) {
+        size = 1;
+      } else if (destRegister <= Constants.U8BIT_MAX) {
+        size = 2;
+      } else {
+        size = 3;
+      }
+      return size;
+    }
+
+    @Override
+    public void addInstructions(DexBuilder builder, List<Instruction> instructions) {
+      Move move = getMove();
+      int dest = builder.allocatedRegister(move.dest(), move.getNumber());
+      int src = builder.allocatedRegister(move.src(), move.getNumber());
+      Instruction instruction = null;
+      switch (size) {
+        case 1:
+          if (src == dest) {
+            instruction = new Nop();
+            break;
+          }
+          switch (move.outType()) {
+            case SINGLE:
+              instruction = new com.android.tools.r8.code.Move(dest, src);
+              break;
+            case WIDE:
+              instruction = new MoveWide(dest, src);
+              break;
+            case OBJECT:
+              instruction = new MoveObject(dest, src);
+              break;
+            default:
+              throw new Unreachable("Unexpected type: " + move.outType());
+          }
+          break;
+        case 2:
+          switch (move.outType()) {
+            case SINGLE:
+              instruction = new MoveFrom16(dest, src);
+              break;
+            case WIDE:
+              instruction = new MoveWideFrom16(dest, src);
+              break;
+            case OBJECT:
+              instruction = new MoveObjectFrom16(dest, src);
+              break;
+            default:
+              throw new Unreachable("Unexpected type: " + move.outType());
+          }
+          break;
+        case 3:
+          switch (move.outType()) {
+            case SINGLE:
+              instruction = new Move16(dest, src);
+              break;
+            case WIDE:
+              instruction = new MoveWide16(dest, src);
+              break;
+            case OBJECT:
+              instruction = new MoveObject16(dest, src);
+              break;
+            default:
+              throw new Unreachable("Unexpected type: " + move.outType());
+          }
+          break;
+      }
+      instruction.setOffset(getOffset());
+      instructions.add(instruction);
+    }
+
+    @Override
+    public int minSize() {
+      assert new Nop().getSize() == 1 && new com.android.tools.r8.code.Move(0, 0).getSize() == 1;
+      return 1;
+    }
+
+    @Override
+    public int maxSize() {
+      assert new Move16(0, 0).getSize() == 3;
+      return 3;
+    }
+
+    @Override
+    public int getSize() {
+      assert size > 0;
+      return size;
+    }
+  }
+
+  // Return-type wrapper for try-related data.
+  private static class TryInfo {
+
+    public final Try[] tries;
+    public final TryHandler[] handlers;
+
+    public TryInfo(Try[] tries, TryHandler[] handlers) {
+      this.tries = tries;
+      this.handlers = handlers;
+    }
+  }
+
+  // Helper class for coalescing ranges for try blocks.
+  private static class TryItem implements Comparable<TryItem> {
+
+    public final CatchHandlers<BasicBlock> handlers;
+    public int start;
+    public int end;
+
+    public TryItem(CatchHandlers<BasicBlock> handlers, int start, int end) {
+      this.handlers = handlers;
+      this.start = start;
+      this.end = end;
+    }
+
+    @Override
+    public int compareTo(TryItem other) {
+      return Integer.compare(start, other.start);
+    }
+  }
+
+  private static class SwitchPayloadInfo {
+
+    public final Switch ir;
+    public final Format31t dex;
+
+    public SwitchPayloadInfo(Switch ir, Format31t dex) {
+      this.ir = ir;
+      this.dex = dex;
+    }
+  }
+
+  private static class FillArrayDataInfo {
+
+    public final NewArrayFilledData ir;
+    public final FillArrayData dex;
+
+    public FillArrayDataInfo(NewArrayFilledData ir, FillArrayData dex) {
+      this.ir = ir;
+      this.dex = dex;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
new file mode 100644
index 0000000..7e47ed2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -0,0 +1,376 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.FillArrayData;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.code.FilledNewArray;
+import com.android.tools.r8.code.FilledNewArrayRange;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.code.InvokeInterface;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokePolymorphic;
+import com.android.tools.r8.code.InvokePolymorphicRange;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.code.InvokeSuper;
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultObject;
+import com.android.tools.r8.code.MoveResultWide;
+import com.android.tools.r8.code.SwitchPayload;
+import com.android.tools.r8.code.Throw;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Switch.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DexSourceCode implements SourceCode {
+
+  private final DexCode code;
+  private final DexAccessFlags accessFlags;
+  private final DexProto proto;
+
+  // Mapping from instruction offset to instruction index in the DexCode instruction array.
+  private final Map<Integer, Integer> offsetToInstructionIndex = new HashMap<>();
+
+  private final SwitchPayloadResolver switchPayloadResolver = new SwitchPayloadResolver();
+  private final ArrayFilledDataPayloadResolver arrayFilledDataPayloadResolver =
+      new ArrayFilledDataPayloadResolver();
+
+  private Try currentTryRange = null;
+  private CatchHandlers<Integer> currentCatchHandlers = null;
+  private Instruction currentDexInstruction = null;
+
+  private final List<MoveType> argumentTypes;
+
+  private List<DexDebugEntry> debugEntries = null;
+
+  public DexSourceCode(DexCode code, DexEncodedMethod method) {
+    this.code = code;
+    this.proto = method.method.proto;
+    this.accessFlags = method.accessFlags;
+    argumentTypes = computeArgumentTypes();
+    DexDebugInfo info = code.getDebugInfo();
+    if (info != null) {
+      debugEntries = info.computeEntries();
+    }
+  }
+
+  @Override
+  public boolean verifyRegister(int register) {
+    return register < code.registerSize;
+  }
+
+  @Override
+  public boolean needsPrelude() {
+    return !accessFlags.isStatic() || !argumentTypes.isEmpty();
+  }
+
+  @Override
+  public int instructionCount() {
+    return code.instructions.length;
+  }
+
+  @Override
+  public DebugLocalInfo getCurrentLocal(int register) {
+    // TODO(zerny): Support locals in the dex front-end. b/36378142
+    return null;
+  }
+
+  @Override
+  public void setUp() {
+    // Collect all payloads in the instruction stream.
+    for (int index = 0; index < code.instructions.length; index++) {
+      Instruction insn = code.instructions[index];
+      offsetToInstructionIndex.put(insn.getOffset(), index);
+      if (insn.isPayload()) {
+        if (insn.isSwitchPayload()) {
+          switchPayloadResolver.resolve((SwitchPayload) insn);
+        } else {
+          arrayFilledDataPayloadResolver.resolve((FillArrayDataPayload) insn);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void buildPrelude(IRBuilder builder) {
+    if (code.incomingRegisterSize == 0) {
+      return;
+    }
+    // Fill in the Argument instructions (incomingRegisterSize last registers) in the argument
+    // block.
+    int register = code.registerSize - code.incomingRegisterSize;
+    if (!accessFlags.isStatic()) {
+      builder.addThisArgument(register);
+      ++register;
+    }
+    for (MoveType type : argumentTypes) {
+      builder.addNonThisArgument(register, type);
+      register += type.requiredRegisters();
+    }
+  }
+
+  @Override
+  public void buildPostlude(IRBuilder builder) {
+    // Intentionally left empty. (Needed in the Java bytecode frontend for synchronization support.)
+  }
+
+  @Override
+  public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+  }
+
+  @Override
+  public void closedCurrentBlock() {
+  }
+
+  @Override
+  public void buildInstruction(IRBuilder builder, int instructionIndex) {
+    updateCurrentCatchHandlers(instructionIndex);
+    emitDebugPosition(instructionIndex, builder);
+    currentDexInstruction = code.instructions[instructionIndex];
+    currentDexInstruction.buildIR(builder);
+  }
+
+  @Override
+  public CatchHandlers<Integer> getCurrentCatchHandlers() {
+    return currentCatchHandlers;
+  }
+
+  @Override
+  public boolean verifyCurrentInstructionCanThrow() {
+    return currentDexInstruction.canThrow();
+  }
+
+  @Override
+  public boolean verifyLocalInScope(DebugLocalInfo local) {
+    return true;
+  }
+
+  private void updateCurrentCatchHandlers(int instructionIndex) {
+    Try tryRange = getTryForOffset(instructionOffset(instructionIndex));
+    if (tryRange == currentTryRange) {
+      return;
+    }
+    currentTryRange = tryRange;
+    if (tryRange == null) {
+      currentCatchHandlers = null;
+    } else {
+      currentCatchHandlers = new CatchHandlers<>(
+          getTryHandlerGuards(tryRange),
+          getTryHandlerOffsets(tryRange));
+    }
+  }
+
+  private void emitDebugPosition(int instructionIndex, IRBuilder builder) {
+    if (debugEntries == null || debugEntries.isEmpty()) {
+      return;
+    }
+    int offset = instructionOffset(instructionIndex);
+    for (DexDebugEntry entry : debugEntries) {
+      if (entry.address == offset) {
+        builder.updateCurrentDebugPosition(entry.line, entry.sourceFile);
+        return;
+      }
+      if (entry.address > offset) {
+        return;
+      }
+    }
+  }
+
+  @Override
+  public void clear() {
+    switchPayloadResolver.clear();
+    arrayFilledDataPayloadResolver.clear();
+  }
+
+  @Override
+  public int instructionIndex(int instructionOffset) {
+    return offsetToInstructionIndex.get(instructionOffset);
+  }
+
+  @Override
+  public int instructionOffset(int instructionIndex) {
+    return code.instructions[instructionIndex].getOffset();
+  }
+
+  @Override
+  public void resolveAndBuildSwitch(Type type, int value, int fallthroughOffset, int payloadOffset,
+      IRBuilder builder) {
+    builder.addSwitch(type, value, switchPayloadResolver.getKeys(payloadOffset), fallthroughOffset,
+        switchPayloadResolver.absoluteTargets(payloadOffset));
+  }
+
+  @Override
+  public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
+      IRBuilder builder) {
+    builder.addNewArrayFilledData(arrayRef,
+        arrayFilledDataPayloadResolver.getElementWidth(payloadOffset),
+        arrayFilledDataPayloadResolver.getSize(payloadOffset),
+        arrayFilledDataPayloadResolver.getData(payloadOffset));
+  }
+
+  private List<MoveType> computeArgumentTypes() {
+    List<MoveType> types = new ArrayList<>(proto.parameters.values.length);
+    String shorty = proto.shorty.toString();
+    for (int i = 1; i < proto.shorty.size; i++) {
+      MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+      types.add(moveType);
+    }
+    return types;
+  }
+
+  private boolean isInvoke(Instruction dex) {
+    return dex instanceof InvokeDirect
+        || dex instanceof InvokeDirectRange
+        || dex instanceof InvokeVirtual
+        || dex instanceof InvokeVirtualRange
+        || dex instanceof InvokeInterface
+        || dex instanceof InvokeInterfaceRange
+        || dex instanceof InvokeStatic
+        || dex instanceof InvokeStaticRange
+        || dex instanceof InvokeSuper
+        || dex instanceof InvokeSuperRange
+        || dex instanceof InvokePolymorphic
+        || dex instanceof InvokePolymorphicRange
+        || dex instanceof FilledNewArray
+        || dex instanceof FilledNewArrayRange;
+  }
+
+  private boolean isMoveResult(Instruction dex) {
+    return dex instanceof MoveResult
+        || dex instanceof MoveResultObject
+        || dex instanceof MoveResultWide;
+  }
+
+  @Override
+  public boolean traceInstruction(int index, IRBuilder builder) {
+    Instruction dex = code.instructions[index];
+    int offset = dex.getOffset();
+    assert !dex.isPayload();
+    int[] targets = dex.getTargets();
+    if (targets != Instruction.NO_TARGETS) {
+      // Check that we don't ever have instructions that can throw and have targets.
+      assert !dex.canThrow();
+      for (int relativeOffset : targets) {
+        builder.ensureSuccessorBlock(offset + relativeOffset);
+      }
+      return true;
+    }
+    if (dex.canThrow()) {
+      // If the instruction can throw and is in a try block, add edges to its catch successors.
+      Try tryRange = getTryForOffset(offset);
+      if (tryRange != null) {
+        // Ensure the block starts at the start of the try-range (don't enqueue, not a target).
+        int tryRangeStartAddress = tryRange.startAddress;
+        if (isMoveResult(code.instructions[offsetToInstructionIndex.get(tryRangeStartAddress)])) {
+          // If a handler range starts at a move result instruction it is safe to start it at
+          // the following instruction since the move-result cannot throw an exception. Doing so
+          // makes sure that we do not split an invoke and its move result instruction across
+          // two blocks.
+          ++tryRangeStartAddress;
+        }
+        builder.ensureBlockWithoutEnqueuing(tryRangeStartAddress);
+        // Edge to exceptional successors.
+        for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange)) {
+          builder.ensureSuccessorBlock(handlerOffset);
+        }
+        // If the following instruction is a move-result include it in this (the invokes) block.
+        if (index + 1 < code.instructions.length && isMoveResult(code.instructions[index + 1])) {
+          assert isInvoke(dex);
+          ++index;
+          dex = code.instructions[index];
+        }
+        // Edge to normal successor if any (fallthrough).
+        if (!(dex instanceof Throw)) {
+          builder.ensureSuccessorBlock(dex.getOffset() + dex.getSize());
+        }
+        return true;
+      }
+      // Close the block if the instruction is a throw, otherwise the block remains open.
+      return dex instanceof Throw;
+    }
+    if (dex.isSwitch()) {
+      // TODO(zerny): Remove this from block computation.
+      switchPayloadResolver.addPayloadUser(dex);
+
+      for (int target : switchPayloadResolver.absoluteTargets(dex)) {
+        builder.ensureSuccessorBlock(target);
+      }
+      builder.ensureSuccessorBlock(offset + dex.getSize());
+      return true;
+    }
+    // TODO(zerny): Remove this from block computation.
+    if (dex.hasPayload()) {
+      arrayFilledDataPayloadResolver.addPayloadUser((FillArrayData) dex);
+    }
+    // This instruction does not close the block.
+    return false;
+  }
+
+  private boolean inTryRange(Try tryItem, int offset) {
+    return tryItem.startAddress <= offset
+        && offset < tryItem.startAddress + tryItem.instructionCount;
+  }
+
+  private Try getTryForOffset(int offset) {
+    for (Try tryRange : code.tries) {
+      if (inTryRange(tryRange, offset)) {
+        return tryRange;
+      }
+    }
+    return null;
+  }
+
+  private Set<Integer> getUniqueTryHandlerOffsets(Try tryRange) {
+    return new HashSet<>(getTryHandlerOffsets(tryRange));
+  }
+
+  private List<Integer> getTryHandlerOffsets(Try tryRange) {
+    List<Integer> handlerOffsets = new ArrayList<>();
+    TryHandler handler = code.handlers[tryRange.handlerIndex];
+    for (TypeAddrPair pair : handler.pairs) {
+      handlerOffsets.add(pair.addr);
+    }
+    if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
+      handlerOffsets.add(handler.catchAllAddr);
+    }
+    return handlerOffsets;
+  }
+
+  private List<DexType> getTryHandlerGuards(Try tryRange) {
+    List<DexType> handlerGuards = new ArrayList<>();
+    TryHandler handler = code.handlers[tryRange.handlerIndex];
+    for (TypeAddrPair pair : handler.pairs) {
+      handlerGuards.add(pair.type);
+    }
+    if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
+      handlerGuards.add(DexItemFactory.catchAllType);
+    }
+    return handlerGuards;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
new file mode 100644
index 0000000..1576190
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -0,0 +1,1864 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.PackedSwitchPayload;
+import com.android.tools.r8.code.SparseSwitchPayload;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.And;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ArrayGet;
+import com.android.tools.r8.ir.code.ArrayLength;
+import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlock.EdgeType;
+import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstClass;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.DebugLocalRead;
+import com.android.tools.r8.ir.code.DebugLocalUninitialized;
+import com.android.tools.r8.ir.code.DebugLocalWrite;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.Div;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstanceOf;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.MoveException;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.Neg;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Not;
+import com.android.tools.r8.ir.code.NumberConversion;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Or;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Rem;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Shl;
+import com.android.tools.r8.ir.code.Shr;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.Ushr;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.Value.DebugInfo;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.code.Xor;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * Builder object for constructing high-level IR from dex bytecode.
+ *
+ * <p>The generated IR is in SSA form. The SSA construction is based on the paper
+ * "Simple and Efficient Construction of Static Single Assignment Form" available at
+ * http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf
+ */
+public class IRBuilder {
+
+  // SSA construction uses a worklist of basic blocks reachable from the entry and their
+  // instruction offsets.
+  private static class WorklistItem {
+
+    private final BasicBlock block;
+    private final int firstInstructionIndex;
+
+    private WorklistItem(BasicBlock block, int firstInstructionIndex) {
+      assert block != null;
+      this.block = block;
+      this.firstInstructionIndex = firstInstructionIndex;
+    }
+  }
+
+  /**
+   * Representation of lists of values that can be used as keys in maps. A list of
+   * values is equal to another list of values if it contains exactly the same values
+   * in the same order.
+   */
+  private static class ValueList {
+
+    private List<Value> values = new ArrayList<>();
+
+    /**
+     * Creates a ValueList of all the operands at the given index in the list of phis.
+     */
+    public static ValueList fromPhis(List<Phi> phis, int index) {
+      ValueList result = new ValueList();
+      for (Phi phi : phis) {
+        result.values.add(phi.getOperand(index));
+      }
+      return result;
+    }
+
+    @Override
+    public int hashCode() {
+      return values.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (!(other instanceof ValueList)) {
+        return false;
+      }
+      ValueList o = (ValueList) other;
+      if (o.values.size() != values.size()) {
+        return false;
+      }
+      for (int i = 0; i < values.size(); i++) {
+        if (values.get(i) != o.values.get(i)) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+
+  // Mapping from instruction offsets to basic-block targets.
+  private final Map<Integer, BasicBlock> targets = new HashMap<>();
+
+  // Worklist of reachable blocks.
+  private final Queue<Integer> traceBlocksWorklist = new LinkedList<>();
+
+  // Bitmap to ensure we don't process an instruction more than once.
+  private boolean[] processedInstructions = null;
+
+  // Bitmap of processed subroutine instructions. Lazily allocated off the fast-path.
+  private Set<Integer> processedSubroutineInstructions = null;
+
+  // Worklist for SSA construction.
+  private final Queue<WorklistItem> ssaWorklist = new LinkedList<>();
+
+  // Basic blocks. Added after processing from the worklist.
+  private LinkedList<BasicBlock> blocks = new LinkedList<>();
+
+  private BasicBlock currentBlock = null;
+
+  // Mappings for canonicalizing constants of a given type at IR construction time.
+  private Map<Long, ConstNumber> intConstants = new HashMap<>();
+  private Map<Long, ConstNumber> longConstants = new HashMap<>();
+  private Map<Long, ConstNumber> floatConstants = new HashMap<>();
+  private Map<Long, ConstNumber> doubleConstants = new HashMap<>();
+  private Map<Long, ConstNumber> nullConstants = new HashMap<>();
+
+  private List<BasicBlock> exitBlocks = new ArrayList<>();
+  private BasicBlock normalExitBlock;
+
+  private List<BasicBlock.Pair> needGotoToCatchBlocks = new ArrayList<>();
+
+  final private ValueNumberGenerator valueNumberGenerator;
+
+  private DebugPosition currentDebugPosition = null;
+
+  private DexEncodedMethod method;
+
+  // Source code to build IR from. Null if already built.
+  private SourceCode source;
+
+  boolean throwingInstructionInCurrentBlock = false;
+
+  private final InternalOptions options;
+
+  public IRBuilder(DexEncodedMethod method, SourceCode source, InternalOptions options) {
+    this(method, source, new ValueNumberGenerator(), options);
+  }
+
+  public IRBuilder(
+      DexEncodedMethod method,
+      SourceCode source,
+      ValueNumberGenerator valueNumberGenerator,
+      InternalOptions options) {
+    assert source != null;
+    this.method = method;
+    this.source = source;
+    this.valueNumberGenerator = valueNumberGenerator;
+    this.options = options;
+  }
+
+
+  private void addToWorklist(BasicBlock block, int firstInstructionIndex) {
+    // TODO(ager): Filter out the ones that are already in the worklist, mark bit in block?
+    if (!block.isFilled()) {
+      ssaWorklist.add(new WorklistItem(block, firstInstructionIndex));
+    }
+  }
+
+  private void setCurrentBlock(BasicBlock block) {
+    currentBlock = block;
+  }
+
+  /**
+   * Build the high-level IR in SSA form.
+   *
+   * @return The list of basic blocks. First block is the main entry.
+   */
+  public IRCode build() {
+    assert source != null;
+    source.setUp();
+
+    // Create entry block.
+    setCurrentBlock(new BasicBlock());
+
+    // If the method needs a prelude, the entry block must never be the target of other blocks.
+    if (!source.needsPrelude()) {
+      targets.put(0, currentBlock);
+    }
+
+    // Process reachable code paths starting from instruction 0.
+    processedInstructions = new boolean[source.instructionCount()];
+    traceBlocksWorklist.add(0);
+    while (!traceBlocksWorklist.isEmpty()) {
+      int startOfBlockOffset = traceBlocksWorklist.remove();
+      int startOfBlockIndex = source.instructionIndex(startOfBlockOffset);
+      // Check that the block has not been processed after being added.
+      if (isIndexProcessed(startOfBlockIndex)) {
+        continue;
+      }
+      // Process each instruction until the block is closed.
+      for (int index = startOfBlockIndex; index < source.instructionCount(); ++index) {
+        markIndexProcessed(index);
+        boolean closed = source.traceInstruction(index, this);
+        if (closed) {
+          break;
+        }
+        // If the next instruction starts a block, fall through to it.
+        if (index + 1 < source.instructionCount()) {
+          int nextOffset = source.instructionOffset(index + 1);
+          if (getTarget(nextOffset) != null) {
+            ensureSuccessorBlock(nextOffset);
+            break;
+          }
+        }
+      }
+    }
+    processedInstructions = null;
+
+    source.buildPrelude(this);
+
+    // Process normal blocks reachable from the entry block using a worklist of reachable
+    // blocks.
+    int blockNumber = 0;
+    addToWorklist(currentBlock, 0);
+    blockNumber = processWorklist(blockNumber);
+
+    // Check that the last block is closed and does not fall off the end.
+    assert currentBlock == null;
+
+    // Handle where a catch handler hits the same block as the fallthrough.
+    blockNumber = handleFallthroughToCatchBlock(blockNumber);
+
+    // Verify that we have properly filled all blocks
+    // Must be after handle-catch (which has delayed edges),
+    // but before handle-exit (which does not maintain predecessor counts).
+    assert verifyFilledPredecessors();
+
+    // If there are multiple returns create an exit block.
+    blockNumber = handleExitBlock(blockNumber);
+
+    // Clear all reaching definitions to free up memory (and avoid invalid use).
+    for (BasicBlock block : blocks) {
+      block.clearCurrentDefinitions();
+    }
+
+    // Join predecessors for which all phis have the same inputs. This avoids generating the
+    // same phi moves in multiple blocks.
+    joinPredecessorsWithIdenticalPhis();
+
+    // Split critical edges to make sure that we have a place to insert phi moves if
+    // necessary.
+    splitCriticalEdges();
+
+    // Consistency check.
+    assert phiOperandsAreConsistent();
+
+    // Package up the IR code.
+    IRCode ir = new IRCode(method, blocks, normalExitBlock, valueNumberGenerator);
+
+    // Create block order and make sure that all blocks are immediately followed by their
+    // fallthrough block if any.
+    traceBlocks(ir);
+
+    // Clear the code so we don't build multiple times.
+    source.clear();
+    clearCanonicalizationMaps();
+    source = null;
+
+    assert ir.isConsistentSSA();
+    return ir;
+  }
+
+  private void clearCanonicalizationMaps() {
+    intConstants = null;
+    longConstants = null;
+    floatConstants = null;
+    doubleConstants = null;
+    nullConstants = null;
+  }
+
+  private boolean verifyFilledPredecessors() {
+    for (BasicBlock block : blocks) {
+      assert block.verifyFilledPredecessors();
+    }
+    return true;
+  }
+
+  private int processWorklist(int blockNumber) {
+    for (WorklistItem item = ssaWorklist.poll(); item != null; item = ssaWorklist.poll()) {
+      if (item.block.isFilled()) {
+        continue;
+      }
+      setCurrentBlock(item.block);
+      blocks.add(currentBlock);
+      currentBlock.setNumber(blockNumber++);
+      // Build IR for each dex instruction in the block.
+      for (int i = item.firstInstructionIndex; i < source.instructionCount(); ++i) {
+        if (currentBlock == null) {
+          source.closedCurrentBlock();
+          break;
+        }
+        BasicBlock block = getTarget(source.instructionOffset(i));
+        if (block != null && block != currentBlock) {
+          closeCurrentBlockWithFallThrough(block);
+          source.closedCurrentBlockWithFallthrough(i);
+          addToWorklist(block, i);
+          break;
+        }
+        source.buildInstruction(this, i);
+      }
+    }
+    return blockNumber;
+  }
+
+  // Helper to resolve switch payloads and build switch instructions (dex code only).
+  public void resolveAndBuildSwitch(Switch.Type type, int value, int fallthroughOffset,
+      int payloadOffset) {
+    source.resolveAndBuildSwitch(type, value, fallthroughOffset, payloadOffset, this);
+  }
+
+  // Helper to resolve fill-array data and build new-array instructions (dex code only).
+  public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset) {
+    source.resolveAndBuildNewArrayFilledData(arrayRef, payloadOffset, this);
+  }
+
+  /**
+   * Add an (non-jump) instruction to the builder.
+   *
+   * @param ir IR instruction to add as the next instruction.
+   */
+  public void add(Instruction ir) {
+    assert !ir.isJumpInstruction();
+    addInstruction(ir);
+  }
+
+  public void addThisArgument(int register) {
+    DebugLocalInfo local = getCurrentLocal(register);
+    DebugInfo info = local == null ? null : new DebugInfo(local, null);
+    Value value = writeRegister(register, MoveType.OBJECT, ThrowingInfo.NO_THROW, info);
+    addInstruction(new Argument(value));
+    value.markAsThis();
+  }
+
+  public void addNonThisArgument(int register, MoveType moveType) {
+    DebugLocalInfo local = getCurrentLocal(register);
+    DebugInfo info = local == null ? null : new DebugInfo(local, null);
+    Value value = writeRegister(register, moveType, ThrowingInfo.NO_THROW, info);
+    addInstruction(new Argument(value));
+  }
+
+  public void addDebugUninitialized(int register, ConstType type) {
+    if (!options.debug) {
+      return;
+    }
+    Value value = writeRegister(register, MoveType.fromConstType(type), ThrowingInfo.NO_THROW, null);
+    assert value.getLocalInfo() == null;
+    addInstruction(new DebugLocalUninitialized(type, value));
+  }
+
+  public void addDebugLocalStart(int register, DebugLocalInfo local) {
+    if (!options.debug) {
+      return;
+    }
+    assert local != null;
+    assert local == getCurrentLocal(register);
+    MoveType moveType = MoveType.fromDexType(local.type);
+    Value in = readRegisterIgnoreLocal(register, moveType);
+    if (in.isPhi() || in.getLocalInfo() != local) {
+      // We cannot shortcut if the local is defined by a phi as it could end up being trivial.
+      addDebugLocalWrite(moveType, register, in);
+    } else {
+      DebugLocalRead read = addDebugLocalRead(register, local);
+      if (read != null) {
+        read.addDebugLocalStart();
+      }
+    }
+  }
+
+  public void addDebugLocalEnd(int register, DebugLocalInfo local) {
+    if (!options.debug) {
+      return;
+    }
+    DebugLocalRead read = addDebugLocalRead(register, local);
+    if (read != null) {
+      read.addDebugLocalEnd();
+    }
+  }
+
+  private void addDebugLocalWrite(MoveType type, int dest, Value in) {
+    Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+    DebugLocalWrite write = new DebugLocalWrite(out, in);
+    assert !write.instructionTypeCanThrow();
+    addInstruction(write);
+  }
+
+  public DebugLocalRead addDebugLocalRead(int register, DebugLocalInfo local) {
+    if (!options.debug) {
+      return null;
+    }
+    assert local != null;
+    assert local == getCurrentLocal(register);
+    MoveType moveType = MoveType.fromDexType(local.type);
+    // Invalid debug-info may cause attempt to read a local that is not actually alive.
+    // See b/37722432 and regression test {@code jasmin.InvalidDebugInfoTests::testInvalidInfoThrow}
+    Value in = readRegisterIgnoreLocal(register, moveType);
+    if (in.isUninitializedLocal() || in.getLocalInfo() != local) {
+      return null;
+    }
+    assert in.getLocalInfo() == local;
+    DebugLocalRead readLocal = new DebugLocalRead(in);
+    assert !readLocal.instructionTypeCanThrow();
+    addInstruction(readLocal);
+    return readLocal;
+  }
+
+  public void addAdd(NumericType type, int dest, int left, int right) {
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Add instruction = new Add(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addAddLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Add instruction = new Add(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addAnd(NumericType type, int dest, int left, int right) {
+    assert isIntegerType(type);
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    And instruction = new And(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addAndLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    And instruction = new And(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addArrayGet(MemberType type, int dest, int array, int index) {
+    Value in1 = readRegister(array, MoveType.OBJECT);
+    Value in2 = readRegister(index, MoveType.SINGLE);
+    Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+    ArrayGet instruction = new ArrayGet(type, out, in1, in2);
+    assert instruction.instructionTypeCanThrow();
+    add(instruction);
+  }
+
+  public void addArrayLength(int dest, int array) {
+    Value in = readRegister(array, MoveType.OBJECT);
+    Value out = writeRegister(dest, MoveType.SINGLE, ThrowingInfo.CAN_THROW);
+    ArrayLength instruction = new ArrayLength(out, in);
+    assert instruction.instructionTypeCanThrow();
+    add(instruction);
+  }
+
+  public void addArrayPut(MemberType type, int value, int array, int index) {
+    List<Value> ins = new ArrayList<>(3);
+    ins.add(readRegister(value, MoveType.fromMemberType(type)));
+    ins.add(readRegister(array, MoveType.OBJECT));
+    ins.add(readRegister(index, MoveType.SINGLE));
+    ArrayPut instruction = new ArrayPut(type, ins);
+    add(instruction);
+  }
+
+  public void addCheckCast(int value, DexType type) {
+    Value in = readRegister(value, MoveType.OBJECT);
+    Value out = writeRegister(value, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    CheckCast instruction = new CheckCast(out, in, type);
+    assert instruction.instructionTypeCanThrow();
+    add(instruction);
+  }
+
+  public void addCmp(NumericType type, Bias bias, int dest, int left, int right) {
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeRegister(dest, MoveType.SINGLE, ThrowingInfo.NO_THROW);
+    Cmp instruction = new Cmp(type, bias, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    add(instruction);
+  }
+
+  public void addConst(MoveType type, int dest, long value) {
+    ConstNumber instruction;
+    if (type == MoveType.SINGLE) {
+      Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+      instruction = new ConstNumber(ConstType.INT_OR_FLOAT, out, value);
+    } else {
+      assert type == MoveType.WIDE;
+      Value out = writeRegister(dest, type, ThrowingInfo.NO_THROW);
+      instruction = new ConstNumber(ConstType.LONG_OR_DOUBLE, out, value);
+    }
+    assert !instruction.instructionTypeCanThrow();
+    add(instruction);
+  }
+
+  // TODO(ager): Does art support changing the value of locals  during debugging? If so, we need
+  // to disable constant canonicalization in debug builds to make sure we have separate values
+  // for separate locals.
+  private void canonicalizeAndAddConst(
+      ConstType type, int dest, long value, Map<Long, ConstNumber> table) {
+    ConstNumber existing = table.get(value);
+    if (existing != null) {
+      currentBlock.writeCurrentDefinition(dest, existing.outValue(), ThrowingInfo.NO_THROW);
+    } else {
+      Value out = writeRegister(dest, MoveType.fromConstType(type), ThrowingInfo.NO_THROW);
+      ConstNumber instruction = new ConstNumber(type, out, value);
+      BasicBlock entryBlock = blocks.get(0);
+      if (currentBlock != entryBlock) {
+        // Insert the constant instruction at the start of the block right after the argument
+        // instructions. It is important that the const instruction is put before any instruction
+        // that can throw exceptions (since the value could be used on the exceptional edge).
+        InstructionListIterator it = entryBlock.listIterator();
+        while (it.hasNext()) {
+          if (!it.next().isArgument()) {
+            it.previous();
+            break;
+          }
+        }
+        it.add(instruction);
+      } else {
+        add(instruction);
+      }
+      table.put(value, instruction);
+    }
+  }
+
+  public void addLongConst(int dest, long value) {
+    canonicalizeAndAddConst(ConstType.LONG, dest, value, longConstants);
+  }
+
+  public void addDoubleConst(int dest, long value) {
+    canonicalizeAndAddConst(ConstType.DOUBLE, dest, value, doubleConstants);
+  }
+
+  public void addIntConst(int dest, long value) {
+    canonicalizeAndAddConst(ConstType.INT, dest, value, intConstants);
+  }
+
+  public void addFloatConst(int dest, long value) {
+    canonicalizeAndAddConst(ConstType.FLOAT, dest, value, floatConstants);
+  }
+
+  public void addNullConst(int dest, long value) {
+    canonicalizeAndAddConst(ConstType.INT, dest, value, nullConstants);
+  }
+
+  public void addConstClass(int dest, DexType type) {
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    ConstClass instruction = new ConstClass(out, type);
+    assert instruction.instructionTypeCanThrow();
+    add(instruction);
+  }
+
+  public void addConstString(int dest, DexString string) {
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    ConstString instruction = new ConstString(out, string);
+    add(instruction);
+  }
+
+  public void addDiv(NumericType type, int dest, int left, int right) {
+    boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+    Div instruction = new Div(type, out, in1, in2);
+    assert instruction.instructionTypeCanThrow() == canThrow;
+    add(instruction);
+  }
+
+  public void addDivLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+    Div instruction = new Div(type, out, in1, in2);
+    assert instruction.instructionTypeCanThrow() == canThrow;
+    add(instruction);
+  }
+
+  public Monitor addMonitor(Monitor.Type type, int monitor) {
+    Value in = readRegister(monitor, MoveType.OBJECT);
+    Monitor monitorEnter = new Monitor(type, in);
+    add(monitorEnter);
+    return monitorEnter;
+  }
+
+  public void addMove(MoveType type, int dest, int src) {
+    Value in = readRegister(src, type);
+    if (options.debug) {
+      // If the move is writing to a different local we must construct a new value.
+      DebugLocalInfo destLocal = getCurrentLocal(dest);
+      if (destLocal != null && destLocal != in.getLocalInfo()) {
+        addDebugLocalWrite(type, dest, in);
+        return;
+      }
+    }
+    currentBlock.writeCurrentDefinition(dest, in, ThrowingInfo.NO_THROW);
+  }
+
+  public void addMul(NumericType type, int dest, int left, int right) {
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Mul instruction = new Mul(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addMulLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Mul instruction = new Mul(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addRem(NumericType type, int dest, int left, int right) {
+    boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+    Rem instruction = new Rem(type, out, in1, in2);
+    assert instruction.instructionTypeCanThrow() == canThrow;
+    addInstruction(instruction);
+  }
+
+  public void addRemLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    boolean canThrow = type != NumericType.DOUBLE && type != NumericType.FLOAT;
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, canThrow ? ThrowingInfo.CAN_THROW : ThrowingInfo.NO_THROW);
+    Rem instruction = new Rem(type, out, in1, in2);
+    assert instruction.instructionTypeCanThrow() == canThrow;
+    addInstruction(instruction);
+  }
+
+  public void addGoto(int targetOffset) {
+    addInstruction(new Goto());
+    BasicBlock targetBlock = getTarget(targetOffset);
+    if (currentBlock.isCatchSuccessor(targetBlock)) {
+      needGotoToCatchBlocks.add(new BasicBlock.Pair(currentBlock, targetBlock));
+    } else {
+      currentBlock.link(targetBlock);
+    }
+    addToWorklist(targetBlock, source.instructionIndex(targetOffset));
+    closeCurrentBlock();
+  }
+
+  private void addTrivialIf(int trueTargetOffset, int falseTargetOffset) {
+    assert trueTargetOffset == falseTargetOffset;
+    // Conditional instructions with the same true and false targets are noops. They will
+    // always go to the next instruction. We end this basic block with a goto instead of
+    // a conditional.
+    BasicBlock target = getTarget(trueTargetOffset);
+    // We expected an if here and therefore we incremented the expected predecessor count
+    // twice for the following block.
+    target.decrementUnfilledPredecessorCount();
+    addInstruction(new Goto());
+    currentBlock.link(target);
+    addToWorklist(target, source.instructionIndex(trueTargetOffset));
+    closeCurrentBlock();
+  }
+
+  private void addNonTrivialIf(If instruction, int trueTargetOffset, int falseTargetOffset) {
+    addInstruction(instruction);
+    BasicBlock trueTarget = getTarget(trueTargetOffset);
+    BasicBlock falseTarget = getTarget(falseTargetOffset);
+    currentBlock.link(trueTarget);
+    currentBlock.link(falseTarget);
+    // Generate fall-through before the block that is branched to.
+    addToWorklist(falseTarget, source.instructionIndex(falseTargetOffset));
+    addToWorklist(trueTarget, source.instructionIndex(trueTargetOffset));
+    closeCurrentBlock();
+  }
+
+  public void addIf(If.Type type, int value1, int value2,
+      int trueTargetOffset, int falseTargetOffset) {
+    if (trueTargetOffset == falseTargetOffset) {
+      addTrivialIf(trueTargetOffset, falseTargetOffset);
+    } else {
+      List<Value> values = new ArrayList<>(2);
+      values.add(readRegister(value1, MoveType.SINGLE));
+      values.add(readRegister(value2, MoveType.SINGLE));
+      If instruction = new If(type, values);
+      addNonTrivialIf(instruction, trueTargetOffset, falseTargetOffset);
+    }
+  }
+
+  public void addIfZero(If.Type type, int value, int trueTargetOffset, int falseTargetOffset) {
+    if (trueTargetOffset == falseTargetOffset) {
+      addTrivialIf(trueTargetOffset, falseTargetOffset);
+    } else {
+      If instruction = new If(type, readRegister(value, MoveType.SINGLE));
+      addNonTrivialIf(instruction, trueTargetOffset, falseTargetOffset);
+    }
+  }
+
+  public void addInstanceGet(
+      MemberType type,
+      int dest,
+      int object,
+      DexField field) {
+    Value in = readRegister(object, MoveType.OBJECT);
+    Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+    InstanceGet instruction = new InstanceGet(type, out, in, field);
+    assert instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addInstanceOf(int dest, int value, DexType type) {
+    Value in = readRegister(value, MoveType.OBJECT);
+    Value out = writeRegister(dest, MoveType.SINGLE, ThrowingInfo.CAN_THROW);
+    InstanceOf instruction = new InstanceOf(out, in, type);
+    assert instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addInstancePut(
+      MemberType type,
+      int value,
+      int object,
+      DexField field) {
+    List<Value> values = new ArrayList<>(2);
+    values.add(readRegister(value, MoveType.fromMemberType(type)));
+    values.add(readRegister(object, MoveType.OBJECT));
+    InstancePut instruction = new InstancePut(type, values, field);
+    add(instruction);
+  }
+
+  public void addInvoke(
+      Type type, DexItem item, DexProto callSiteProto, List<Value> arguments) {
+    if (type == Invoke.Type.POLYMORPHIC && !options.canUseInvokePolymorphic()) {
+      throw new CompilationError(
+          "MethodHandle.invoke and MethodHandle.invokeExact is unsupported before "
+              + "Android O (--min-sdk-version " + Constants.ANDROID_O_API + ")");
+    }
+    add(Invoke.create(type, item, callSiteProto, null, arguments));
+  }
+
+  public void addInvoke(
+      Invoke.Type type,
+      DexItem item,
+      DexProto callSiteProto,
+      List<MoveType> types,
+      List<Integer> registers) {
+    assert types.size() == registers.size();
+    List<Value> arguments = new ArrayList<>(types.size());
+    for (int i = 0; i < types.size(); i++) {
+      arguments.add(readRegister(registers.get(i), types.get(i)));
+    }
+    addInvoke(type, item, callSiteProto, arguments);
+  }
+
+  public void addInvokeCustomRegisters(
+      DexCallSite callSite, int argumentRegisterCount, int[] argumentRegisters) {
+    int registerIndex = 0;
+    DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
+    List<Value> arguments = new ArrayList<>(argumentRegisterCount);
+
+    if (!bootstrapMethod.isStaticHandle()) {
+      arguments.add(readRegister(argumentRegisters[registerIndex], MoveType.OBJECT));
+      registerIndex += MoveType.OBJECT.requiredRegisters();
+    }
+
+    String shorty = callSite.methodProto.shorty.toString();
+
+    for (int i = 1; i < shorty.length(); i++) {
+      MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+      arguments.add(readRegister(argumentRegisters[registerIndex], moveType));
+      registerIndex += moveType.requiredRegisters();
+    }
+
+    add(new InvokeCustom(callSite, null, arguments));
+  }
+
+  public void addInvokeCustomRange(
+      DexCallSite callSite, int argumentCount, int firstArgumentRegister) {
+    DexMethodHandle bootstrapMethod = callSite.bootstrapMethod;
+    List<Value> arguments = new ArrayList<>(argumentCount);
+
+    int register = firstArgumentRegister;
+    if (!bootstrapMethod.isStaticHandle()) {
+      arguments.add(readRegister(register, MoveType.OBJECT));
+      register += MoveType.OBJECT.requiredRegisters();
+    }
+
+    String shorty = callSite.methodProto.shorty.toString();
+
+    for (int i = 1; i < shorty.length(); i++) {
+      MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+      arguments.add(readRegister(register, moveType));
+      register += moveType.requiredRegisters();
+    }
+    checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
+    add(new InvokeCustom(callSite, null, arguments));
+  }
+
+  public void addInvokeCustom(
+      DexCallSite callSite, List<MoveType> types, List<Integer> registers) {
+    assert types.size() == registers.size();
+    List<Value> arguments = new ArrayList<>(types.size());
+    for (int i = 0; i < types.size(); i++) {
+      arguments.add(readRegister(registers.get(i), types.get(i)));
+    }
+    add(new InvokeCustom(callSite, null, arguments));
+  }
+
+  public void addInvokeRegisters(
+      Invoke.Type type,
+      DexMethod method,
+      DexProto callSiteProto,
+      int argumentRegisterCount,
+      int[] argumentRegisters) {
+    // The value of argumentRegisterCount is the number of registers - not the number of values,
+    // but it is an upper bound on the number of arguments.
+    List<Value> arguments = new ArrayList<>(argumentRegisterCount);
+    int registerIndex = 0;
+    if (type != Invoke.Type.STATIC) {
+      arguments.add(readRegister(argumentRegisters[registerIndex], MoveType.OBJECT));
+      registerIndex += MoveType.OBJECT.requiredRegisters();
+    }
+    DexString methodShorty;
+    if (type == Invoke.Type.POLYMORPHIC) {
+      // The call site signature for invoke polymorphic must be take from call site and not from
+      // the called method.
+      methodShorty = callSiteProto.shorty;
+    } else {
+      methodShorty = method.proto.shorty;
+    }
+    String shorty = methodShorty.toString();
+    for (int i = 1; i < methodShorty.size; i++) {
+      MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+      arguments.add(readRegister(argumentRegisters[registerIndex], moveType));
+      registerIndex += moveType.requiredRegisters();
+    }
+    checkInvokeArgumentRegisters(registerIndex, argumentRegisterCount);
+    addInvoke(type, method, callSiteProto, arguments);
+  }
+
+  public void addInvokeNewArray(DexType type, int argumentCount, int[] argumentRegisters) {
+    String descriptor = type.descriptor.toString();
+    assert descriptor.charAt(0) == '[';
+    assert descriptor.length() >= 2;
+    MoveType moveType = MoveType.fromTypeDescriptorChar(descriptor.charAt(1));
+    List<Value> arguments = new ArrayList<>(argumentCount / moveType.requiredRegisters());
+    int registerIndex = 0;
+    while (registerIndex < argumentCount) {
+      arguments.add(readRegister(argumentRegisters[registerIndex], moveType));
+      if (moveType == MoveType.WIDE) {
+        assert registerIndex < argumentCount - 1;
+        assert argumentRegisters[registerIndex] == argumentRegisters[registerIndex + 1] + 1;
+      }
+      registerIndex += moveType.requiredRegisters();
+    }
+    checkInvokeArgumentRegisters(registerIndex, argumentCount);
+    addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+  }
+
+  public void addInvokeRange(
+      Invoke.Type type,
+      DexMethod method,
+      DexProto callSiteProto,
+      int argumentCount,
+      int firstArgumentRegister) {
+    // The value of argumentCount is the number of registers - not the number of values, but it
+    // is an upper bound on the number of arguments.
+    List<Value> arguments = new ArrayList<>(argumentCount);
+    int register = firstArgumentRegister;
+    if (type != Invoke.Type.STATIC) {
+      arguments.add(readRegister(register, MoveType.OBJECT));
+      register += MoveType.OBJECT.requiredRegisters();
+    }
+    DexString methodShorty;
+    if (type == Invoke.Type.POLYMORPHIC) {
+      // The call site signature for invoke polymorphic must be take from call site and not from
+      // the called method.
+      methodShorty = callSiteProto.shorty;
+    } else {
+      methodShorty = method.proto.shorty;
+    }
+    String shorty = methodShorty.toString();
+    for (int i = 1; i < methodShorty.size; i++) {
+      MoveType moveType = MoveType.fromTypeDescriptorChar(shorty.charAt(i));
+      arguments.add(readRegister(register, moveType));
+      register += moveType.requiredRegisters();
+    }
+    checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
+    addInvoke(type, method, callSiteProto, arguments);
+  }
+
+  public void addInvokeRangeNewArray(DexType type, int argumentCount, int firstArgumentRegister) {
+    String descriptor = type.descriptor.toString();
+    assert descriptor.charAt(0) == '[';
+    assert descriptor.length() >= 2;
+    MoveType moveType = MoveType.fromTypeDescriptorChar(descriptor.charAt(1));
+    List<Value> arguments = new ArrayList<>(argumentCount / moveType.requiredRegisters());
+    int register = firstArgumentRegister;
+    while (register < firstArgumentRegister + argumentCount) {
+      arguments.add(readRegister(register, moveType));
+      register += moveType.requiredRegisters();
+    }
+    checkInvokeArgumentRegisters(register, firstArgumentRegister + argumentCount);
+    addInvoke(Invoke.Type.NEW_ARRAY, type, null, arguments);
+  }
+
+  private void checkInvokeArgumentRegisters(int expected, int actual) {
+    if (expected != actual) {
+      throw new CompilationError("Invalid invoke instruction. "
+          + "Expected use of " + expected + " argument registers, "
+          + "found actual use of " + actual);
+    }
+  }
+
+  public void addMoveException(int dest) {
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.NO_THROW);
+    MoveException instruction = new MoveException(out);
+    assert !instruction.instructionTypeCanThrow();
+    assert currentBlock.getInstructions().isEmpty();
+    addInstruction(instruction);
+  }
+
+  public void addMoveResult(MoveType type, int dest) {
+    List<Instruction> instructions = currentBlock.getInstructions();
+    Invoke invoke = instructions.get(instructions.size() - 1).asInvoke();
+    assert invoke.outValue() == null;
+    assert invoke.instructionTypeCanThrow();
+    invoke.setOutValue(writeRegister(dest, type, ThrowingInfo.CAN_THROW));
+  }
+
+  public void addNeg(NumericType type, int dest, int value) {
+    Value in = readNumericRegister(value, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Neg instruction = new Neg(type, out, in);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addNot(NumericType type, int dest, int value) {
+    Value in = readNumericRegister(value, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Not instruction = new Not(type, out, in);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addNewArrayEmpty(int dest, int size, DexType type) {
+    assert type.isArrayType();
+    Value in = readRegister(size, MoveType.SINGLE);
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    NewArrayEmpty instruction = new NewArrayEmpty(out, in, type);
+    assert instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addNewArrayFilledData(int arrayRef, int elementWidth, long size, short[] data) {
+    add(new NewArrayFilledData(readRegister(arrayRef, MoveType.OBJECT), elementWidth, size, data));
+  }
+
+  public void addNewInstance(int dest, DexType type) {
+    Value out = writeRegister(dest, MoveType.OBJECT, ThrowingInfo.CAN_THROW);
+    NewInstance instruction = new NewInstance(type, out);
+    assert instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addReturn(MoveType type, int value) {
+    Value in = readRegister(value, type);
+    addInstruction(new Return(in, type));
+    exitBlocks.add(currentBlock);
+    closeCurrentBlock();
+  }
+
+  public void addReturn() {
+    addInstruction(new Return());
+    exitBlocks.add(currentBlock);
+    closeCurrentBlock();
+  }
+
+  public void addStaticGet(MemberType type, int dest, DexField field) {
+    Value out = writeRegister(dest, MoveType.fromMemberType(type), ThrowingInfo.CAN_THROW);
+    StaticGet instruction = new StaticGet(type, out, field);
+    assert instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addStaticPut(MemberType type, int value, DexField field) {
+    Value in = readRegister(value, MoveType.fromMemberType(type));
+    add(new StaticPut(type, in, field));
+  }
+
+  public void addSub(NumericType type, int dest, int left, int right) {
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Sub instruction = new Sub(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addRsubLiteral(NumericType type, int dest, int value, int constant) {
+    assert type != NumericType.DOUBLE;
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    // Add this as a sub instruction - sub instructions with literals need to have the constant
+    // on the left side (rsub).
+    Sub instruction = new Sub(type, out, in2, in1);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  private void addSwitchIf(int key, int value, int caseOffset, int fallthroughOffset) {
+    if (key == 0) {
+      addIfZero(If.Type.EQ, value, caseOffset, fallthroughOffset);
+    } else {
+      if (caseOffset == fallthroughOffset) {
+        addTrivialIf(caseOffset, fallthroughOffset);
+      } else {
+        List<Value> values = new ArrayList<>(2);
+        values.add(readRegister(value, MoveType.SINGLE));
+        values.add(readLiteral(NumericType.INT, key));
+        If instruction = new If(If.Type.EQ, values);
+        addNonTrivialIf(instruction, caseOffset, fallthroughOffset);
+      }
+    }
+  }
+
+  public void addSwitch(Switch.Type type, int value, int[] keys, int fallthroughOffset,
+      int[] labelOffsets) {
+    int numberOfTargets = labelOffsets.length;
+    assert (type == Switch.Type.PACKED && keys.length == 1) || (keys.length == numberOfTargets);
+
+    // If the switch has no targets simply add a goto to the fallthrough.
+    if (numberOfTargets == 0) {
+      addGoto(fallthroughOffset);
+      return;
+    }
+
+    Value switchValue = readRegister(value, MoveType.SINGLE);
+
+    // Change a switch with just one case to an if.
+    if (numberOfTargets == 1) {
+      addSwitchIf(keys[0], value, labelOffsets[0], fallthroughOffset);
+      return;
+    }
+
+    // javac generates sparse switch (lookupwitch bytecode) for all two-case switch. Change
+    // to packed switch for consecutive keys.
+    if (numberOfTargets == 2 && type == Switch.Type.SPARSE && keys[0] + 1 == keys[1]) {
+      addInstruction(createSwitch(
+          Switch.Type.PACKED, switchValue, new int[]{keys[0]}, fallthroughOffset, labelOffsets));
+      closeCurrentBlock();
+      return;
+    }
+
+    // javac generates packed switches pretty aggressively. We convert to sparse switches
+    // if the dex sparse switch payload takes up less space than the packed payload.
+    if (type == Switch.Type.PACKED) {
+      int numberOfFallthroughs = 0;
+      for (int i = 0; i < numberOfTargets; i++) {
+        if (labelOffsets[i] == fallthroughOffset) {
+          ++numberOfFallthroughs;
+        }
+      }
+      // If this was a packed switch with only fallthrough cases we can make it a goto.
+      // Oddly, this does happen.
+      if (numberOfFallthroughs == numberOfTargets) {
+        targets.get(fallthroughOffset).decrementUnfilledPredecessorCount(numberOfFallthroughs);
+        addGoto(fallthroughOffset);
+        return;
+      }
+      int numberOfSparseTargets = numberOfTargets - numberOfFallthroughs;
+      int sparseSwitchPayloadSize = SparseSwitchPayload.getSizeForTargets(numberOfSparseTargets);
+      int packedSwitchPayloadSize = PackedSwitchPayload.getSizeForTargets(numberOfTargets);
+      int bytesSaved = packedSwitchPayloadSize - sparseSwitchPayloadSize;
+      // Perform the rewrite if we can reduce the payload size by more than 20%.
+      if (bytesSaved > (packedSwitchPayloadSize / 5)) {
+        targets.get(fallthroughOffset).decrementUnfilledPredecessorCount(numberOfFallthroughs);
+        int nextCaseIndex = 0;
+        int currentKey = keys[0];
+        keys = new int[numberOfSparseTargets];
+        int[] newLabelOffsets = new int[numberOfSparseTargets];
+        for (int i = 0; i < numberOfTargets; i++) {
+          if (labelOffsets[i] != fallthroughOffset) {
+            keys[nextCaseIndex] = currentKey;
+            newLabelOffsets[nextCaseIndex] = labelOffsets[i];
+            ++nextCaseIndex;
+          }
+          ++currentKey;
+        }
+        addInstruction(createSwitch(
+            Switch.Type.SPARSE, switchValue, keys, fallthroughOffset, newLabelOffsets));
+        closeCurrentBlock();
+        return;
+      }
+    }
+
+    addInstruction(createSwitch(type, switchValue, keys, fallthroughOffset, labelOffsets));
+    closeCurrentBlock();
+  }
+
+  private Switch createSwitch(Switch.Type type, Value value, int[] keys, int fallthroughOffset,
+      int[] targetOffsets) {
+    assert keys.length > 0;
+    // Compute target blocks for all keys. Only add a successor block once even
+    // if it is hit by more of the keys.
+    int[] targetBlockIndices = new int[targetOffsets.length];
+    Map<Integer, Integer> offsetToBlockIndex = new HashMap<>();
+    // Start with fall-through block.
+    BasicBlock fallthroughBlock = getTarget(fallthroughOffset);
+    currentBlock.link(fallthroughBlock);
+    addToWorklist(fallthroughBlock, source.instructionIndex(fallthroughOffset));
+    int fallthroughBlockIndex = currentBlock.getSuccessors().size() - 1;
+    offsetToBlockIndex.put(fallthroughOffset, fallthroughBlockIndex);
+    // Then all the switch target blocks.
+    for (int i = 0; i < targetOffsets.length; i++) {
+      int targetOffset = targetOffsets[i];
+      BasicBlock targetBlock = getTarget(targetOffset);
+      Integer targetBlockIndex = offsetToBlockIndex.get(targetOffset);
+      if (targetBlockIndex == null) {
+        // Target block not added as successor. Add it now.
+        currentBlock.link(targetBlock);
+        addToWorklist(targetBlock, source.instructionIndex(targetOffset));
+        int successorIndex = currentBlock.getSuccessors().size() - 1;
+        offsetToBlockIndex.put(targetOffset, successorIndex);
+        targetBlockIndices[i] = successorIndex;
+      } else {
+        // Target block already added as successor. The target block therefore
+        // has one less predecessor than precomputed.
+        targetBlock.decrementUnfilledPredecessorCount();
+        targetBlockIndices[i] = targetBlockIndex;
+      }
+    }
+    return new Switch(type, value, keys, targetBlockIndices, fallthroughBlockIndex);
+  }
+
+  public void addThrow(int value) {
+    Value in = readRegister(value, MoveType.OBJECT);
+    addInstruction(new Throw(in));
+    closeCurrentBlock();
+  }
+
+  public void addOr(NumericType type, int dest, int left, int right) {
+    assert isIntegerType(type);
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Or instruction = new Or(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addOrLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Or instruction = new Or(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addShl(NumericType type, int dest, int left, int right) {
+    assert isIntegerType(type);
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readRegister(right, MoveType.SINGLE);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Shl instruction = new Shl(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addShlLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Shl instruction = new Shl(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addShr(NumericType type, int dest, int left, int right) {
+    assert isIntegerType(type);
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readRegister(right, MoveType.SINGLE);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Shr instruction = new Shr(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addShrLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Shr instruction = new Shr(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addUshr(NumericType type, int dest, int left, int right) {
+    assert isIntegerType(type);
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readRegister(right, MoveType.SINGLE);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Ushr instruction = new Ushr(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addUshrLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Value in2 = readLiteral(type, constant);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Ushr instruction = new Ushr(type, out, in1, in2);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addXor(NumericType type, int dest, int left, int right) {
+    assert isIntegerType(type);
+    Value in1 = readNumericRegister(left, type);
+    Value in2 = readNumericRegister(right, type);
+    Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+    Instruction instruction;
+    if (in2.isConstant() && in2.getConstInstruction().asConstNumber().isIntegerNegativeOne(type)) {
+      instruction = new Not(type, out, in1);
+    } else {
+      instruction = new Xor(type, out, in1, in2);
+    }
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addXorLiteral(NumericType type, int dest, int value, int constant) {
+    assert isNonLongIntegerType(type);
+    Value in1 = readNumericRegister(value, type);
+    Instruction instruction;
+    if (constant == -1) {
+      Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+      instruction = new Not(type, out, in1);
+    } else {
+      Value in2 = readLiteral(type, constant);
+      Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
+      instruction = new Xor(type, out, in1, in2);
+    }
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  public void addConversion(NumericType to, NumericType from, int dest, int source) {
+    Value in = readNumericRegister(source, from);
+    Value out = writeNumericRegister(dest, to, ThrowingInfo.NO_THROW);
+    NumberConversion instruction = new NumberConversion(from, to, out, in);
+    assert !instruction.instructionTypeCanThrow();
+    addInstruction(instruction);
+  }
+
+  // Value abstraction methods.
+
+  public Value readRegister(int register, MoveType type) {
+    DebugLocalInfo local = getCurrentLocal(register);
+    Value value = readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
+    // Check that any information about a current-local is consistent with the read.
+    assert local == null || value.getLocalInfo() == local || value.isUninitializedLocal();
+    // Check that any local information on the value is actually visible.
+    // If this assert triggers, the probable cause is that we end up reading an SSA value
+    // after it should have been ended on a fallthrough from a conditional jump or a trivial-phi
+    // removal resurrected the local.
+    assert value.getLocalInfo() == null
+        || value.getDebugLocalEnds() != null
+        || source.verifyLocalInScope(value.getLocalInfo());
+    return value;
+  }
+
+  public Value readRegisterIgnoreLocal(int register, MoveType type) {
+    DebugLocalInfo local = getCurrentLocal(register);
+    return readRegister(register, currentBlock, EdgeType.NON_EDGE, type, local);
+  }
+
+  public Value readRegister(int register, BasicBlock block, EdgeType readingEdge, MoveType type,
+      DebugLocalInfo local) {
+    checkRegister(register);
+    Value value = block.readCurrentDefinition(register, readingEdge);
+    return value != null ? value : readRegisterRecursive(register, block, readingEdge, type, local);
+  }
+
+  private Value readRegisterRecursive(
+      int register, BasicBlock block, EdgeType readingEdge, MoveType type, DebugLocalInfo local) {
+    Value value;
+    if (!block.isSealed()) {
+      assert !blocks.isEmpty() : "No write to " + register;
+      Phi phi = new Phi(valueNumberGenerator.next(), register, block, type, local);
+      block.addIncompletePhi(register, phi, readingEdge);
+      value = phi;
+    } else if (block.getPredecessors().size() == 1) {
+      assert block.verifyFilledPredecessors();
+      BasicBlock pred = block.getPredecessors().get(0);
+      EdgeType edgeType = pred.getEdgeType(block);
+      value = readRegister(register, pred, edgeType, type, local);
+    } else {
+      Phi phi = new Phi(valueNumberGenerator.next(), register, block, type, local);
+      // We need to write the phi before adding operands to break cycles. If the phi is trivial
+      // and is removed by addOperands, the definition is overwritten and looked up again below.
+      block.updateCurrentDefinition(register, phi, readingEdge);
+      phi.addOperands(this);
+      // Lookup the value for the register again at this point. Recursive trivial
+      // phi removal could have simplified what we wanted to return here.
+      value = block.readCurrentDefinition(register, readingEdge);
+    }
+    block.updateCurrentDefinition(register, value, readingEdge);
+    return value;
+  }
+
+  public Value readNumericRegister(int register, NumericType type) {
+    return readRegister(register, type.moveTypeFor());
+  }
+
+  public Value readLiteral(NumericType type, long constant) {
+    Value value = new Value(valueNumberGenerator.next(), -1, MoveType.fromNumericType(type), null);
+    add(new ConstNumber(ConstType.fromNumericType(type), value, constant));
+    return value;
+  }
+
+  // This special write register is needed when changing the scoping of a local variable.
+  // See addDebugLocalStart and addDebugLocalEnd.
+  private Value writeRegister(int register, MoveType type, ThrowingInfo throwing, DebugInfo info) {
+    checkRegister(register);
+    Value value = new Value(valueNumberGenerator.next(), register, type, info);
+    currentBlock.writeCurrentDefinition(register, value, throwing);
+    return value;
+  }
+
+  public Value writeRegister(int register, MoveType type, ThrowingInfo throwing) {
+    DebugLocalInfo local = getCurrentLocal(register);
+    DebugInfo info = null;
+    if (local != null) {
+      Value previousLocal = readRegisterIgnoreLocal(register, type);
+      info = new DebugInfo(local, previousLocal.getLocalInfo() != local ? null : previousLocal);
+    }
+    return writeRegister(register, type, throwing, info);
+  }
+
+  public Value writeNumericRegister(int register, NumericType type, ThrowingInfo throwing) {
+    return writeRegister(register, type.moveTypeFor(), throwing);
+  }
+
+  private DebugLocalInfo getCurrentLocal(int register) {
+    return options.debug ? source.getCurrentLocal(register) : null;
+  }
+
+  private void checkRegister(int register) {
+    if (register < 0) {
+      throw new InternalCompilerError("Invalid register");
+    }
+    if (!source.verifyRegister(register)) {
+      throw new CompilationError("Invalid use of register " + register);
+    }
+  }
+
+  /**
+   * Ensure that the current block can hold a throwing instruction. This will create a new current
+   * block if the current block has handlers and already has one throwing instruction.
+   */
+  void ensureBlockForThrowingInstruction() {
+    if (!throwingInstructionInCurrentBlock) {
+      return;
+    }
+    BasicBlock block = new BasicBlock();
+    blocks.add(block);
+    block.incrementUnfilledPredecessorCount();
+    for (Integer offset : source.getCurrentCatchHandlers().getUniqueTargets()) {
+      BasicBlock target = getTarget(offset);
+      assert !target.isSealed();
+      target.incrementUnfilledPredecessorCount();
+    }
+    addInstruction(new Goto());
+    currentBlock.link(block);
+    closeCurrentBlock();
+    setCurrentBlock(block);
+  }
+
+  // Private instruction helpers.
+  private void addInstruction(Instruction ir) {
+    if (currentDebugPosition != null && !ir.isMoveException()) {
+      flushCurrentDebugPosition();
+    }
+    currentBlock.add(ir);
+    if (ir.instructionTypeCanThrow()) {
+      assert source.verifyCurrentInstructionCanThrow();
+      CatchHandlers<Integer> catchHandlers = source.getCurrentCatchHandlers();
+      if (catchHandlers != null) {
+        assert !throwingInstructionInCurrentBlock;
+        throwingInstructionInCurrentBlock = true;
+        List<BasicBlock> targets = new ArrayList<>(catchHandlers.getAllTargets().size());
+        for (int targetOffset : catchHandlers.getAllTargets()) {
+          BasicBlock target = getTarget(targetOffset);
+          addToWorklist(target, source.instructionIndex(targetOffset));
+          targets.add(target);
+        }
+        currentBlock.linkCatchSuccessors(catchHandlers.getGuards(), targets);
+      }
+    }
+    if (currentDebugPosition != null) {
+      assert ir.isMoveException();
+      flushCurrentDebugPosition();
+    }
+  }
+
+  // Package (ie, SourceCode accessed) helpers.
+
+  // Ensure there is a block starting at offset.
+  BasicBlock ensureBlockWithoutEnqueuing(int offset) {
+    BasicBlock block = targets.get(offset);
+    if (block == null) {
+      block = new BasicBlock();
+      targets.put(offset, block);
+      // If this is a processed instruction, the block split and it has a fall-through predecessor.
+      if (offset >= 0 && isOffsetProcessed(offset)) {
+        block.incrementUnfilledPredecessorCount();
+      }
+    }
+    return block;
+  }
+
+  // Ensure there is a block starting at offset and add it to the work-list if it needs processing.
+  private BasicBlock ensureBlock(int offset) {
+    // We don't enqueue negative targets (these are special blocks, eg, an argument prelude).
+    if (offset >= 0 && !isOffsetProcessed(offset)) {
+      traceBlocksWorklist.add(offset);
+    }
+    return ensureBlockWithoutEnqueuing(offset);
+  }
+
+  private boolean isOffsetProcessed(int offset) {
+    return isIndexProcessed(source.instructionIndex(offset));
+  }
+
+  private boolean isIndexProcessed(int index) {
+    if (index < processedInstructions.length) {
+      return processedInstructions[index];
+    }
+    ensureSubroutineProcessedInstructions();
+    return processedSubroutineInstructions.contains(index);
+  }
+
+  private void markIndexProcessed(int index) {
+    assert !isIndexProcessed(index);
+    if (index < processedInstructions.length) {
+      processedInstructions[index] = true;
+      return;
+    }
+    ensureSubroutineProcessedInstructions();
+    processedSubroutineInstructions.add(index);
+  }
+
+  private void ensureSubroutineProcessedInstructions() {
+    if (processedSubroutineInstructions == null) {
+      processedSubroutineInstructions = new HashSet<>();
+    }
+  }
+
+  // Ensure there is a block at offset and add a predecessor to it.
+  BasicBlock ensureSuccessorBlock(int offset) {
+    BasicBlock block = ensureBlock(offset);
+    block.incrementUnfilledPredecessorCount();
+    return block;
+  }
+
+  // Private block helpers.
+
+  private BasicBlock getTarget(int offset) {
+    return targets.get(offset);
+  }
+
+  private void closeCurrentBlock() {
+    // TODO(zerny): To ensure liveness of locals throughout the entire block, we might want to
+    // insert reads before closing the block. It is unclear if we can rely on a local-end to ensure
+    // liveness in all blocks where the local should be live.
+    assert currentBlock != null;
+    assert currentDebugPosition == null;
+    currentBlock.close(this);
+    setCurrentBlock(null);
+    throwingInstructionInCurrentBlock = false;
+  }
+
+  private void closeCurrentBlockWithFallThrough(BasicBlock nextBlock) {
+    assert currentBlock != null;
+    flushCurrentDebugPosition();
+    currentBlock.add(new Goto());
+    if (currentBlock.isCatchSuccessor(nextBlock)) {
+      needGotoToCatchBlocks.add(new BasicBlock.Pair(currentBlock, nextBlock));
+    } else {
+      currentBlock.link(nextBlock);
+    }
+    closeCurrentBlock();
+  }
+
+  int handleExitBlock(int blockNumber) {
+    if (exitBlocks.size() > 0) {
+      // Create and populate the exit block if needed (eg, synchronized support for jar).
+      setCurrentBlock(new BasicBlock());
+      source.buildPostlude(this);
+      // If the new exit block is empty and we only have one exit, abort building a new exit block.
+      if (currentBlock.getInstructions().isEmpty() && exitBlocks.size() == 1) {
+        normalExitBlock = exitBlocks.get(0);
+        setCurrentBlock(null);
+        return blockNumber;
+      }
+      // Commit to creating the new exit block.
+      normalExitBlock = currentBlock;
+      normalExitBlock.setNumber(blockNumber++);
+      blocks.add(normalExitBlock);
+      // Add the return instruction possibly creating a phi of return values.
+      Return origReturn = exitBlocks.get(0).exit().asReturn();
+      Phi phi = null;
+      if (origReturn.isReturnVoid()) {
+        normalExitBlock.add(new Return());
+      } else {
+        Value returnValue = origReturn.returnValue();
+        MoveType returnType = origReturn.getReturnType();
+        assert origReturn.getLocalInfo() == null;
+        phi = new Phi(
+            valueNumberGenerator.next(), -1, normalExitBlock, returnValue.outType(), null);
+        normalExitBlock.add(new Return(phi, returnType));
+        assert returnType == MoveType.fromDexType(method.method.proto.returnType);
+      }
+      closeCurrentBlock();
+      // Replace each return instruction with a goto to the new exit block.
+      List<Value> operands = new ArrayList<>();
+      for (BasicBlock block : exitBlocks) {
+        List<Instruction> instructions = block.getInstructions();
+        Return ret = block.exit().asReturn();
+        if (!ret.isReturnVoid()) {
+          operands.add(ret.returnValue());
+          ret.returnValue().removeUser(ret);
+        }
+        Goto gotoExit = new Goto();
+        gotoExit.setBlock(block);
+        instructions.set(instructions.size() - 1, gotoExit);
+        block.link(normalExitBlock);
+        gotoExit.setTarget(normalExitBlock);
+      }
+      if (phi != null) {
+        phi.addOperands(operands);
+      }
+    }
+    return blockNumber;
+  }
+
+  private int handleFallthroughToCatchBlock(int blockNumber) {
+    // When a catch handler for a block goes to the same block as the fallthrough for that
+    // block the graph only has one edge there. In these cases we add an additional block so the
+    // catch edge goes through that and then make the fallthrough go through a new direct edge.
+    for (BasicBlock.Pair pair : needGotoToCatchBlocks) {
+      BasicBlock source = pair.first;
+      BasicBlock target = pair.second;
+
+      // New block with one unfilled predecessor.
+      BasicBlock newBlock = BasicBlock.createGotoBlock(target, blockNumber++);
+      blocks.add(newBlock);
+      newBlock.incrementUnfilledPredecessorCount();
+
+      // Link blocks.
+      source.replaceSuccessor(target, newBlock);
+      newBlock.getPredecessors().add(source);
+      source.getSuccessors().add(target);
+      target.getPredecessors().add(newBlock);
+
+      // Check that the successor indexes are correct.
+      assert source.isCatchSuccessor(newBlock);
+      assert !source.isCatchSuccessor(target);
+
+      // Mark the filled predecessors to the blocks.
+      if (source.isFilled()) {
+        newBlock.filledPredecessor(this);
+      }
+      target.filledPredecessor(this);
+    }
+    return blockNumber;
+  }
+
+  private boolean phiOperandsAreConsistent() {
+    for (BasicBlock block : blocks) {
+      if (block.hasIncompletePhis()) {
+        StringBuilder builder = new StringBuilder("Incomplete phis in ");
+        builder.append(method);
+        builder.append(". The following registers appear to be uninitialized: ");
+        StringUtils.append(builder, block.getIncompletePhiRegisters(), ", ", BraceType.NONE);
+        throw new CompilationError(builder.toString());
+      }
+      for (Phi phi : block.getPhis()) {
+        assert phi.getOperands().size() == block.getPredecessors().size();
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Change to control-flow graph to avoid repeated phi operands when all the same values
+   * flow in from multiple predecessors.
+   *
+   * <p> As an example:
+   *
+   * <pre>
+   *
+   *              b1          b2         b3
+   *              |                       |
+   *              ----------\ | /----------
+   *
+   *                         b4
+   *                  v3 = phi(v1, v1, v2)
+   * </pre>
+   *
+   * <p> Is rewritten to:
+   *
+   * <pre>
+   *              b1          b2         b3
+   *                  \    /             /
+   *                    b5        -------
+   *                        \    /
+   *                          b4
+   *                  v3 = phi(v1, v2)
+   *
+   * </pre>
+   */
+  public void joinPredecessorsWithIdenticalPhis() {
+    List<BasicBlock> blocksToAdd = new ArrayList<>();
+    for (BasicBlock block : blocks) {
+      if (block.entry() instanceof MoveException) {
+        // TODO: Should we support joining in the presence of move-exception instructions?
+        continue;
+      }
+      List<Integer> operandsToRemove = new ArrayList<>();
+      Map<ValueList, Integer> values = new HashMap<>();
+      Map<Integer, BasicBlock> joinBlocks = new HashMap<>();
+      if (block.getPhis().size() > 0) {
+        Phi phi = block.getPhis().get(0);
+        for (int operandIndex = 0; operandIndex < phi.getOperands().size(); operandIndex++) {
+          ValueList v = ValueList.fromPhis(block.getPhis(), operandIndex);
+          BasicBlock predecessor = block.getPredecessors().get(operandIndex);
+          if (values.containsKey(v)) {
+            // Seen before, create a join block (or reuse an existing join block) to join through.
+            int otherPredecessorIndex = values.get(v);
+            BasicBlock joinBlock = joinBlocks.get(otherPredecessorIndex);
+            if (joinBlock == null) {
+              joinBlock = BasicBlock.createGotoBlock(block, blocks.size() + blocksToAdd.size());
+              joinBlocks.put(otherPredecessorIndex, joinBlock);
+              blocksToAdd.add(joinBlock);
+              BasicBlock otherPredecessor = block.getPredecessors().get(otherPredecessorIndex);
+              joinBlock.getPredecessors().add(otherPredecessor);
+              otherPredecessor.replaceSuccessor(block, joinBlock);
+              block.getPredecessors().set(otherPredecessorIndex, joinBlock);
+            }
+            joinBlock.getPredecessors().add(predecessor);
+            predecessor.replaceSuccessor(block, joinBlock);
+            operandsToRemove.add(operandIndex);
+          } else {
+            // Record the value and its predecessor index.
+            values.put(v, operandIndex);
+          }
+        }
+      }
+      block.removePredecessorsByIndex(operandsToRemove);
+      block.removePhisByIndex(operandsToRemove);
+    }
+    blocks.addAll(blocksToAdd);
+  }
+
+  private void splitCriticalEdges() {
+    List<BasicBlock> newBlocks = new ArrayList<>();
+    for (BasicBlock block : blocks) {
+      // We are using a spilling register allocator that might need to insert moves at
+      // all critical edges, so we always split them all.
+      List<BasicBlock> predecessors = block.getPredecessors();
+      if (predecessors.size() <= 1) {
+        continue;
+      }
+      // If any of the edges to the block are critical, we need to insert new blocks on each
+      // containing the move-exception instruction which must remain the first instruction.
+      if (block.entry() instanceof MoveException) {
+        block.splitCriticalExceptioEdges(valueNumberGenerator,
+            newBlock -> {
+              newBlock.setNumber(blocks.size() + newBlocks.size());
+              newBlocks.add(newBlock);
+            });
+        continue;
+      }
+      for (int predIndex = 0; predIndex < predecessors.size(); predIndex++) {
+        BasicBlock pred = predecessors.get(predIndex);
+        if (!pred.hasOneNormalExit()) {
+          // Critical edge: split it and inject a new block into which the
+          // phi moves can be inserted. The new block is created with the
+          // correct predecessor and successor structure. It is inserted
+          // at the end of the list of blocks disregarding branching
+          // structure.
+          int blockNumber = blocks.size() + newBlocks.size();
+          BasicBlock newBlock = BasicBlock.createGotoBlock(block, blockNumber);
+          newBlocks.add(newBlock);
+          pred.replaceSuccessor(block, newBlock);
+          newBlock.getPredecessors().add(pred);
+          predecessors.set(predIndex, newBlock);
+        }
+      }
+    }
+    blocks.addAll(newBlocks);
+  }
+
+  /**
+   * Trace blocks and attempt to put fallthrough blocks immediately after the block that
+   * falls through. When we fail to do that we create a new fallthrough block with an explicit
+   * goto to the actual fallthrough block.
+   */
+  private void traceBlocks(IRCode code) {
+    BasicBlock[] sorted = code.topologicallySortedBlocks();
+    code.clearMarks();
+    int nextBlockNumber = blocks.size();
+    LinkedList<BasicBlock> tracedBlocks = new LinkedList<>();
+    for (BasicBlock block : sorted) {
+      if (!block.isMarked()) {
+        block.mark();
+        tracedBlocks.add(block);
+        BasicBlock current = block;
+        BasicBlock fallthrough = block.exit().fallthroughBlock();
+        while (fallthrough != null && !fallthrough.isMarked()) {
+          fallthrough.mark();
+          tracedBlocks.add(fallthrough);
+          current = fallthrough;
+          fallthrough = fallthrough.exit().fallthroughBlock();
+        }
+        if (fallthrough != null) {
+          BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++);
+          current.exit().setFallthroughBlock(newFallthrough);
+          newFallthrough.getPredecessors().add(current);
+          fallthrough.replacePredecessor(current, newFallthrough);
+          newFallthrough.mark();
+          tracedBlocks.add(newFallthrough);
+        }
+      }
+    }
+    code.blocks = tracedBlocks;
+  }
+
+  // Debug info helpers.
+
+  public void updateCurrentDebugPosition(int line, DexString file) {
+    // Stack-trace support requires position information in both debug and release mode.
+    flushCurrentDebugPosition();
+    currentDebugPosition = new DebugPosition(line, file);
+  }
+
+  private void flushCurrentDebugPosition() {
+    if (currentDebugPosition != null) {
+      DebugPosition position = currentDebugPosition;
+      currentDebugPosition = null;
+      addInstruction(position);
+    }
+  }
+
+  // Other stuff.
+
+  boolean isIntegerType(NumericType type) {
+    return type != NumericType.FLOAT && type != NumericType.DOUBLE;
+  }
+
+  boolean isNonLongIntegerType(NumericType type) {
+    return type != NumericType.FLOAT && type != NumericType.DOUBLE && type != NumericType.LONG;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append(("blocks:\n"));
+    for (BasicBlock block : blocks) {
+      builder.append(block.toDetailedString());
+      builder.append("\n");
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
new file mode 100644
index 0000000..b01fad8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -0,0 +1,562 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.Flavor.IncludeAllResources;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover;
+import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.InliningConstraint;
+import com.android.tools.r8.ir.optimize.MemberValuePropagation;
+import com.android.tools.r8.ir.optimize.Outliner;
+import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.function.BiConsumer;
+
+public class IRConverter {
+
+  public static final int PEEPHOLE_OPTIMIZATION_PASSES = 2;
+
+  private final Timing timing;
+  public final DexApplication application;
+  public final AppInfo appInfo;
+  private final Outliner outliner;
+  private final LambdaRewriter lambdaRewriter;
+  private final InterfaceMethodRewriter interfaceMethodRewriter;
+  private final InternalOptions options;
+  private final CfgPrinter printer;
+  private final GraphLense graphLense;
+  private final CodeRewriter codeRewriter;
+  private final MemberValuePropagation memberValuePropagation;
+  private final LensCodeRewriter lensCodeRewriter;
+  private final Inliner inliner;
+  private CallGraph callGraph;
+
+  private DexString highestSortingString;
+
+  public IRConverter(
+      DexApplication application,
+      AppInfo appInfo,
+      InternalOptions options,
+      CfgPrinter printer,
+      boolean enableDesugaring) {
+    this.timing = new Timing("Testing");
+    this.application = application;
+    this.appInfo = appInfo;
+    this.options = options;
+    this.printer = printer;
+    this.graphLense = GraphLense.getIdentityLense();
+    this.inliner = null;
+    this.outliner = null;
+    this.codeRewriter = new CodeRewriter(appInfo);
+    this.memberValuePropagation = null;
+    this.lambdaRewriter = enableDesugaring ? new LambdaRewriter(this) : null;
+    this.interfaceMethodRewriter =
+        (enableDesugaring && enableInterfaceMethodDesugaring())
+            ? new InterfaceMethodRewriter(this) : null;
+    lensCodeRewriter = null;
+    markLibraryMethodsReturningReceiver();
+  }
+
+  public IRConverter(
+      DexApplication application, AppInfo appInfo, InternalOptions options, CfgPrinter printer) {
+    this(application, appInfo, options, printer, true);
+  }
+
+  public IRConverter(
+      Timing timing,
+      DexApplication application,
+      AppInfoWithSubtyping appInfo,
+      InternalOptions options,
+      CfgPrinter printer,
+      GraphLense graphLense) {
+    this.timing = timing;
+    this.application = application;
+    this.appInfo = appInfo;
+    this.options = options;
+    this.printer = printer;
+    this.graphLense = graphLense;
+    this.codeRewriter = new CodeRewriter(appInfo);
+    this.outliner = new Outliner(appInfo, options);
+    this.memberValuePropagation = new MemberValuePropagation(appInfo);
+    this.inliner = new Inliner(appInfo, options);
+    this.lambdaRewriter = new LambdaRewriter(this);
+    this.interfaceMethodRewriter = enableInterfaceMethodDesugaring()
+        ? new InterfaceMethodRewriter(this) : null;
+    lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo);
+    markLibraryMethodsReturningReceiver();
+  }
+
+  private boolean enableInterfaceMethodDesugaring() {
+    switch (options.interfaceMethodDesugaring) {
+      case Off:
+        return false;
+      case Auto:
+        return !options.canUseDefaultAndStaticInterfaceMethods();
+    }
+    throw new Unreachable();
+  }
+
+  private void markLibraryMethodsReturningReceiver() {
+    DexItemFactory dexItemFactory = appInfo.dexItemFactory;
+    dexItemFactory.stringBuilderMethods.forEeachAppendMethod(this::markReturnsReceiver);
+    dexItemFactory.stringBufferMethods.forEeachAppendMethod(this::markReturnsReceiver);
+  }
+
+  private void markReturnsReceiver(DexMethod method) {
+    DexEncodedMethod definition = appInfo.definitionFor(method);
+    if (definition != null) {
+      definition.markReturnsArgument(0);
+    }
+  }
+
+  public DexApplication convertToDex() {
+    convertClassesToDex(application.classes());
+
+    // Build a new application with jumbo string info,
+    Builder builder = new Builder(application);
+    builder.setHighestSortingString(highestSortingString);
+
+    // Lambda rewriter
+    if (lambdaRewriter != null) {
+      lambdaRewriter.adjustAccessibility(builder);
+      lambdaRewriter.synthesizeLambdaClasses(builder);
+    }
+
+    if (interfaceMethodRewriter != null) {
+      interfaceMethodRewriter.desugarInterfaceMethods(builder, ExcludeDexResources);
+    }
+
+    return builder.build();
+  }
+
+  private void convertClassesToDex(Iterable<DexProgramClass> classes) {
+    for (DexProgramClass clazz : classes) {
+      convertMethodsToDex(clazz.directMethods());
+      convertMethodsToDex(clazz.virtualMethods());
+    }
+  }
+
+  private void convertMethodsToDex(DexEncodedMethod[] methods) {
+    for (int i = 0; i < methods.length; i++) {
+      DexEncodedMethod method = methods[i];
+      if (method.getCode() != null) {
+        boolean matchesMethodFilter = options.methodMatchesFilter(method);
+        if (matchesMethodFilter) {
+          if (method.getCode().isJarCode()) {
+            rewriteCode(method, Outliner::noProcessing);
+          }
+          updateHighestSortingStrings(method);
+        }
+      }
+    }
+  }
+
+  public DexApplication optimize() throws ExecutionException {
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+    try {
+      return optimize(executor);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  public DexApplication optimize(ExecutorService executorService) throws ExecutionException {
+    timing.begin("Build call graph");
+    callGraph = CallGraph.build(application, appInfo.withSubtyping(), graphLense);
+    timing.end();
+
+    // The process is in two phases.
+    // 1) Subject all DexEncodedMethods to optimization (except outlining).
+    //    - a side effect is candidates for outlining are identified.
+    // 2) Perform outlining for the collected candidates.
+    // Ideally, we should outline eagerly when threshold for a template has been reached.
+
+    // Process the application identifying outlining candidates.
+    timing.begin("IR conversion phase 1");
+    while (!callGraph.isEmpty()) {
+      CallGraph.Leaves leaves = callGraph.pickLeaves();
+      List<DexEncodedMethod> leafMethods = leaves.getLeaves();
+      assert leafMethods.size() > 0;
+      // If cycles where broken to produce leaves, don't do parallel processing to keep
+      // deterministic output.
+      // TODO(37133161): Most likely the failing:
+      //  java.com.android.tools.r8.internal.R8GMSCoreDeterministicTest
+      // is caused by processing in multiple threads.
+      if (true || leaves.brokeCycles()) {
+        for (DexEncodedMethod method : leafMethods) {
+          processMethod(method,
+              outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
+        }
+      } else {
+        List<Future<?>> futures = new ArrayList<>();
+        // For testing we have the option to randomize the processing order for the
+        // deterministic tests.
+        if (options.testing.randomizeCallGraphLeaves) {
+          Collections.shuffle(leafMethods, new Random(System.nanoTime()));
+        }
+        for (DexEncodedMethod method : leafMethods) {
+          futures.add(executorService.submit(() -> {
+            processMethod(method,
+                outliner == null ? Outliner::noProcessing : outliner::identifyCandidates);
+          }));
+        }
+        ThreadUtils.awaitFutures(futures);
+      }
+    }
+    timing.end();
+
+    // Build a new application with jumbo string info.
+    Builder builder = new Builder(application);
+    builder.setHighestSortingString(highestSortingString);
+
+    // Second inlining pass.
+    if ((inliner != null) && (inliner.doubleInlineCallers.size() > 0)) {
+      inliner.applyDoubleInlining = true;
+      for (DexEncodedMethod method : inliner.doubleInlineCallers) {
+        method.markNotProcessed();
+        processMethod(method, Outliner::noProcessing);
+        assert method.isProcessed();
+      }
+    }
+
+    // Lambda rewriter.
+    if (lambdaRewriter != null) {
+      lambdaRewriter.adjustAccessibility(builder);
+      lambdaRewriter.synthesizeLambdaClasses(builder);
+    }
+
+    if (interfaceMethodRewriter != null) {
+      interfaceMethodRewriter.desugarInterfaceMethods(builder, IncludeAllResources);
+    }
+
+    if (outliner != null) {
+      timing.begin("IR conversion phase 2");
+      // Compile all classes flagged for outlining and
+      // add the outline support class IF needed.
+      DexProgramClass outlineClass = prepareOutlining();
+      if (outlineClass != null) {
+        // Process the selected methods for outlining.
+        for (DexEncodedMethod method : outliner.getMethodsSelectedForOutlining()) {
+          // This is the second time we compile this method first mark it not processed.
+          assert !method.getCode().isOutlineCode();
+          method.markNotProcessed();
+          processMethod(method, outliner::applyOutliningCandidate);
+          assert method.isProcessed();
+        }
+        builder.addSynthesizedClass(outlineClass, true);
+        clearDexMethodCompilationState(outlineClass);
+      }
+      timing.end();
+    }
+    clearDexMethodCompilationState();
+    return builder.build();
+  }
+
+  public void processJumboStrings(DexEncodedMethod method, DexString firstJumboString) {
+    convertMethodJumboStringsOnly(method, firstJumboString);
+  }
+
+  private void clearDexMethodCompilationState() {
+    application.classes().forEach(this::clearDexMethodCompilationState);
+  }
+
+  private void clearDexMethodCompilationState(DexProgramClass clazz) {
+    Arrays.stream(clazz.directMethods()).forEach(DexEncodedMethod::markNotProcessed);
+    Arrays.stream(clazz.virtualMethods()).forEach(DexEncodedMethod::markNotProcessed);
+  }
+
+  /**
+   * This will replace the Dex code in the method with the Dex code generated from the provided IR.
+   *
+   * This method is *only* intended for testing, where tests manipulate the IR and need runnable Dex
+   * code.
+   *
+   * @param method the method to replace code for
+   * @param code the IR code for the method
+   */
+  public void replaceCodeForTesting(DexEncodedMethod method, IRCode code) {
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
+    }
+    assert code.isConsistentSSA();
+    RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
+    method.setCode(code, registerAllocator);
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Resulting dex code for %s:\n%s",
+          method.toSourceString(), logCode(options, method));
+    }
+  }
+
+  // Find an unused name for the outlining class. When multiple runs produces additional
+  // outlining the default outlining class might already be present.
+  private DexType computeOutlineClassType() {
+    DexType result;
+    int count = 0;
+    do {
+      String name = options.outline.className + (count == 0 ? "" : Integer.toString(count));
+      count++;
+      result = application.dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(name));
+    } while (application.definitionFor(result) != null);
+    return result;
+  }
+
+  private DexProgramClass prepareOutlining() {
+    if (!outliner.selectMethodsForOutlining()) {
+      return null;
+    }
+    DexProgramClass outlineClass = outliner.buildOutlinerClass(computeOutlineClassType());
+    optimizeSynthesizedClass(outlineClass);
+    return outlineClass;
+  }
+
+  public void optimizeSynthesizedClass(DexProgramClass clazz) {
+    // Process the generated class, but don't apply any outlining.
+    for (DexEncodedMethod method : clazz.directMethods()) {
+      optimizeSynthesizedMethod(method);
+    }
+    for (DexEncodedMethod method : clazz.virtualMethods()) {
+      optimizeSynthesizedMethod(method);
+    }
+  }
+
+  public void optimizeSynthesizedMethod(DexEncodedMethod method) {
+    // Process the generated method, but don't apply any outlining.
+    processMethod(method, Outliner::noProcessing);
+  }
+
+  private String logCode(InternalOptions options, DexEncodedMethod method) {
+    return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
+  }
+
+  private void processMethod(
+      DexEncodedMethod method, BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
+    Code code = method.getCode();
+    boolean matchesMethodFilter = options.methodMatchesFilter(method);
+    if (code != null && matchesMethodFilter) {
+      assert !method.isProcessed();
+      InliningConstraint state = rewriteCode(method, outlineHandler);
+      method.markProcessed(state);
+    } else {
+      // Mark abstract methods as processed as well.
+      method.markProcessed(InliningConstraint.NEVER);
+    }
+  }
+
+  private InliningConstraint rewriteCode(DexEncodedMethod method,
+      BiConsumer<IRCode, DexEncodedMethod> outlineHandler) {
+    if (options.verbose) {
+      System.out.println("Processing: " + method.toSourceString());
+    }
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Original code for %s:\n%s",
+          method.toSourceString(), logCode(options, method));
+    }
+    IRCode code = method.buildIR(options);
+    if (code == null) {
+      return InliningConstraint.NEVER;
+    }
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
+    }
+    // Compilation header if printing CFGs for this method.
+    printC1VisualizerHeader(method);
+    printMethod(code, "Initial IR (SSA)");
+
+    if (lensCodeRewriter != null) {
+      lensCodeRewriter.rewrite(code, method);
+    } else {
+      assert graphLense.isIdentityLense();
+    }
+    if (memberValuePropagation != null) {
+      memberValuePropagation.rewriteWithConstantValues(code);
+    }
+    if (options.inlineAccessors && inliner != null) {
+      inliner.performInlining(method, code, callGraph);
+    }
+    codeRewriter.rewriteLongCompareAndRequireNonNull(code, options.canUseObjectsNonNull());
+    codeRewriter.commonSubexpressionElimination(code);
+    codeRewriter.simplifyArrayConstruction(code);
+    codeRewriter.rewriteMoveResult(code);
+    codeRewriter.splitConstants(code);
+    codeRewriter.foldConstants(code);
+    codeRewriter.simplifyIf(code);
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Intermediate (SSA) flow graph for %s:\n%s",
+          method.toSourceString(), code);
+    }
+    codeRewriter.removeUnneededCatchHandlers(code);
+    // Dead code removal. Performed after simplifications to remove code that becomes dead
+    // as a result of those simplifications. The following optimizations could reveal more
+    // dead code which is removed right before register allocation in performRegisterAllocation.
+    DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+    assert code.isConsistentSSA();
+
+    if (lambdaRewriter != null) {
+      lambdaRewriter.desugarLambdas(method, code);
+      assert code.isConsistentSSA();
+    }
+
+    if (interfaceMethodRewriter != null) {
+      interfaceMethodRewriter.rewriteMethodReferences(method, code);
+      assert code.isConsistentSSA();
+    }
+
+    if (options.outline.enabled) {
+      outlineHandler.accept(code, method);
+      assert code.isConsistentSSA();
+    }
+
+    codeRewriter.shortenLiveRanges(code);
+    codeRewriter.identifyReturnsArgument(method, code);
+
+    printMethod(code, "Optimized IR (SSA)");
+    // Perform register allocation.
+    RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
+    method.setCode(code, registerAllocator);
+    updateHighestSortingStrings(method);
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Resulting dex code for %s:\n%s",
+          method.toSourceString(), logCode(options, method));
+    }
+    printMethod(code, "Final IR (non-SSA)");
+
+    // After all the optimizations have take place, we compute whether method should be inlined.
+    if (!options.inlineAccessors || inliner == null) {
+      return InliningConstraint.NEVER;
+    }
+    return inliner.identifySimpleMethods(code, method);
+  }
+
+  private void updateHighestSortingStrings(DexEncodedMethod method) {
+    DexString highestSortingReferencedString = method.getCode().asDexCode().highestSortingString;
+    if (highestSortingReferencedString != null) {
+      if (highestSortingString == null
+          || highestSortingReferencedString.slowCompareTo(highestSortingString) > 0) {
+        highestSortingString = highestSortingReferencedString;
+      }
+    }
+  }
+
+  // Convert a method ensuring that strings sorting equal or higher than the argument
+  // firstJumboString are encoded as jumbo strings.
+  // TODO(sgjesse): Consider replacing this with a direct dex2dex converter instead of going
+  // through IR.
+  private void convertMethodJumboStringsOnly(
+      DexEncodedMethod method, DexString firstJumboString) {
+    // This is only used for methods already converted to Dex, but missing jumbo strings.
+    assert method.getCode() != null && method.getCode().isDexCode();
+    if (options.verbose) {
+      System.out.println("Processing jumbo strings: " + method.toSourceString());
+    }
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Original code for %s:\n%s",
+          method.toSourceString(), logCode(options, method));
+    }
+    IRCode code = method.buildIR(options);
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s",
+          method.toSourceString(), code);
+    }
+    // Compilation header if printing CFGs for this method.
+    printC1VisualizerHeader(method);
+    printMethod(code, "Initial IR (SSA)");
+
+    // Methods passed through here should have been through IR processing already and
+    // therefore, we skip most of the IR processing.
+
+    // Perform register allocation.
+    RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
+    method.setCode(code, registerAllocator, firstJumboString);
+
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Resulting dex code for %s:\n%s",
+          method.toSourceString(), logCode(options, method));
+    }
+    printMethod(code, "Final IR (non-SSA)");
+  }
+
+  private RegisterAllocator performRegisterAllocation(IRCode code, DexEncodedMethod method) {
+    // Always perform dead code elimination before register allocation. The register allocator
+    // does not allow dead code (to make sure that we do not waste registers for unneeded values).
+    DeadCodeRemover.removeDeadCode(code, codeRewriter, options);
+    LinearScanRegisterAllocator registerAllocator = new LinearScanRegisterAllocator(code);
+    registerAllocator.allocateRegisters(options.debug);
+    printMethod(code, "After register allocation (non-SSA)");
+    printLiveRanges(registerAllocator, "Final live ranges.");
+    if (!options.debug) {
+      CodeRewriter.removedUnneededDebugPositions(code);
+    }
+    for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
+      CodeRewriter.collapsTrivialGotos(method, code);
+      PeepholeOptimizer.optimize(code, registerAllocator);
+    }
+    CodeRewriter.collapsTrivialGotos(method, code);
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Final (non-SSA) flow graph for %s:\n%s",
+          method.toSourceString(), code);
+    }
+    return registerAllocator;
+  }
+
+  private void printC1VisualizerHeader(DexEncodedMethod method) {
+    if (printer != null) {
+      printer.begin("compilation");
+      printer.print("name \"").append(method.toSourceString()).append("\"").ln();
+      printer.print("method \"").append(method.toSourceString()).append("\"").ln();
+      printer.print("date 0").ln();
+      printer.end("compilation");
+    }
+  }
+
+  private void printMethod(IRCode code, String title) {
+    if (printer != null) {
+      printer.resetUnusedValue();
+      printer.begin("cfg");
+      printer.print("name \"").append(title).append("\"\n");
+      code.print(printer);
+      printer.end("cfg");
+    }
+  }
+
+  private void printLiveRanges(LinearScanRegisterAllocator allocator, String title) {
+    if (printer != null) {
+      allocator.print(printer, title);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
new file mode 100644
index 0000000..5647878
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -0,0 +1,2097 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.Monitor;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.conversion.JarState.Local;
+import com.android.tools.r8.ir.conversion.JarState.Slot;
+import com.android.tools.r8.logging.Log;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.IincInsnNode;
+import org.objectweb.asm.tree.InsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.InvokeDynamicInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.LineNumberNode;
+import org.objectweb.asm.tree.LocalVariableNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.MultiANewArrayInsnNode;
+import org.objectweb.asm.tree.TableSwitchInsnNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+import org.objectweb.asm.tree.VarInsnNode;
+import org.objectweb.asm.util.Textifier;
+import org.objectweb.asm.util.TraceMethodVisitor;
+
+
+public class JarSourceCode implements SourceCode {
+
+  // Try-catch block wrapper containing resolved offsets.
+  private static class TryCatchBlock {
+
+    private final int handler;
+    private final int start;
+    private final int end;
+
+    private final String type;
+
+    public TryCatchBlock(TryCatchBlockNode node, JarSourceCode code) {
+      this(code.getOffset(node.handler),
+          code.getOffset(node.start),
+          code.getOffset(node.end),
+          node.type);
+    }
+
+    private TryCatchBlock(int handler, int start, int end, String type) {
+      assert start < end;
+      this.handler = handler;
+      this.start = start;
+      this.end = end;
+      this.type = type;
+    }
+
+    int getStart() {
+      return start;
+    }
+
+    int getEnd() {
+      return end;
+    }
+
+    int getHandler() {
+      return handler;
+    }
+
+    String getType() {
+      return type;
+    }
+  }
+
+  // Various descriptors.
+  private static final String INT_ARRAY_DESC = "[I";
+  private static final String REFLECT_ARRAY_DESC = "Ljava/lang/reflect/Array;";
+  private static final String REFLECT_ARRAY_NEW_INSTANCE_NAME = "newInstance";
+  private static final String REFLECT_ARRAY_NEW_INSTANCE_DESC =
+      "(Ljava/lang/Class;[I)Ljava/lang/Object;";
+  private static final String METHODHANDLE_INVOKE_OR_INVOKEEXACT_DESC =
+      "([Ljava/lang/Object;)Ljava/lang/Object;";
+
+  // Language types.
+  private static final Type CLASS_TYPE = Type.getObjectType("java/lang/Class");
+  private static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
+  private static final Type INT_ARRAY_TYPE = Type.getObjectType(INT_ARRAY_DESC);
+  private static final Type THROWABLE_TYPE = Type.getObjectType("java/lang/Throwable");
+
+  private static final int[] NO_TARGETS = {};
+
+  private final JarApplicationReader application;
+  private final MethodNode node;
+  private final DexType clazz;
+  private final List<Type> parameterTypes;
+  private final LabelNode initialLabel;
+
+  private TraceMethodVisitor printVisitor = null;
+
+  private final JarState state;
+  private AbstractInsnNode currentInstruction = null;
+
+  // Special try-catch block for synchronized methods.
+  // This block is given a negative instruction index as it is not part of the instruction stream.
+  // The start range of 0 ensures that a new block will start at the first real instruction and
+  // thus that the monitor-entry prelude (part of the argument block which must not have a try-catch
+  // successor) is not joined with the first instruction block (which likely will have a try-catch
+  // successor).
+  private static final int EXCEPTIONAL_SYNC_EXIT_OFFSET = -1;
+  private static final TryCatchBlock EXCEPTIONAL_SYNC_EXIT =
+      new TryCatchBlock(EXCEPTIONAL_SYNC_EXIT_OFFSET, 0, Integer.MAX_VALUE, null);
+
+  // Instruction that enters the monitor. Null if the method is not synchronized.
+  private Monitor monitorEnter = null;
+
+  // State to signal that the code currently being emitted is part of synchronization prelude/exits.
+  private boolean generatingMethodSynchronization = false;
+
+  public JarSourceCode(DexType clazz, MethodNode node, JarApplicationReader application) {
+    assert node != null;
+    assert node.desc != null;
+    this.node = node;
+    this.application = application;
+    this.clazz = clazz;
+    parameterTypes = Arrays.asList(Type.getArgumentTypes(node.desc));
+    state = new JarState(node.maxLocals, computeLocals(node.localVariables, application));
+    AbstractInsnNode first = node.instructions.getFirst();
+    initialLabel = first instanceof LabelNode ? (LabelNode) first : null;
+  }
+
+  private static Map<LocalVariableNode, DebugLocalInfo> computeLocals(
+      List localNodes, JarApplicationReader application) {
+    Map<DebugLocalInfo, DebugLocalInfo> canonical = new HashMap<>(localNodes.size());
+    Map<LocalVariableNode, DebugLocalInfo> localVariables = new HashMap<>(localNodes.size());
+    for (Object o : localNodes) {
+      LocalVariableNode node = (LocalVariableNode) o;
+      localVariables.computeIfAbsent(node, n -> canonicalizeLocal(n, canonical, application));
+    }
+    return localVariables;
+  }
+
+  private static DebugLocalInfo canonicalizeLocal(
+      LocalVariableNode node,
+      Map<DebugLocalInfo, DebugLocalInfo> canonicalLocalVariables,
+      JarApplicationReader application) {
+    DebugLocalInfo info = new DebugLocalInfo(
+        application.getString(node.name),
+        application.getType(Type.getType(node.desc)),
+        node.signature == null ? null : application.getString(node.signature));
+    DebugLocalInfo canonical = canonicalLocalVariables.putIfAbsent(info, info);
+    return canonical != null ? canonical : info;
+  }
+
+  private boolean isStatic() {
+    return (node.access & Opcodes.ACC_STATIC) > 0;
+  }
+
+  private boolean isSynchronized() {
+    return (node.access & Opcodes.ACC_SYNCHRONIZED) > 0;
+  }
+
+  private int formalParameterCount() {
+    return parameterTypes.size();
+  }
+
+  private int actualArgumentCount() {
+    return isStatic() ? formalParameterCount() : formalParameterCount() + 1;
+  }
+
+  @Override
+  public int instructionCount() {
+    return node.instructions.size();
+  }
+
+  @Override
+  public int instructionIndex(int instructionOffset) {
+    return instructionOffset;
+  }
+
+  @Override
+  public int instructionOffset(int instructionIndex) {
+    return instructionIndex;
+  }
+
+  @Override
+  public boolean verifyRegister(int register) {
+    // The register set is dynamically managed by the state so we assume all values valid here.
+    return true;
+  }
+
+  @Override
+  public void setUp() {
+    if (Log.ENABLED) {
+      Log.debug(JarSourceCode.class, "Computing blocks for:\n" + toString());
+    }
+  }
+
+  @Override
+  public void clear() {
+
+  }
+
+  @Override
+  public boolean needsPrelude() {
+    return isSynchronized() || actualArgumentCount() > 0 || !node.localVariables.isEmpty();
+  }
+
+  @Override
+  public void buildPrelude(IRBuilder builder) {
+    Map<Integer, MoveType> initializedLocals = new HashMap<>(node.localVariables.size());
+    if (initialLabel != null) {
+      state.openLocals(initialLabel);
+    }
+    int argumentRegister = 0;
+    if (!isStatic()) {
+      Type thisType = Type.getType(clazz.descriptor.toString());
+      int register = state.writeLocal(argumentRegister++, thisType);
+      builder.addThisArgument(register);
+      initializedLocals.put(register, moveType(thisType));
+    }
+    for (Type type : parameterTypes) {
+      MoveType moveType = moveType(type);
+      int register = state.writeLocal(argumentRegister, type);
+      builder.addNonThisArgument(register, moveType);
+      argumentRegister += moveType.requiredRegisters();
+      initializedLocals.put(register, moveType);
+    }
+    if (isSynchronized()) {
+      generatingMethodSynchronization = true;
+      Type clazzType = Type.getType(clazz.toDescriptorString());
+      int monitorRegister;
+      if (isStatic()) {
+        // Load the class using a temporary on the stack.
+        monitorRegister = state.push(clazzType);
+        state.pop();
+        builder.addConstClass(monitorRegister, clazz);
+      } else {
+        assert actualArgumentCount() > 0;
+        // The object is stored in the first local.
+        monitorRegister = state.readLocal(0, clazzType).register;
+      }
+      // Build the monitor enter and save it for when generating exits later.
+      monitorEnter = builder.addMonitor(Monitor.Type.ENTER, monitorRegister);
+      generatingMethodSynchronization = false;
+    }
+    // Initialize all non-argument locals to ensure safe insertion of debug-local instructions.
+    for (Object o : node.localVariables) {
+      LocalVariableNode local = (LocalVariableNode) o;
+      Type localType = Type.getType(local.desc);
+      int localRegister = state.getLocalRegister(local.index, localType);
+      MoveType exitingLocalType = initializedLocals.get(localRegister);
+      assert exitingLocalType == null || exitingLocalType == moveType(localType);
+      if (exitingLocalType == null) {
+        int localRegister2 = state.writeLocal(local.index, localType);
+        assert localRegister == localRegister2;
+        initializedLocals.put(localRegister, moveType(localType));
+        builder.addDebugUninitialized(localRegister, constType(localType));
+      }
+    }
+  }
+
+  @Override
+  public void buildPostlude(IRBuilder builder) {
+    if (isSynchronized()) {
+      generatingMethodSynchronization = true;
+      buildMonitorExit(builder);
+      generatingMethodSynchronization = false;
+    }
+  }
+
+  private void buildExceptionalPostlude(IRBuilder builder) {
+    assert isSynchronized();
+    assert state.isInvalid();
+    generatingMethodSynchronization = true;
+    int exceptionRegister = 0; // We are exiting the method so we just overwrite register 0.
+    builder.addMoveException(exceptionRegister);
+    buildMonitorExit(builder);
+    builder.addThrow(exceptionRegister);
+    generatingMethodSynchronization = false;
+  }
+
+  private void buildMonitorExit(IRBuilder builder) {
+    assert generatingMethodSynchronization;
+    builder.add(new Monitor(Monitor.Type.EXIT, monitorEnter.inValues().get(0)));
+  }
+
+  @Override
+  public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+    assert !state.isInvalid();
+    state.recordStateForTarget(fallthroughInstructionIndex, this);
+    state.invalidateState();
+  }
+
+  @Override
+  public void closedCurrentBlock() {
+    assert state.isInvalid();
+  }
+
+  @Override
+  public void buildInstruction(IRBuilder builder, int instructionIndex) {
+    if (instructionIndex == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
+      buildExceptionalPostlude(builder);
+      return;
+    }
+    AbstractInsnNode insn = getInstruction(instructionIndex);
+    currentInstruction = insn;
+    assert verifyExceptionEdgesAreRecorded(insn);
+
+    // Restore the state if invalid.
+    if (state.isInvalid()) {
+      state.restoreState(instructionIndex);
+      // If the block being restored is a try-catch handler push the exception on the stack.
+      for (int i = 0; i < node.tryCatchBlocks.size(); i++) {
+        TryCatchBlockNode tryCatchBlockNode = (TryCatchBlockNode) node.tryCatchBlocks.get(i);
+        if (tryCatchBlockNode.handler == insn) {
+          builder.addMoveException(state.push(THROWABLE_TYPE));
+          break;
+        }
+      }
+    }
+
+    String preInstructionState;
+    if (Log.ENABLED) {
+      preInstructionState = state.toString();
+    }
+
+    build(insn, builder);
+
+    // Process local-variable end scopes if this instruction is not a control-flow instruction.
+    // For control-flow instructions, processing takes place before closing their respective blocks.
+    if (!isControlFlowInstruction(insn)) {
+      processLocalVariableEnd(insn, builder);
+    }
+
+    if (Log.ENABLED && !(insn instanceof LineNumberNode)) {
+      int offset = getOffset(insn);
+      if (insn instanceof LabelNode) {
+        Log.debug(getClass(), "\n%4d: %s",
+            offset, instructionToString(insn).replace('\n', ' '));
+      } else {
+        Log.debug(getClass(), "\n%4d: %s          pre:  %s\n          post: %s",
+            offset, instructionToString(insn), preInstructionState, state);
+      }
+    }
+  }
+
+  private boolean verifyExceptionEdgesAreRecorded(AbstractInsnNode insn) {
+    if (canThrow(insn)) {
+      for (TryCatchBlock tryCatchBlock : getTryHandlers(insn)) {
+        assert tryCatchBlock.getHandler() == EXCEPTIONAL_SYNC_EXIT_OFFSET
+            || state.hasState(tryCatchBlock.getHandler());
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public void resolveAndBuildSwitch(Switch.Type type, int value, int fallthroughOffset,
+      int payloadOffset, IRBuilder builder) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
+      IRBuilder builder) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public DebugLocalInfo getCurrentLocal(int register) {
+    return state.getLocalInfoForRegister(register);
+  }
+
+  @Override
+  public CatchHandlers<Integer> getCurrentCatchHandlers() {
+    if (generatingMethodSynchronization) {
+      return null;
+    }
+    List<TryCatchBlock> tryCatchBlocks = getTryHandlers(currentInstruction);
+    if (tryCatchBlocks.isEmpty()) {
+      return null;
+    }
+    // TODO(zerny): Compute this more efficiently.
+    return new CatchHandlers<>(
+        getTryHandlerGuards(tryCatchBlocks),
+        getTryHandlerOffsets(tryCatchBlocks));
+  }
+
+  @Override
+  public boolean verifyCurrentInstructionCanThrow() {
+    return generatingMethodSynchronization || canThrow(currentInstruction);
+  }
+
+  @Override
+  public boolean verifyLocalInScope(DebugLocalInfo local) {
+    for (Local open : state.getLocals()) {
+      if (open.info != null && open.info.name == local.name) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private AbstractInsnNode getInstruction(int index) {
+    return node.instructions.get(index);
+  }
+
+  private static boolean isReturn(AbstractInsnNode insn) {
+    return Opcodes.IRETURN <= insn.getOpcode() && insn.getOpcode() <= Opcodes.RETURN;
+  }
+
+  private static boolean isSwitch(AbstractInsnNode insn) {
+    return Opcodes.TABLESWITCH == insn.getOpcode() || insn.getOpcode() == Opcodes.LOOKUPSWITCH;
+  }
+
+  private static boolean isThrow(AbstractInsnNode insn) {
+    return Opcodes.ATHROW == insn.getOpcode();
+  }
+
+  private static boolean isControlFlowInstruction(AbstractInsnNode insn) {
+    return isReturn(insn) || isThrow(insn) || isSwitch(insn) || (insn instanceof JumpInsnNode)
+        || insn.getOpcode() == Opcodes.RET;
+  }
+
+  private boolean canThrow(AbstractInsnNode insn) {
+    switch (insn.getOpcode()) {
+      case Opcodes.AALOAD:
+      case Opcodes.AASTORE:
+      case Opcodes.ANEWARRAY:
+        // ARETURN does not throw in its dex image.
+      case Opcodes.ARRAYLENGTH:
+      case Opcodes.ATHROW:
+      case Opcodes.BALOAD:
+      case Opcodes.BASTORE:
+      case Opcodes.CALOAD:
+      case Opcodes.CASTORE:
+      case Opcodes.CHECKCAST:
+      case Opcodes.DALOAD:
+      case Opcodes.DASTORE:
+        // DRETURN does not throw in its dex image.
+      case Opcodes.FALOAD:
+      case Opcodes.FASTORE:
+        // FRETURN does not throw in its dex image.
+      case Opcodes.GETFIELD:
+      case Opcodes.GETSTATIC:
+      case Opcodes.IALOAD:
+      case Opcodes.IASTORE:
+      case Opcodes.IDIV:
+      case Opcodes.INSTANCEOF:
+      case Opcodes.INVOKEDYNAMIC:
+      case Opcodes.INVOKEINTERFACE:
+      case Opcodes.INVOKESPECIAL:
+      case Opcodes.INVOKESTATIC:
+      case Opcodes.INVOKEVIRTUAL:
+      case Opcodes.IREM:
+        // IRETURN does not throw in its dex image.
+      case Opcodes.LALOAD:
+      case Opcodes.LASTORE:
+      case Opcodes.LDIV:
+      case Opcodes.LREM:
+        // LRETURN does not throw in its dex image.
+      case Opcodes.MONITORENTER:
+      case Opcodes.MONITOREXIT:
+      case Opcodes.MULTIANEWARRAY:
+      case Opcodes.NEW:
+      case Opcodes.NEWARRAY:
+      case Opcodes.PUTFIELD:
+      case Opcodes.PUTSTATIC:
+        // RETURN does not throw in its dex image.
+      case Opcodes.SALOAD:
+      case Opcodes.SASTORE:
+        return true;
+      case Opcodes.LDC: {
+        // const-class and const-string* may throw in dex.
+        LdcInsnNode ldc = (LdcInsnNode) insn;
+        return ldc.cst instanceof String || ldc.cst instanceof Type;
+      }
+      default:
+        return false;
+    }
+  }
+
+  @Override
+  public boolean traceInstruction(int index, IRBuilder builder) {
+    AbstractInsnNode insn = getInstruction(index);
+    // Exit early on no-op instructions.
+    if (insn instanceof LabelNode || insn instanceof LineNumberNode) {
+      return false;
+    }
+    // If this instruction exits, close this block.
+    if (isReturn(insn)) {
+      return true;
+    }
+    // For each target ensure a basic block and close this block.
+    int[] targets = getTargets(insn);
+    if (targets != NO_TARGETS) {
+      assert !canThrow(insn);
+      for (int target : targets) {
+        builder.ensureSuccessorBlock(target);
+      }
+      return true;
+    }
+    if (canThrow(insn)) {
+      List<TryCatchBlock> tryCatchBlocks = getTryHandlers(insn);
+      if (!tryCatchBlocks.isEmpty()) {
+        Set<Integer> seenHandlerOffsets = new HashSet<>();
+        for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
+          // Ensure the block starts at the start of the try-range (don't enqueue, not a target).
+          builder.ensureBlockWithoutEnqueuing(tryCatchBlock.getStart());
+          // Add edge to exceptional successor (only one edge for each unique successor).
+          int handler = tryCatchBlock.getHandler();
+          if (!seenHandlerOffsets.contains(handler)) {
+            seenHandlerOffsets.add(handler);
+            builder.ensureSuccessorBlock(handler);
+          }
+        }
+        // Edge to normal successor if any (fallthrough).
+        if (!isThrow(insn)) {
+          builder.ensureSuccessorBlock(getOffset(insn.getNext()));
+        }
+        return true;
+      }
+      // If the throwable instruction is "throw" it closes the block.
+      return isThrow(insn);
+    }
+    // This instruction does not close the block.
+    return false;
+  }
+
+  private List<TryCatchBlock> getPotentialTryHandlers(AbstractInsnNode insn) {
+    int offset = getOffset(insn);
+    return getPotentialTryHandlers(offset);
+  }
+
+  private boolean tryBlockRelevant(TryCatchBlockNode tryHandler, int offset) {
+    int start = getOffset(tryHandler.start);
+    int end = getOffset(tryHandler.end);
+    return start <= offset && offset < end;
+  }
+
+  private List<TryCatchBlock> getPotentialTryHandlers(int offset) {
+    List<TryCatchBlock> handlers = new ArrayList<>();
+    for (int i = 0; i < node.tryCatchBlocks.size(); i++) {
+      TryCatchBlockNode tryBlock = (TryCatchBlockNode) node.tryCatchBlocks.get(i);
+      if (tryBlockRelevant(tryBlock, offset)) {
+        handlers.add(new TryCatchBlock(tryBlock, this));
+      }
+    }
+    return handlers;
+  }
+
+  private List<TryCatchBlock> getTryHandlers(AbstractInsnNode insn) {
+    List<TryCatchBlock> handlers = new ArrayList<>();
+    Set<String> seen = new HashSet<>();
+    // The try-catch blocks are ordered by precedence.
+    for (TryCatchBlock tryCatchBlock : getPotentialTryHandlers(insn)) {
+      if (tryCatchBlock.getType() == null) {
+        handlers.add(tryCatchBlock);
+        return handlers;
+      }
+      if (!seen.contains(tryCatchBlock.getType())) {
+        seen.add(tryCatchBlock.getType());
+        handlers.add(tryCatchBlock);
+      }
+    }
+    if (isSynchronized()) {
+      // Add synchronized exceptional exit for synchronized-method instructions without a default.
+      assert handlers.isEmpty() || handlers.get(handlers.size() - 1).getType() != null;
+      handlers.add(EXCEPTIONAL_SYNC_EXIT);
+    }
+    return handlers;
+  }
+
+  private List<Integer> getTryHandlerOffsets(List<TryCatchBlock> tryCatchBlocks) {
+    List<Integer> offsets = new ArrayList<>();
+    for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
+      offsets.add(tryCatchBlock.getHandler());
+    }
+    return offsets;
+  }
+
+  private List<DexType> getTryHandlerGuards(List<TryCatchBlock> tryCatchBlocks) {
+    List<DexType> guards = new ArrayList<>();
+    for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
+      guards.add(tryCatchBlock.getType() == null
+          ? DexItemFactory.catchAllType
+          : application.getTypeFromName(tryCatchBlock.getType()));
+
+    }
+    return guards;
+  }
+
+  int getOffset(AbstractInsnNode insn) {
+    return node.instructions.indexOf(insn);
+  }
+
+  private int[] getTargets(AbstractInsnNode insn) {
+    switch (insn.getType()) {
+      case AbstractInsnNode.TABLESWITCH_INSN: {
+        TableSwitchInsnNode switchInsn = (TableSwitchInsnNode) insn;
+        return getSwitchTargets(switchInsn.dflt, switchInsn.labels);
+      }
+      case AbstractInsnNode.LOOKUPSWITCH_INSN: {
+        LookupSwitchInsnNode switchInsn = (LookupSwitchInsnNode) insn;
+        return getSwitchTargets(switchInsn.dflt, switchInsn.labels);
+      }
+      case AbstractInsnNode.JUMP_INSN: {
+        return getJumpTargets((JumpInsnNode) insn);
+      }
+      case AbstractInsnNode.VAR_INSN: {
+        return getVarTargets((VarInsnNode) insn);
+      }
+      default:
+        return NO_TARGETS;
+    }
+  }
+
+  private int[] getSwitchTargets(LabelNode dflt, List labels) {
+    int[] targets = new int[1 + labels.size()];
+    targets[0] = getOffset(dflt);
+    for (int i = 1; i < targets.length; i++) {
+      targets[i] = getOffset((LabelNode) labels.get(i - 1));
+    }
+    return targets;
+  }
+
+  private int[] getJumpTargets(JumpInsnNode jump) {
+    switch (jump.getOpcode()) {
+      case Opcodes.IFEQ:
+      case Opcodes.IFNE:
+      case Opcodes.IFLT:
+      case Opcodes.IFGE:
+      case Opcodes.IFGT:
+      case Opcodes.IFLE:
+      case Opcodes.IF_ICMPEQ:
+      case Opcodes.IF_ICMPNE:
+      case Opcodes.IF_ICMPLT:
+      case Opcodes.IF_ICMPGE:
+      case Opcodes.IF_ICMPGT:
+      case Opcodes.IF_ICMPLE:
+      case Opcodes.IF_ACMPEQ:
+      case Opcodes.IF_ACMPNE:
+      case Opcodes.IFNULL:
+      case Opcodes.IFNONNULL:
+        return new int[]{getOffset(jump.label), getOffset(jump.getNext())};
+      case Opcodes.GOTO:
+        return new int[]{getOffset(jump.label)};
+      case Opcodes.JSR: {
+        throw new Unreachable("JSR should be handled by the ASM jsr inliner");
+      }
+      default:
+        throw new Unreachable("Unexpected opcode in jump instruction: " + jump);
+    }
+  }
+
+  private int[] getVarTargets(VarInsnNode insn) {
+    if (insn.getOpcode() == Opcodes.RET) {
+      throw new Unreachable("RET should be handled by the ASM jsr inliner");
+    }
+    return NO_TARGETS;
+  }
+
+  // Type conversion helpers.
+
+  private static MoveType moveType(Type type) {
+    switch (type.getSort()) {
+      case Type.ARRAY:
+      case Type.OBJECT:
+        return MoveType.OBJECT;
+      case Type.BOOLEAN:
+      case Type.BYTE:
+      case Type.SHORT:
+      case Type.CHAR:
+      case Type.INT:
+      case Type.FLOAT:
+        return MoveType.SINGLE;
+      case Type.LONG:
+      case Type.DOUBLE:
+        return MoveType.WIDE;
+      case Type.VOID:
+        // Illegal. Throws in fallthrough.
+      default:
+        throw new Unreachable("Invalid type in moveType: " + type);
+    }
+  }
+
+  private static ConstType constType(Type type) {
+    switch (type.getSort()) {
+      case Type.ARRAY:
+      case Type.OBJECT:
+        return ConstType.OBJECT;
+      case Type.BOOLEAN:
+      case Type.BYTE:
+      case Type.SHORT:
+      case Type.CHAR:
+      case Type.INT:
+        return ConstType.INT;
+      case Type.FLOAT:
+        return ConstType.FLOAT;
+      case Type.LONG:
+        return ConstType.LONG;
+      case Type.DOUBLE:
+        return ConstType.DOUBLE;
+      case Type.VOID:
+        // Illegal. Throws in fallthrough.
+      default:
+        throw new Unreachable("Invalid type in constType: " + type);
+    }
+  }
+
+  private static MemberType memberType(Type type) {
+    switch (type.getSort()) {
+      case Type.ARRAY:
+      case Type.OBJECT:
+        return MemberType.OBJECT;
+      case Type.BOOLEAN:
+        return MemberType.BOOLEAN;
+      case Type.BYTE:
+        return MemberType.BYTE;
+      case Type.SHORT:
+        return MemberType.SHORT;
+      case Type.CHAR:
+        return MemberType.CHAR;
+      case Type.INT:
+      case Type.FLOAT:
+        return MemberType.SINGLE;
+      case Type.LONG:
+      case Type.DOUBLE:
+        return MemberType.WIDE;
+      case Type.VOID:
+        // Illegal. Throws in fallthrough.
+      default:
+        throw new Unreachable("Invalid type in memberType: " + type);
+    }
+  }
+
+  private static MemberType memberType(String fieldDesc) {
+    return memberType(Type.getType(fieldDesc));
+  }
+
+  private static NumericType numericType(Type type) {
+    switch (type.getSort()) {
+      case Type.BYTE:
+        return NumericType.BYTE;
+      case Type.CHAR:
+        return NumericType.CHAR;
+      case Type.SHORT:
+        return NumericType.SHORT;
+      case Type.INT:
+        return NumericType.INT;
+      case Type.LONG:
+        return NumericType.LONG;
+      case Type.FLOAT:
+        return NumericType.FLOAT;
+      case Type.DOUBLE:
+        return NumericType.DOUBLE;
+      default:
+        throw new Unreachable("Invalid type in numericType: " + type);
+    }
+  }
+
+  private Invoke.Type invokeType(MethodInsnNode method) {
+    switch (method.getOpcode()) {
+      case Opcodes.INVOKEVIRTUAL:
+        if (isCallToPolymorphicSignatureMethod(method)) {
+          return Invoke.Type.POLYMORPHIC;
+        }
+        return Invoke.Type.VIRTUAL;
+      case Opcodes.INVOKESTATIC:
+        return Invoke.Type.STATIC;
+      case Opcodes.INVOKEINTERFACE:
+        return Invoke.Type.INTERFACE;
+      case Opcodes.INVOKESPECIAL: {
+        DexType owner = application.getTypeFromName(method.owner);
+        if (owner == clazz || method.name.equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+          return Invoke.Type.DIRECT;
+        } else {
+          return Invoke.Type.SUPER;
+        }
+      }
+      default:
+        throw new Unreachable("Unexpected MethodInsnNode opcode: " + method.getOpcode());
+    }
+  }
+
+  static Type getArrayElementType(Type array) {
+    if (array == JarState.NULL_TYPE) {
+      return null;
+    }
+    String desc = array.getDescriptor();
+    assert desc.charAt(0) == '[';
+    return Type.getType(desc.substring(1));
+  }
+
+  private static Type makeArrayType(Type elementType) {
+    return Type.getObjectType("[" + elementType.getDescriptor());
+  }
+
+  private static String arrayTypeDesc(int arrayTypeCode) {
+    switch (arrayTypeCode) {
+      case Opcodes.T_BOOLEAN:
+        return "[Z";
+      case Opcodes.T_CHAR:
+        return "[C";
+      case Opcodes.T_FLOAT:
+        return "[F";
+      case Opcodes.T_DOUBLE:
+        return "[D";
+      case Opcodes.T_BYTE:
+        return "[B";
+      case Opcodes.T_SHORT:
+        return "[S";
+      case Opcodes.T_INT:
+        return "[I";
+      case Opcodes.T_LONG:
+        return "[J";
+      default:
+        throw new Unreachable("Unexpected array-type code " + arrayTypeCode);
+    }
+  }
+
+  private static Type getArrayElementTypeForOpcode(int opcode) {
+    switch (opcode) {
+      case Opcodes.IALOAD:
+      case Opcodes.IASTORE:
+        return Type.INT_TYPE;
+      case Opcodes.FALOAD:
+      case Opcodes.FASTORE:
+        return Type.FLOAT_TYPE;
+      case Opcodes.LALOAD:
+      case Opcodes.LASTORE:
+        return Type.LONG_TYPE;
+      case Opcodes.DALOAD:
+      case Opcodes.DASTORE:
+        return Type.DOUBLE_TYPE;
+      case Opcodes.AALOAD:
+      case Opcodes.AASTORE:
+        return JarState.NULL_TYPE; // We might not know the type.
+      case Opcodes.BALOAD:
+      case Opcodes.BASTORE:
+        return Type.BYTE_TYPE; // We don't distinguish byte and boolean.
+      case Opcodes.CALOAD:
+      case Opcodes.CASTORE:
+        return Type.CHAR_TYPE;
+      case Opcodes.SALOAD:
+      case Opcodes.SASTORE:
+        return Type.SHORT_TYPE;
+      default:
+        throw new Unreachable("Unexpected array opcode " + opcode);
+    }
+  }
+
+  private static boolean isCompatibleArrayElementType(int opcode, Type type) {
+    switch (opcode) {
+      case Opcodes.IALOAD:
+      case Opcodes.IASTORE:
+        return Slot.isCompatible(type, Type.INT_TYPE);
+      case Opcodes.FALOAD:
+      case Opcodes.FASTORE:
+        return Slot.isCompatible(type, Type.FLOAT_TYPE);
+      case Opcodes.LALOAD:
+      case Opcodes.LASTORE:
+        return Slot.isCompatible(type, Type.LONG_TYPE);
+      case Opcodes.DALOAD:
+      case Opcodes.DASTORE:
+        return Slot.isCompatible(type, Type.DOUBLE_TYPE);
+      case Opcodes.AALOAD:
+      case Opcodes.AASTORE:
+        return Slot.isCompatible(type, JarState.REFERENCE_TYPE);
+      case Opcodes.BALOAD:
+      case Opcodes.BASTORE:
+        return Slot.isCompatible(type, Type.BYTE_TYPE)
+            || Slot.isCompatible(type, Type.BOOLEAN_TYPE);
+      case Opcodes.CALOAD:
+      case Opcodes.CASTORE:
+        return Slot.isCompatible(type, Type.CHAR_TYPE);
+      case Opcodes.SALOAD:
+      case Opcodes.SASTORE:
+        return Slot.isCompatible(type, Type.SHORT_TYPE);
+      default:
+        throw new Unreachable("Unexpected array opcode " + opcode);
+    }
+  }
+
+  private static If.Type ifType(int opcode) {
+    switch (opcode) {
+      case Opcodes.IFEQ:
+      case Opcodes.IF_ICMPEQ:
+      case Opcodes.IF_ACMPEQ:
+        return If.Type.EQ;
+      case Opcodes.IFNE:
+      case Opcodes.IF_ICMPNE:
+      case Opcodes.IF_ACMPNE:
+        return If.Type.NE;
+      case Opcodes.IFLT:
+      case Opcodes.IF_ICMPLT:
+        return If.Type.LT;
+      case Opcodes.IFGE:
+      case Opcodes.IF_ICMPGE:
+        return If.Type.GE;
+      case Opcodes.IFGT:
+      case Opcodes.IF_ICMPGT:
+        return If.Type.GT;
+      case Opcodes.IFLE:
+      case Opcodes.IF_ICMPLE:
+        return If.Type.LE;
+      default:
+        throw new Unreachable("Unexpected If instruction opcode: " + opcode);
+    }
+  }
+
+  private static Type opType(int opcode) {
+    switch (opcode) {
+      case Opcodes.IADD:
+      case Opcodes.ISUB:
+      case Opcodes.IMUL:
+      case Opcodes.IDIV:
+      case Opcodes.IREM:
+      case Opcodes.INEG:
+      case Opcodes.ISHL:
+      case Opcodes.ISHR:
+      case Opcodes.IUSHR:
+        return Type.INT_TYPE;
+      case Opcodes.LADD:
+      case Opcodes.LSUB:
+      case Opcodes.LMUL:
+      case Opcodes.LDIV:
+      case Opcodes.LREM:
+      case Opcodes.LNEG:
+      case Opcodes.LSHL:
+      case Opcodes.LSHR:
+      case Opcodes.LUSHR:
+        return Type.LONG_TYPE;
+      case Opcodes.FADD:
+      case Opcodes.FSUB:
+      case Opcodes.FMUL:
+      case Opcodes.FDIV:
+      case Opcodes.FREM:
+      case Opcodes.FNEG:
+        return Type.FLOAT_TYPE;
+      case Opcodes.DADD:
+      case Opcodes.DSUB:
+      case Opcodes.DMUL:
+      case Opcodes.DDIV:
+      case Opcodes.DREM:
+      case Opcodes.DNEG:
+        return Type.DOUBLE_TYPE;
+      default:
+        throw new Unreachable("Unexpected opcode " + opcode);
+    }
+  }
+
+  // IR instruction building procedures.
+
+  private void build(AbstractInsnNode insn, IRBuilder builder) {
+    switch (insn.getType()) {
+      case AbstractInsnNode.INSN:
+        build((InsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.INT_INSN:
+        build((IntInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.VAR_INSN:
+        build((VarInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.TYPE_INSN:
+        build((TypeInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.FIELD_INSN:
+        build((FieldInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.METHOD_INSN:
+        build((MethodInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.INVOKE_DYNAMIC_INSN:
+        build((InvokeDynamicInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.JUMP_INSN:
+        build((JumpInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.LABEL:
+        build((LabelNode) insn, builder);
+        break;
+      case AbstractInsnNode.LDC_INSN:
+        build((LdcInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.IINC_INSN:
+        build((IincInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.TABLESWITCH_INSN:
+        build((TableSwitchInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.LOOKUPSWITCH_INSN:
+        build((LookupSwitchInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.MULTIANEWARRAY_INSN:
+        build((MultiANewArrayInsnNode) insn, builder);
+        break;
+      case AbstractInsnNode.LINE:
+        build((LineNumberNode) insn, builder);
+        break;
+      default:
+        throw new Unreachable("Unexpected instruction " + insn);
+    }
+  }
+
+  private void processLocalVariableEnd(AbstractInsnNode insn, IRBuilder builder) {
+    assert !isControlFlowInstruction(insn);
+    if (!(insn.getNext() instanceof LabelNode)) {
+      return;
+    }
+    // If the label is the end of any local-variable scopes end the locals.
+    LabelNode label = (LabelNode) insn.getNext();
+    List<Local> locals = state.getLocalsToClose(label);
+    for (Local local : locals) {
+      builder.addDebugLocalEnd(local.slot.register, local.info);
+    }
+    state.closeLocals(locals);
+  }
+
+  private void processLocalVariablesAtControlEdge(AbstractInsnNode insn, IRBuilder builder) {
+    assert isControlFlowInstruction(insn) && !isReturn(insn);
+    if (!(insn.getNext() instanceof LabelNode)) {
+      return;
+    }
+    // If the label is the end of any local-variable scopes read the locals to ensure liveness.
+    LabelNode label = (LabelNode) insn.getNext();
+    for (Local local : state.getLocalsToClose(label)) {
+      builder.addDebugLocalRead(local.slot.register, local.info);
+    }
+  }
+
+  private void processLocalVariablesAtExit(AbstractInsnNode insn, IRBuilder builder) {
+    assert isReturn(insn) || isThrow(insn);
+    // Read all locals live at exit to ensure liveness.
+    for (Local local : state.getLocals()) {
+      if (local.info != null) {
+        builder.addDebugLocalRead(local.slot.register, local.info);
+      }
+    }
+  }
+
+  private void build(InsnNode insn, IRBuilder builder) {
+    int opcode = insn.getOpcode();
+    switch (opcode) {
+      case Opcodes.NOP:
+        // Intentionally left empty.
+        break;
+      case Opcodes.ACONST_NULL:
+        builder.addNullConst(state.push(JarState.NULL_TYPE), 0);
+        break;
+      case Opcodes.ICONST_M1:
+      case Opcodes.ICONST_0:
+      case Opcodes.ICONST_1:
+      case Opcodes.ICONST_2:
+      case Opcodes.ICONST_3:
+      case Opcodes.ICONST_4:
+      case Opcodes.ICONST_5:
+        builder.addIntConst(state.push(Type.INT_TYPE), opcode - Opcodes.ICONST_0);
+        break;
+      case Opcodes.LCONST_0:
+      case Opcodes.LCONST_1:
+        builder.addLongConst(state.push(Type.LONG_TYPE), opcode - Opcodes.LCONST_0);
+        break;
+      case Opcodes.FCONST_0:
+      case Opcodes.FCONST_1:
+      case Opcodes.FCONST_2:
+        builder.addFloatConst(state.push(Type.FLOAT_TYPE),
+            Float.floatToRawIntBits(opcode - Opcodes.FCONST_0));
+        break;
+      case Opcodes.DCONST_0:
+      case Opcodes.DCONST_1:
+        builder.addDoubleConst(state.push(Type.DOUBLE_TYPE),
+            Double.doubleToRawLongBits(opcode - Opcodes.DCONST_0));
+        break;
+      case Opcodes.IALOAD:
+      case Opcodes.LALOAD:
+      case Opcodes.FALOAD:
+      case Opcodes.DALOAD:
+      case Opcodes.AALOAD:
+      case Opcodes.BALOAD:
+      case Opcodes.CALOAD:
+      case Opcodes.SALOAD: {
+        Slot index = state.pop(Type.INT_TYPE);
+        Slot array = state.pop(JarState.ARRAY_TYPE);
+        Type elementType = getArrayElementType(array.type);
+        if (elementType == null) {
+          elementType = getArrayElementTypeForOpcode(opcode);
+        }
+        int dest = state.push(elementType);
+        assert isCompatibleArrayElementType(opcode, elementType);
+        builder.addArrayGet(memberType(elementType), dest, array.register, index.register);
+        break;
+      }
+      case Opcodes.IASTORE:
+      case Opcodes.LASTORE:
+      case Opcodes.FASTORE:
+      case Opcodes.DASTORE:
+      case Opcodes.AASTORE:
+      case Opcodes.BASTORE:
+      case Opcodes.CASTORE:
+      case Opcodes.SASTORE: {
+        Slot value = state.pop();
+        Slot index = state.pop(Type.INT_TYPE);
+        Slot array = state.pop(JarState.ARRAY_TYPE);
+        Type elementType = getArrayElementType(array.type);
+        if (elementType == null) {
+          elementType = getArrayElementTypeForOpcode(opcode);
+        }
+        assert isCompatibleArrayElementType(opcode, elementType);
+        assert isCompatibleArrayElementType(opcode, value.type);
+        builder.addArrayPut(
+            memberType(elementType), value.register, array.register, index.register);
+        break;
+      }
+      case Opcodes.POP: {
+        Slot value = state.pop();
+        assert value.isCategory1();
+        break;
+      }
+      case Opcodes.POP2: {
+        Slot value = state.pop();
+        if (value.isCategory1()) {
+          Slot value2 = state.pop();
+          assert value2.isCategory1();
+        }
+        break;
+      }
+      case Opcodes.DUP: {
+        Slot value = state.peek();
+        assert value.isCategory1();
+        int copy = state.push(value.type);
+        builder.addMove(moveType(value.type), copy, value.register);
+        break;
+      }
+      case Opcodes.DUP_X1: {
+        // Stack transformation: ..., v2, v1 -> ..., v1, v2, v1
+        Slot value1 = state.pop();
+        Slot value2 = state.pop();
+        assert value1.isCategory1() && value2.isCategory1();
+        int stack2 = state.push(value1.type);
+        int stack1 = state.push(value2.type);
+        int stack0 = state.push(value1.type);
+        assert value2.register == stack2;
+        assert value1.register == stack1;
+        // stack0 is new top-of-stack.
+        builder.addMove(moveType(value1.type), stack0, stack1);
+        builder.addMove(moveType(value2.type), stack1, stack2);
+        builder.addMove(moveType(value1.type), stack2, stack0);
+        break;
+      }
+      case Opcodes.DUP_X2: {
+        Slot value1 = state.pop();
+        Slot value2 = state.pop();
+        assert value1.isCategory1();
+        if (value2.isCategory1()) {
+          Slot value3 = state.pop();
+          assert value3.isCategory1();
+          // Stack transformation: ..., v3, v2, v1 -> ..., v1, v3, v2, v1
+          dupOneBelowTwo(value3, value2, value1, builder);
+        } else {
+          // Stack transformation: ..., w2, v1 -> ..., v1, w2, v1
+          dupOneBelowOne(value2, value1, builder);
+        }
+        break;
+      }
+      case Opcodes.DUP2: {
+        Slot value1 = state.pop();
+        if (value1.isCategory1()) {
+          Slot value2 = state.pop();
+          // Stack transformation: ..., v2, v1 -> ..., v2, v1, v2, v1
+          assert value2.isCategory1();
+          state.push(value2.type);
+          state.push(value1.type);
+          int copy2 = state.push(value2.type);
+          int copy1 = state.push(value1.type);
+          builder.addMove(moveType(value1.type), copy1, value1.register);
+          builder.addMove(moveType(value2.type), copy2, value2.register);
+        } else {
+          // Stack transformation: ..., w1 -> ..., w1, w1
+          state.push(value1.type);
+          int copy1 = state.push(value1.type);
+          builder.addMove(moveType(value1.type), copy1, value1.register);
+        }
+        break;
+      }
+      case Opcodes.DUP2_X1: {
+        Slot value1 = state.pop();
+        Slot value2 = state.pop();
+        assert value2.isCategory1();
+        if (value1.isCategory1()) {
+          // Stack transformation: ..., v3, v2, v1 -> v2, v1, v3, v2, v1
+          Slot value3 = state.pop();
+          assert value3.isCategory1();
+          dupTwoBelowOne(value3, value2, value1, builder);
+        } else {
+          // Stack transformation: ..., v2, w1 -> ..., w1, v2, w1
+          dupOneBelowOne(value2, value1, builder);
+        }
+        break;
+      }
+      case Opcodes.DUP2_X2: {
+        Slot value1 = state.pop();
+        Slot value2 = state.pop();
+        if (!value1.isCategory1() && !value2.isCategory1()) {
+          // State transformation: ..., w2, w1 -> w1, w2, w1
+          dupOneBelowOne(value2, value1, builder);
+        } else {
+          Slot value3 = state.pop();
+          if (!value1.isCategory1()) {
+            assert value2.isCategory1();
+            assert value3.isCategory1();
+            // State transformation: ..., v3, v2, w1 -> w1, v3, v2, w1
+            dupOneBelowTwo(value3, value2, value1, builder);
+          } else if (!value3.isCategory1()) {
+            assert value1.isCategory1();
+            assert value2.isCategory1();
+            // State transformation: ..., w3, v2, v1 -> v2, v1, w3, v2, v1
+            dupTwoBelowOne(value3, value2, value1, builder);
+          } else {
+            Slot value4 = state.pop();
+            assert value1.isCategory1();
+            assert value2.isCategory1();
+            assert value3.isCategory1();
+            assert value4.isCategory1();
+            // State transformation: ..., v4, v3, v2, v1 -> v2, v1, v4, v3, v2, v1
+            dupTwoBelowTwo(value4, value3, value2, value1, builder);
+          }
+        }
+        break;
+      }
+      case Opcodes.SWAP: {
+        Slot value1 = state.pop();
+        Slot value2 = state.pop();
+        assert value1.isCategory1() && value2.isCategory1();
+        state.push(value1.type);
+        state.push(value2.type);
+        int tmp = state.push(value1.type);
+        builder.addMove(moveType(value1.type), tmp, value1.register);
+        builder.addMove(moveType(value2.type), value1.register, value2.register);
+        builder.addMove(moveType(value1.type), value2.register, tmp);
+        state.pop(); // Remove temp.
+        break;
+      }
+      case Opcodes.IADD:
+      case Opcodes.LADD:
+      case Opcodes.FADD:
+      case Opcodes.DADD:
+      case Opcodes.ISUB:
+      case Opcodes.LSUB:
+      case Opcodes.FSUB:
+      case Opcodes.DSUB:
+      case Opcodes.IMUL:
+      case Opcodes.LMUL:
+      case Opcodes.FMUL:
+      case Opcodes.DMUL:
+      case Opcodes.IDIV:
+      case Opcodes.LDIV:
+      case Opcodes.FDIV:
+      case Opcodes.DDIV:
+      case Opcodes.IREM:
+      case Opcodes.LREM:
+      case Opcodes.FREM:
+      case Opcodes.DREM: {
+        Type type = opType(opcode);
+        NumericType numericType = numericType(type);
+        int right = state.pop(type).register;
+        int left = state.pop(type).register;
+        int dest = state.push(type);
+        if (opcode <= Opcodes.DADD) {
+          builder.addAdd(numericType, dest, left, right);
+        } else if (opcode <= Opcodes.DSUB) {
+          builder.addSub(numericType, dest, left, right);
+        } else if (opcode <= Opcodes.DMUL) {
+          builder.addMul(numericType, dest, left, right);
+        } else if (opcode <= Opcodes.DDIV) {
+          builder.addDiv(numericType, dest, left, right);
+        } else {
+          assert Opcodes.IREM <= opcode && opcode <= Opcodes.DREM;
+          builder.addRem(numericType, dest, left, right);
+        }
+        break;
+      }
+      case Opcodes.INEG:
+      case Opcodes.LNEG:
+      case Opcodes.FNEG:
+      case Opcodes.DNEG: {
+        Type type = opType(opcode);
+        NumericType numericType = numericType(type);
+        int value = state.pop(type).register;
+        int dest = state.push(type);
+        builder.addNeg(numericType, dest, value);
+        break;
+      }
+      case Opcodes.ISHL:
+      case Opcodes.LSHL:
+      case Opcodes.ISHR:
+      case Opcodes.LSHR:
+      case Opcodes.IUSHR:
+      case Opcodes.LUSHR: {
+        Type type = opType(opcode);
+        NumericType numericType = numericType(type);
+        int right = state.pop(Type.INT_TYPE).register;
+        int left = state.pop(type).register;
+        int dest = state.push(type);
+        if (opcode <= Opcodes.LSHL) {
+          builder.addShl(numericType, dest, left, right);
+        } else if (opcode <= Opcodes.LSHR) {
+          builder.addShr(numericType, dest, left, right);
+        } else {
+          assert opcode == Opcodes.IUSHR || opcode == Opcodes.LUSHR;
+          builder.addUshr(numericType, dest, left, right);
+        }
+        break;
+      }
+      case Opcodes.IAND:
+      case Opcodes.LAND: {
+        Type type = opcode == Opcodes.IAND ? Type.INT_TYPE : Type.LONG_TYPE;
+        int right = state.pop(type).register;
+        int left = state.pop(type).register;
+        int dest = state.push(type);
+        builder.addAnd(numericType(type), dest, left, right);
+        break;
+      }
+      case Opcodes.IOR:
+      case Opcodes.LOR: {
+        Type type = opcode == Opcodes.IOR ? Type.INT_TYPE : Type.LONG_TYPE;
+        int right = state.pop(type).register;
+        int left = state.pop(type).register;
+        int dest = state.push(type);
+        builder.addOr(numericType(type), dest, left, right);
+        break;
+      }
+      case Opcodes.IXOR:
+      case Opcodes.LXOR: {
+        Type type = opcode == Opcodes.IXOR ? Type.INT_TYPE : Type.LONG_TYPE;
+        int right = state.pop(type).register;
+        int left = state.pop(type).register;
+        int dest = state.push(type);
+        builder.addXor(numericType(type), dest, left, right);
+        break;
+      }
+      case Opcodes.I2L:
+        buildConversion(Type.INT_TYPE, Type.LONG_TYPE, builder);
+        break;
+      case Opcodes.I2F:
+        buildConversion(Type.INT_TYPE, Type.FLOAT_TYPE, builder);
+        break;
+      case Opcodes.I2D:
+        buildConversion(Type.INT_TYPE, Type.DOUBLE_TYPE, builder);
+        break;
+      case Opcodes.L2I:
+        buildConversion(Type.LONG_TYPE, Type.INT_TYPE, builder);
+        break;
+      case Opcodes.L2F:
+        buildConversion(Type.LONG_TYPE, Type.FLOAT_TYPE, builder);
+        break;
+      case Opcodes.L2D:
+        buildConversion(Type.LONG_TYPE, Type.DOUBLE_TYPE, builder);
+        break;
+      case Opcodes.F2I:
+        buildConversion(Type.FLOAT_TYPE, Type.INT_TYPE, builder);
+        break;
+      case Opcodes.F2L:
+        buildConversion(Type.FLOAT_TYPE, Type.LONG_TYPE, builder);
+        break;
+      case Opcodes.F2D:
+        buildConversion(Type.FLOAT_TYPE, Type.DOUBLE_TYPE, builder);
+        break;
+      case Opcodes.D2I:
+        buildConversion(Type.DOUBLE_TYPE, Type.INT_TYPE, builder);
+        break;
+      case Opcodes.D2L:
+        buildConversion(Type.DOUBLE_TYPE, Type.LONG_TYPE, builder);
+        break;
+      case Opcodes.D2F:
+        buildConversion(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, builder);
+        break;
+      case Opcodes.I2B:
+        buildConversion(Type.INT_TYPE, Type.BYTE_TYPE, builder);
+        break;
+      case Opcodes.I2C:
+        buildConversion(Type.INT_TYPE, Type.CHAR_TYPE, builder);
+        break;
+      case Opcodes.I2S:
+        buildConversion(Type.INT_TYPE, Type.SHORT_TYPE, builder);
+        break;
+      case Opcodes.LCMP: {
+        Slot right = state.pop(Type.LONG_TYPE);
+        Slot left = state.pop(Type.LONG_TYPE);
+        int dest = state.push(Type.INT_TYPE);
+        builder.addCmp(NumericType.LONG, Bias.NONE, dest, left.register, right.register);
+        break;
+      }
+      case Opcodes.FCMPL:
+      case Opcodes.FCMPG: {
+        Slot right = state.pop(Type.FLOAT_TYPE);
+        Slot left = state.pop(Type.FLOAT_TYPE);
+        int dest = state.push(Type.INT_TYPE);
+        Bias bias = opcode == Opcodes.FCMPL ? Bias.LT : Bias.GT;
+        builder.addCmp(NumericType.FLOAT, bias, dest, left.register, right.register);
+        break;
+      }
+      case Opcodes.DCMPL:
+      case Opcodes.DCMPG: {
+        Slot right = state.pop(Type.DOUBLE_TYPE);
+        Slot left = state.pop(Type.DOUBLE_TYPE);
+        int dest = state.push(Type.INT_TYPE);
+        Bias bias = opcode == Opcodes.DCMPL ? Bias.LT : Bias.GT;
+        builder.addCmp(NumericType.DOUBLE, bias, dest, left.register, right.register);
+        break;
+      }
+      case Opcodes.IRETURN: {
+        Slot value = state.pop(Type.INT_TYPE);
+        addReturn(insn, MoveType.SINGLE, value.register, builder);
+        break;
+      }
+      case Opcodes.LRETURN: {
+        Slot value = state.pop(Type.LONG_TYPE);
+        addReturn(insn, MoveType.WIDE, value.register, builder);
+        break;
+      }
+      case Opcodes.FRETURN: {
+        Slot value = state.pop(Type.FLOAT_TYPE);
+        addReturn(insn, MoveType.SINGLE, value.register, builder);
+        break;
+      }
+      case Opcodes.DRETURN: {
+        Slot value = state.pop(Type.DOUBLE_TYPE);
+        addReturn(insn, MoveType.WIDE, value.register, builder);
+        break;
+      }
+      case Opcodes.ARETURN: {
+        Slot obj = state.pop(JarState.REFERENCE_TYPE);
+        addReturn(insn, MoveType.OBJECT, obj.register, builder);
+        break;
+      }
+      case Opcodes.RETURN: {
+        addReturn(insn, null, -1, builder);
+        break;
+      }
+      case Opcodes.ARRAYLENGTH: {
+        Slot array = state.pop(JarState.ARRAY_TYPE);
+        int dest = state.push(Type.INT_TYPE);
+        builder.addArrayLength(dest, array.register);
+        break;
+      }
+      case Opcodes.ATHROW: {
+        Slot object = state.pop(JarState.OBJECT_TYPE);
+        addThrow(insn, object.register, builder);
+        break;
+      }
+      case Opcodes.MONITORENTER: {
+        Slot object = state.pop(JarState.REFERENCE_TYPE);
+        builder.addMonitor(Monitor.Type.ENTER, object.register);
+        break;
+      }
+      case Opcodes.MONITOREXIT: {
+        Slot object = state.pop(JarState.REFERENCE_TYPE);
+        builder.addMonitor(Monitor.Type.EXIT, object.register);
+        break;
+      }
+      default:
+        throw new Unreachable("Unexpected Insn opcode: " + insn.getOpcode());
+    }
+  }
+
+  private void addThrow(InsnNode insn, int register, IRBuilder builder) {
+    if (getTryHandlers(insn).isEmpty()) {
+      processLocalVariablesAtExit(insn, builder);
+    } else {
+      processLocalVariablesAtControlEdge(insn, builder);
+    }
+    builder.addThrow(register);
+    state.invalidateState();
+  }
+
+  private void addReturn(InsnNode insn, MoveType type, int register, IRBuilder builder) {
+    processLocalVariablesAtExit(insn, builder);
+    if (type == null) {
+      assert register == -1;
+      builder.addReturn();
+    } else {
+      builder.addReturn(type, register);
+    }
+    state.invalidateState();
+  }
+
+  private void dupOneBelowTwo(Slot value3, Slot value2, Slot value1, IRBuilder builder) {
+    int stack3 = state.push(value1.type);
+    int stack2 = state.push(value3.type);
+    int stack1 = state.push(value2.type);
+    int stack0 = state.push(value1.type);
+    assert value3.register == stack3;
+    assert value2.register == stack2;
+    assert value1.register == stack1;
+    builder.addMove(moveType(value1.type), stack0, stack1);
+    builder.addMove(moveType(value2.type), stack1, stack2);
+    builder.addMove(moveType(value3.type), stack2, stack3);
+    builder.addMove(moveType(value1.type), stack3, stack0);
+  }
+
+  private void dupOneBelowOne(Slot value2, Slot value1, IRBuilder builder) {
+    int stack2 = state.push(value1.type);
+    int stack1 = state.push(value2.type);
+    int stack0 = state.push(value1.type);
+    assert value2.register == stack2;
+    assert value1.register == stack1;
+    builder.addMove(moveType(value1.type), stack0, stack1);
+    builder.addMove(moveType(value2.type), stack1, stack2);
+    builder.addMove(moveType(value1.type), stack2, stack0);
+  }
+
+  private void dupTwoBelowOne(Slot value3, Slot value2, Slot value1, IRBuilder builder) {
+    int stack4 = state.push(value2.type);
+    int stack3 = state.push(value1.type);
+    int stack2 = state.push(value3.type);
+    int stack1 = state.push(value2.type);
+    int stack0 = state.push(value1.type);
+    assert value3.register == stack4;
+    assert value2.register == stack3;
+    assert value1.register == stack2;
+    builder.addMove(moveType(value1.type), stack0, stack2);
+    builder.addMove(moveType(value2.type), stack1, stack3);
+    builder.addMove(moveType(value3.type), stack2, stack4);
+    builder.addMove(moveType(value1.type), stack3, stack0);
+    builder.addMove(moveType(value2.type), stack4, stack1);
+  }
+
+  private void dupTwoBelowTwo(Slot value4, Slot value3, Slot value2, Slot value1,
+      IRBuilder builder) {
+    int stack5 = state.push(value2.type);
+    int stack4 = state.push(value1.type);
+    int stack3 = state.push(value4.type);
+    int stack2 = state.push(value3.type);
+    int stack1 = state.push(value2.type);
+    int stack0 = state.push(value1.type);
+    assert value4.register == stack5;
+    assert value3.register == stack4;
+    assert value2.register == stack3;
+    assert value1.register == stack2;
+    builder.addMove(moveType(value1.type), stack0, stack2);
+    builder.addMove(moveType(value2.type), stack1, stack3);
+    builder.addMove(moveType(value3.type), stack2, stack4);
+    builder.addMove(moveType(value3.type), stack3, stack5);
+    builder.addMove(moveType(value1.type), stack4, stack0);
+    builder.addMove(moveType(value2.type), stack5, stack1);
+  }
+
+  private void buildConversion(Type from, Type to, IRBuilder builder) {
+    int source = state.pop(from).register;
+    int dest = state.push(to);
+    builder.addConversion(numericType(to), numericType(from), dest, source);
+  }
+
+  private void build(IntInsnNode insn, IRBuilder builder) {
+    switch (insn.getOpcode()) {
+      case Opcodes.BIPUSH:
+      case Opcodes.SIPUSH: {
+        int dest = state.push(Type.INT_TYPE);
+        builder.addIntConst(dest, insn.operand);
+        break;
+      }
+      case Opcodes.NEWARRAY: {
+        String desc = arrayTypeDesc(insn.operand);
+        Type type = Type.getType(desc);
+        DexType dexType = application.getTypeFromDescriptor(desc);
+        int count = state.pop(Type.INT_TYPE).register;
+        int array = state.push(type);
+        builder.addNewArrayEmpty(array, count, dexType);
+        break;
+      }
+      default:
+        throw new Unreachable("Unexpected IntInsn opcode: " + insn.getOpcode());
+    }
+  }
+
+  private void build(VarInsnNode insn, IRBuilder builder) {
+    int opcode = insn.getOpcode();
+    Type expectedType;
+    switch (opcode) {
+      case Opcodes.ILOAD:
+      case Opcodes.ISTORE:
+        expectedType = Type.INT_TYPE;
+        break;
+      case Opcodes.FLOAD:
+      case Opcodes.FSTORE:
+        expectedType = Type.FLOAT_TYPE;
+        break;
+      case Opcodes.LLOAD:
+      case Opcodes.LSTORE:
+        expectedType = Type.LONG_TYPE;
+        break;
+      case Opcodes.DLOAD:
+      case Opcodes.DSTORE:
+        expectedType = Type.DOUBLE_TYPE;
+        break;
+      case Opcodes.ALOAD:
+      case Opcodes.ASTORE:
+        expectedType = JarState.REFERENCE_TYPE;
+        break;
+      case Opcodes.RET: {
+        throw new Unreachable("RET should be handled by the ASM jsr inliner");
+      }
+      default:
+        throw new Unreachable("Unexpected VarInsn opcode: " + insn.getOpcode());
+    }
+    if (Opcodes.ILOAD <= opcode && opcode <= Opcodes.ALOAD) {
+      Slot src = state.readLocal(insn.var, expectedType);
+      int dest = state.push(src.type);
+      builder.addMove(moveType(src.type), dest, src.register);
+    } else {
+      assert Opcodes.ISTORE <= opcode && opcode <= Opcodes.ASTORE;
+      Slot src = state.pop(expectedType);
+      int dest = state.writeLocal(insn.var, src.type);
+      builder.addMove(moveType(src.type), dest, src.register);
+    }
+  }
+
+  private void build(TypeInsnNode insn, IRBuilder builder) {
+    Type type = Type.getObjectType(insn.desc);
+    switch (insn.getOpcode()) {
+      case Opcodes.NEW: {
+        DexType dexType = application.getTypeFromName(insn.desc);
+        int dest = state.push(type);
+        builder.addNewInstance(dest, dexType);
+        break;
+      }
+      case Opcodes.ANEWARRAY: {
+        Type arrayType = makeArrayType(type);
+        DexType dexArrayType = application.getTypeFromDescriptor(arrayType.getDescriptor());
+        int count = state.pop(Type.INT_TYPE).register;
+        int dest = state.push(arrayType);
+        builder.addNewArrayEmpty(dest, count, dexArrayType);
+        break;
+      }
+      case Opcodes.CHECKCAST: {
+        DexType dexType = application.getTypeFromDescriptor(type.getDescriptor());
+        // Pop the top value and push it back on with the checked type.
+        state.pop(type);
+        int object = state.push(type);
+        builder.addCheckCast(object, dexType);
+        break;
+      }
+      case Opcodes.INSTANCEOF: {
+        int obj = state.pop(JarState.REFERENCE_TYPE).register;
+        int dest = state.push(Type.INT_TYPE);
+        DexType dexType = application.getTypeFromDescriptor(type.getDescriptor());
+        builder.addInstanceOf(dest, obj, dexType);
+        break;
+      }
+      default:
+        throw new Unreachable("Unexpected TypeInsn opcode: " + insn.getOpcode());
+    }
+  }
+
+  private void build(FieldInsnNode insn, IRBuilder builder) {
+    DexField field = application.getField(insn.owner, insn.name, insn.desc);
+    Type type = Type.getType(insn.desc);
+    MemberType fieldType = memberType(insn.desc);
+    switch (insn.getOpcode()) {
+      case Opcodes.GETSTATIC:
+        builder.addStaticGet(fieldType, state.push(type), field);
+        break;
+      case Opcodes.PUTSTATIC:
+        builder.addStaticPut(fieldType, state.pop(type).register, field);
+        break;
+      case Opcodes.GETFIELD: {
+        Slot object = state.pop(JarState.OBJECT_TYPE);
+        int dest = state.push(type);
+        builder.addInstanceGet(fieldType, dest, object.register, field);
+        break;
+      }
+      case Opcodes.PUTFIELD: {
+        Slot value = state.pop(type);
+        Slot object = state.pop(JarState.OBJECT_TYPE);
+        builder.addInstancePut(fieldType, value.register, object.register, field);
+        break;
+      }
+      default:
+        throw new Unreachable("Unexpected FieldInsn opcode: " + insn.getOpcode());
+    }
+  }
+
+  private void build(MethodInsnNode insn, IRBuilder builder) {
+    // Resolve the target method of the invoke.
+    DexMethod method = application.getMethod(insn.owner, insn.name, insn.desc);
+
+    buildInvoke(
+        insn.desc,
+        Type.getObjectType(insn.owner),
+        insn.getOpcode() != Opcodes.INVOKESTATIC,
+        builder,
+        (types, registers) -> {
+          Invoke.Type invokeType = invokeType(insn);
+          DexProto callSiteProto = null;
+          DexMethod targetMethod = method;
+          if (invokeType == Invoke.Type.POLYMORPHIC) {
+            targetMethod = application.getMethod(
+                insn.owner, insn.name, METHODHANDLE_INVOKE_OR_INVOKEEXACT_DESC);
+            callSiteProto = application.getProto(insn.desc);
+          }
+          builder.addInvoke(invokeType, targetMethod, callSiteProto, types, registers);
+        });
+  }
+
+  private void buildInvoke(
+      String methodDesc, Type methodOwner, boolean addImplicitReceiver,
+      IRBuilder builder, BiConsumer<List<MoveType>, List<Integer>> creator) {
+
+    // Build the argument list of the form [owner, param1, ..., paramN].
+    // The arguments are in reverse order on the stack, so we pop off the parameters here.
+    Type[] parameterTypes = Type.getArgumentTypes(methodDesc);
+    Slot[] parameterRegisters = state.popReverse(parameterTypes.length);
+
+    List<MoveType> types = new ArrayList<>(parameterTypes.length + 1);
+    List<Integer> registers = new ArrayList<>(parameterTypes.length + 1);
+
+    // Add receiver argument for non-static calls.
+    if (addImplicitReceiver) {
+      addArgument(types, registers, methodOwner, state.pop());
+    }
+
+    // The remaining arguments are the parameters of the method.
+    for (int i = 0; i < parameterTypes.length; i++) {
+      addArgument(types, registers, parameterTypes[i], parameterRegisters[i]);
+    }
+
+    // Create the invoke.
+    creator.accept(types, registers);
+
+    // Move the result to the "top of stack".
+    Type returnType = Type.getReturnType(methodDesc);
+    if (returnType != Type.VOID_TYPE) {
+      builder.addMoveResult(moveType(returnType), state.push(returnType));
+    }
+  }
+
+  private static void addArgument(List<MoveType> types, List<Integer> registers, Type type,
+      Slot slot) {
+    assert slot.isCompatibleWith(type);
+    types.add(moveType(type));
+    registers.add(slot.register);
+  }
+
+  private void build(InvokeDynamicInsnNode insn, IRBuilder builder) {
+    // Bootstrap method
+    Handle bsmHandle = insn.bsm;
+    if (bsmHandle.getTag() != Opcodes.H_INVOKESTATIC &&
+        bsmHandle.getTag() != Opcodes.H_NEWINVOKESPECIAL) {
+      throw new Unreachable(
+          "Bootstrap handle is not yet supported: tag == " + bsmHandle.getTag());
+    }
+    // Resolve the bootstrap method.
+    DexMethodHandle bootstrapMethod = getMethodHandle(application, bsmHandle);
+
+    // Decode static bootstrap arguments
+    List<DexValue> bootstrapArgs = new ArrayList<>();
+    for (Object arg : insn.bsmArgs) {
+      bootstrapArgs.add(decodeBootstrapArgument(arg));
+    }
+
+    // Construct call site
+    DexCallSite callSite = application
+        .getCallSite(insn.name, insn.desc, bootstrapMethod, bootstrapArgs);
+
+    buildInvoke(insn.desc, null /* Not needed */,
+        false /* Receiver is passed explicitly */, builder,
+        (types, registers) -> builder.addInvokeCustom(callSite, types, registers));
+  }
+
+  private DexValue decodeBootstrapArgument(Object value) {
+    if (value instanceof Integer) {
+      return DexValue.DexValueInt.create((Integer) value);
+    } else if (value instanceof Long) {
+      return DexValue.DexValueLong.create((Long) value);
+    } else if (value instanceof Float) {
+      return DexValue.DexValueFloat.create((Float) value);
+    } else if (value instanceof Double) {
+      return DexValue.DexValueDouble.create((Double) value);
+    } else if (value instanceof String) {
+      return new DexValue.DexValueString(application.getString((String) value));
+
+    } else if (value instanceof Type) {
+      Type type = (Type) value;
+      switch (type.getSort()) {
+        case Type.OBJECT:
+          return new DexValue.DexValueType(
+              application.getTypeFromDescriptor(((Type) value).getDescriptor()));
+        case Type.METHOD:
+          return new DexValue.DexValueMethodType(
+              application.getProto(((Type) value).getDescriptor()));
+      }
+      throw new Unreachable("Type sort is not supported: " + type.getSort());
+
+    } else if (value instanceof Handle) {
+      return new DexValue.DexValueMethodHandle(getMethodHandle(application, (Handle) value));
+    } else {
+      throw new Unreachable(
+          "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
+    }
+  }
+
+  private DexMethodHandle getMethodHandle(JarApplicationReader application, Handle handle) {
+    MethodHandleType methodHandleType = getMethodHandleType(handle);
+    Descriptor<? extends DexItem, ? extends Descriptor> descriptor =
+        methodHandleType.isFieldType()
+            ? application.getField(handle.getOwner(), handle.getName(), handle.getDesc())
+            : application.getMethod(handle.getOwner(), handle.getName(), handle.getDesc());
+    return application.getMethodHandle(methodHandleType, descriptor);
+  }
+
+  private MethodHandleType getMethodHandleType(Handle handle) {
+    switch (handle.getTag()) {
+      case Opcodes.H_GETFIELD:
+        return MethodHandleType.INSTANCE_GET;
+      case Opcodes.H_GETSTATIC:
+        return MethodHandleType.STATIC_GET;
+      case Opcodes.H_PUTFIELD:
+        return MethodHandleType.INSTANCE_PUT;
+      case Opcodes.H_PUTSTATIC:
+        return MethodHandleType.STATIC_PUT;
+      case Opcodes.H_INVOKESPECIAL:
+        DexType owner = application.getTypeFromName(handle.getOwner());
+        if (owner == clazz || handle.getName().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
+          return MethodHandleType.INVOKE_INSTANCE;
+        } else {
+          return MethodHandleType.INVOKE_SUPER;
+        }
+      case Opcodes.H_INVOKEVIRTUAL:
+        return MethodHandleType.INVOKE_INSTANCE;
+      case Opcodes.H_INVOKEINTERFACE:
+        return MethodHandleType.INVOKE_INTERFACE;
+      case Opcodes.H_INVOKESTATIC:
+        return MethodHandleType.INVOKE_STATIC;
+      case Opcodes.H_NEWINVOKESPECIAL:
+        return MethodHandleType.INVOKE_CONSTRUCTOR;
+      default:
+        throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
+    }
+  }
+
+  private void build(JumpInsnNode insn, IRBuilder builder) {
+    processLocalVariablesAtControlEdge(insn, builder);
+    int[] targets = getTargets(insn);
+    int opcode = insn.getOpcode();
+    if (Opcodes.IFEQ <= opcode && opcode <= Opcodes.IF_ACMPNE) {
+      assert targets.length == 2;
+      if (opcode <= Opcodes.IFLE) {
+        Slot value = state.pop(Type.INT_TYPE);
+        builder.addIfZero(ifType(opcode), value.register, targets[0], targets[1]);
+      } else {
+        Type expectedType = opcode < Opcodes.IF_ACMPEQ ? Type.INT_TYPE : JarState.REFERENCE_TYPE;
+        Slot value2 = state.pop(expectedType);
+        Slot value1 = state.pop(expectedType);
+        builder.addIf(ifType(opcode), value1.register, value2.register, targets[0], targets[1]);
+      }
+    } else {
+      switch (opcode) {
+        case Opcodes.GOTO: {
+          assert targets.length == 1;
+          builder.addGoto(targets[0]);
+          break;
+        }
+        case Opcodes.IFNULL:
+        case Opcodes.IFNONNULL: {
+          Slot value = state.pop(JarState.REFERENCE_TYPE);
+          If.Type type = opcode == Opcodes.IFNULL ? If.Type.EQ : If.Type.NE;
+          builder.addIfZero(type, value.register, targets[0], targets[1]);
+          break;
+        }
+        case Opcodes.JSR: {
+          throw new Unreachable("JSR should be handled by the ASM jsr inliner");
+        }
+        default:
+          throw new Unreachable("Unexpected JumpInsn opcode: " + insn.getOpcode());
+      }
+    }
+    for (int target : targets) {
+      state.recordStateForTarget(target, this);
+    }
+    state.invalidateState();
+  }
+
+  private void build(LabelNode insn, IRBuilder builder) {
+    // Open the scope of locals starting at this point.
+    if (insn != initialLabel) {
+      for (Local local : state.openLocals(insn)) {
+        builder.addDebugLocalStart(local.slot.register, local.info);
+      }
+    }
+    // Processing local-variable ends is done before closing potential control-flow edges.
+    // Record the state for all the try-catch handlers that are active at this label.
+    for (TryCatchBlock tryCatchBlock : getTryHandlers(insn)) {
+      state.recordStateForExceptionalTarget(tryCatchBlock.getHandler(), this);
+    }
+  }
+
+  private void build(LdcInsnNode insn, IRBuilder builder) {
+    if (insn.cst instanceof Type) {
+      Type type = (Type) insn.cst;
+      int dest = state.push(type);
+      builder.addConstClass(dest, application.getTypeFromDescriptor(type.getDescriptor()));
+    } else if (insn.cst instanceof String) {
+      int dest = state.push(STRING_TYPE);
+      builder.addConstString(dest, application.getString((String) insn.cst));
+    } else if (insn.cst instanceof Long) {
+      int dest = state.push(Type.LONG_TYPE);
+      builder.addLongConst(dest, (Long) insn.cst);
+    } else if (insn.cst instanceof Double) {
+      int dest = state.push(Type.DOUBLE_TYPE);
+      builder.addDoubleConst(dest, Double.doubleToRawLongBits((Double) insn.cst));
+    } else if (insn.cst instanceof Integer) {
+      int dest = state.push(Type.INT_TYPE);
+      builder.addIntConst(dest, (Integer) insn.cst);
+    } else {
+      assert insn.cst instanceof Float;
+      int dest = state.push(Type.FLOAT_TYPE);
+      builder.addFloatConst(dest, Float.floatToRawIntBits((Float) insn.cst));
+    }
+  }
+
+  private void build(IincInsnNode insn, IRBuilder builder) {
+    int local = state.readLocal(insn.var, Type.INT_TYPE).register;
+    builder.addAddLiteral(NumericType.INT, local, local, insn.incr);
+  }
+
+  private void build(TableSwitchInsnNode insn, IRBuilder builder) {
+    processLocalVariablesAtControlEdge(insn, builder);
+    buildSwitch(Switch.Type.PACKED, insn.dflt, insn.labels, new int[]{insn.min}, builder);
+  }
+
+  private void build(LookupSwitchInsnNode insn, IRBuilder builder) {
+    int[] keys = new int[insn.keys.size()];
+    for (int i = 0; i < insn.keys.size(); i++) {
+      keys[i] = (int) insn.keys.get(i);
+    }
+    buildSwitch(Switch.Type.SPARSE, insn.dflt, insn.labels, keys, builder);
+  }
+
+  private void buildSwitch(Switch.Type type, LabelNode dflt, List labels, int[] keys,
+      IRBuilder builder) {
+    int index = state.pop(Type.INT_TYPE).register;
+    int fallthroughOffset = getOffset(dflt);
+    state.recordStateForTarget(fallthroughOffset, this);
+    int[] labelOffsets = new int[labels.size()];
+    for (int i = 0; i < labels.size(); i++) {
+      int offset = getOffset((LabelNode) labels.get(i));
+      labelOffsets[i] = offset;
+      state.recordStateForTarget(offset, this);
+    }
+    builder.addSwitch(type, index, keys, fallthroughOffset, labelOffsets);
+    state.invalidateState();
+  }
+
+  private void build(MultiANewArrayInsnNode insn, IRBuilder builder) {
+    // Type of the full array.
+    Type arrayType = Type.getObjectType(insn.desc);
+    DexType dexArrayType = application.getType(arrayType);
+    // Type of the members. Can itself be of array type, eg, 'int[]' for 'new int[x][y][]'
+    DexType memberType = application.getTypeFromDescriptor(insn.desc.substring(insn.dims));
+    // Push an array containing the dimensions of the desired multi-dimensional array.
+    DexType dimArrayType = application.getTypeFromDescriptor(INT_ARRAY_DESC);
+    Slot[] slots = state.popReverse(insn.dims, Type.INT_TYPE);
+    int[] dimensions = new int[insn.dims];
+    for (int i = 0; i < insn.dims; i++) {
+      dimensions[i] = slots[i].register;
+    }
+    builder.addInvokeNewArray(dimArrayType, insn.dims, dimensions);
+    int dimensionsDestTemp = state.push(INT_ARRAY_TYPE);
+    builder.addMoveResult(MoveType.OBJECT, dimensionsDestTemp);
+    // Push the class object for the member type of the array.
+    int classDestTemp = state.push(CLASS_TYPE);
+    builder.ensureBlockForThrowingInstruction();
+    builder.addConstClass(classDestTemp, memberType);
+    // Create the actual multi-dimensional array using java.lang.reflect.Array::newInstance
+    DexType reflectArrayClass = application.getTypeFromDescriptor(REFLECT_ARRAY_DESC);
+    DexMethod newInstance = application.getMethod(reflectArrayClass,
+        REFLECT_ARRAY_NEW_INSTANCE_NAME, REFLECT_ARRAY_NEW_INSTANCE_DESC);
+    List<MoveType> argumentTypes = Arrays.asList(moveType(CLASS_TYPE), moveType(INT_ARRAY_TYPE));
+    List<Integer> argumentRegisters = Arrays.asList(classDestTemp, dimensionsDestTemp);
+    builder.ensureBlockForThrowingInstruction();
+    builder.addInvoke(Invoke.Type.STATIC, newInstance, null, argumentTypes, argumentRegisters);
+    // Pop the temporaries and push the final result.
+    state.pop(); // classDestTemp.
+    state.pop(); // dimensionsDestTemp.
+    int result = state.push(arrayType);
+    builder.addMoveResult(moveType(arrayType), result);
+    // Insert cast check to satisfy verification.
+    builder.ensureBlockForThrowingInstruction();
+    builder.addCheckCast(result, dexArrayType);
+  }
+
+  private void build(LineNumberNode insn, IRBuilder builder) {
+    builder.updateCurrentDebugPosition(insn.line, null);
+  }
+
+  // Printing helpers.
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("node.name = [").append(node.name).append("]");
+    builder.append("\n");
+    builder.append("node.desc = ").append(node.desc);
+    builder.append("\n");
+    builder.append("node.maxStack = ").append(node.maxStack);
+    builder.append("\n");
+    builder.append("node.maxLocals = ").append(node.maxLocals);
+    builder.append("\n");
+    builder.append("node.locals.size = ").append(node.localVariables.size());
+    builder.append("\n");
+    builder.append("node.insns.size = ").append(node.instructions.size());
+    builder.append("\n");
+    for (int i = 0; i < parameterTypes.size(); i++) {
+      builder.append("arg ").append(i).append(", type: ").append(parameterTypes.get(i))
+          .append("\n");
+    }
+    for (int i = 0; i < node.localVariables.size(); i++) {
+      LocalVariableNode local = (LocalVariableNode) node.localVariables.get(i);
+      builder.append("local ").append(i)
+          .append(", name: ").append(local.name)
+          .append(", desc: ").append(local.desc)
+          .append(", index: ").append(local.index)
+          .append(", [").append(getOffset(local.start))
+          .append("..").append(getOffset(local.end))
+          .append("[\n");
+    }
+    for (int i = 0; i < node.tryCatchBlocks.size(); i++) {
+      TryCatchBlockNode tryCatchBlockNode = (TryCatchBlockNode) node.tryCatchBlocks.get(i);
+      builder.append("[").append(getOffset(tryCatchBlockNode.start))
+          .append("..").append(getOffset(tryCatchBlockNode.end)).append("[ ")
+          .append(tryCatchBlockNode.type).append(" -> ")
+          .append(getOffset(tryCatchBlockNode.handler))
+          .append("\n");
+    }
+    for (int i = 0; i < node.instructions.size(); i++) {
+      AbstractInsnNode insn = node.instructions.get(i);
+      builder.append(String.format("%4d: ", i)).append(instructionToString(insn));
+    }
+    return builder.toString();
+  }
+
+  private String instructionToString(AbstractInsnNode insn) {
+    if (printVisitor == null) {
+      printVisitor = new TraceMethodVisitor(new Textifier());
+    }
+    insn.accept(printVisitor);
+    StringWriter writer = new StringWriter();
+    printVisitor.p.print(new PrintWriter(writer));
+    printVisitor.p.getText().clear();
+    return writer.toString();
+  }
+
+  private boolean isCallToPolymorphicSignatureMethod(MethodInsnNode method) {
+    return method.owner.equals("java/lang/invoke/MethodHandle")
+        && (method.name.equals("invoke") || method.name.equals("invokeExact"));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
new file mode 100644
index 0000000..e45fcb2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -0,0 +1,488 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import static com.android.tools.r8.ir.conversion.JarSourceCode.getArrayElementType;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LocalVariableNode;
+
+/**
+ * Abstraction of the java bytecode state at a given control-flow point.
+ *
+ * The abstract state is defined by the abstract contents of locals and contents of the stack.
+ */
+public class JarState {
+
+  // Type representatives of "any object/array" using invalid names (only valid for asserting).
+  public static final Type REFERENCE_TYPE = Type.getObjectType("<any reference>");
+  public static final Type OBJECT_TYPE = Type.getObjectType("<any object>");
+  public static final Type ARRAY_TYPE = Type.getObjectType("[<any array>");
+
+  // Type representative for the null value (non-existent but works for tracking the types here).
+  public static final Type NULL_TYPE = Type.getObjectType("<null>");
+
+  // Type representative for an address type (used by JSR/RET).
+  public static final Type ADDR_TYPE = Type.getObjectType("<address>");
+
+  private static final Slot INVALID_SLOT = new Slot(-1, null);
+  private static final Local INVALID_LOCAL = new Local(INVALID_SLOT, null);
+
+  public boolean isInvalid() {
+    return stack.peek() == INVALID_SLOT;
+  }
+
+  // Typed mapping from a local slot or stack slot to a virtual register.
+  public static class Slot {
+    public final int register;
+    public final Type type;
+
+    @Override
+    public String toString() {
+      return this == INVALID_SLOT ? "<invalid slot>" : "r" + register + ":" + type;
+    }
+
+    public Slot(int register, Type type) {
+      assert type != REFERENCE_TYPE;
+      assert type != OBJECT_TYPE;
+      assert type != ARRAY_TYPE;
+      this.register = register;
+      this.type = type;
+    }
+
+    public boolean isCompatibleWith(Type other) {
+      return isCompatible(type, other);
+    }
+
+    public boolean isCategory1() {
+      return isCategory1(type);
+    }
+
+    public static boolean isCategory1(Type type) {
+      return type != Type.LONG_TYPE && type != Type.DOUBLE_TYPE;
+    }
+
+    public static boolean isCompatible(Type type, Type other) {
+      assert type != REFERENCE_TYPE;
+      assert type != OBJECT_TYPE;
+      assert type != ARRAY_TYPE;
+      int sort = type.getSort();
+      int otherSort = other.getSort();
+      if (isReferenceCompatible(type, other)) {
+        return true;
+      }
+      // Integers are assumed compatible with any other 32-bit integral.
+      if (isIntCompatible(sort)) {
+        return isIntCompatible(otherSort);
+      }
+      if (isIntCompatible(otherSort)) {
+        return isIntCompatible(sort);
+      }
+      // In all other cases we require the two types to represent the same concrete type.
+      return type.equals(other);
+    }
+
+    private static boolean isIntCompatible(int sort) {
+      return Type.BOOLEAN <= sort && sort <= Type.INT;
+    }
+
+    private static boolean isReferenceCompatible(Type type, Type other) {
+      int sort = type.getSort();
+      int otherSort = other.getSort();
+
+      // Catch all matching.
+      if (other == REFERENCE_TYPE) {
+        return sort == Type.OBJECT || sort == Type.ARRAY;
+      }
+      if (other == OBJECT_TYPE) {
+        return sort == Type.OBJECT;
+      }
+      if (other == ARRAY_TYPE) {
+        return type == NULL_TYPE || sort == Type.ARRAY;
+      }
+
+      return (sort == Type.OBJECT && otherSort == Type.ARRAY)
+          || (sort == Type.ARRAY && otherSort == Type.OBJECT)
+          || (sort == Type.OBJECT && otherSort == Type.OBJECT)
+          || (sort == Type.ARRAY && otherSort == Type.ARRAY
+              && isReferenceCompatible(getArrayElementType(type), getArrayElementType(other)));
+    }
+  }
+
+  public static class Local {
+    final Slot slot;
+    final DebugLocalInfo info;
+
+    public Local(Slot slot, DebugLocalInfo info) {
+      this.slot = slot;
+      this.info = info;
+    }
+  }
+
+  // Immutable recording of the state (locals and stack should not be mutated).
+  private static class Snapshot {
+    public final Local[] locals;
+    public final ImmutableList<Slot> stack;
+
+    public Snapshot(Local[] locals, ImmutableList<Slot> stack) {
+      this.locals = locals;
+      this.stack = stack;
+    }
+
+    @Override
+    public String toString() {
+      return "locals: " + localsToString(Arrays.asList(locals))
+          + ", stack: " + stackToString(stack);
+    }
+  }
+
+  private final int startOfStack;
+  private int topOfStack;
+
+  // Locals are split into three parts based on types:
+  //  1) reference-type locals have registers in range: [0; localsSize[
+  //  2) single-width locals have registers in range: [localsSize; 2*localsSize[
+  //  3) wide-width locals have registers in range: [2*localsSize; 3*localsSize[
+  // This ensures that we can insert debugging-ranges into the SSA graph (via DebugLocal{Start,End})
+  // without conflating locals that are shared among different types. This issue arises because a
+  // debugging range can be larger than the definite-assignment scope of a local (eg, a local
+  // introduced in an unscoped switch case). To ensure that the SSA graph is valid we must introduce
+  // the local before inserting any DebugLocalReads (we do so in the method prelude, but that can
+  // potentially lead to phi functions merging locals of different move-types. Thus we allocate
+  // registers from the three distinct spaces.
+  private final int localsSize;
+  private final Local[] locals;
+
+  // Mapping from local-variable nodes to their canonical local info.
+  private final Map<LocalVariableNode, DebugLocalInfo> localVariables;
+
+  // Scope-points of all local variables for inserting debug scoping instructions.
+  private final Multimap<LabelNode, LocalVariableNode> localVariableStartPoints;
+  private final Multimap<LabelNode, LocalVariableNode> localVariableEndPoints;
+
+
+  private final Deque<Slot> stack = new ArrayDeque<>();
+
+  private final Map<Integer, Snapshot> targetStates = new HashMap<>();
+
+
+  public JarState(int maxLocals, Map<LocalVariableNode, DebugLocalInfo> localVariables) {
+    int localsRegistersSize = maxLocals * 3;
+    localsSize = maxLocals;
+    locals = new Local[localsRegistersSize];
+    startOfStack = localsRegistersSize;
+    topOfStack = startOfStack;
+    this.localVariables = localVariables;
+    localVariableStartPoints = HashMultimap.create();
+    localVariableEndPoints = HashMultimap.create();
+    populateLocalTables();
+  }
+
+  private void populateLocalTables() {
+    for (LocalVariableNode node : localVariables.keySet()) {
+      if (node.start != node.end) {
+        localVariableStartPoints.put(node.start, node);
+        localVariableEndPoints.put(node.end, node);
+      }
+    }
+  }
+
+  // Local variable procedures.
+
+  public List<Local> openLocals(LabelNode label) {
+    Collection<LocalVariableNode> nodes = localVariableStartPoints.get(label);
+    ArrayList<Local> locals = new ArrayList<>(nodes.size());
+    for (LocalVariableNode node : nodes) {
+      locals.add(setLocal(node.index, Type.getType(node.desc), localVariables.get(node)));
+    }
+    // Sort to ensure deterministic instruction ordering (correctness is unaffected).
+    locals.sort(Comparator.comparingInt(local -> local.slot.register));
+    return locals;
+  }
+
+  public List<Local> getLocalsToClose(LabelNode label) {
+    Collection<LocalVariableNode> nodes = localVariableEndPoints.get(label);
+    ArrayList<Local> locals = new ArrayList<>(nodes.size());
+    for (LocalVariableNode node : nodes) {
+      Type type = Type.getType(node.desc);
+      int register = getLocalRegister(node.index, type);
+      Local local = getLocalForRegister(register);
+      assert local != null;
+      locals.add(local);
+    }
+    // Sort to ensure deterministic instruction ordering (correctness is unaffected).
+    locals.sort(Comparator.comparingInt(local -> local.slot.register));
+    return locals;
+  }
+
+  public void closeLocals(List<Local> localsToClose) {
+    for (Local local : localsToClose) {
+      assert local != null;
+      assert local == getLocalForRegister(local.slot.register);
+      setLocalForRegister(local.slot.register, local.slot.type, null);
+    }
+  }
+
+  public ImmutableList<Local> getLocals() {
+    ImmutableList.Builder<Local> nonNullLocals = ImmutableList.builder();
+    for (Local local : locals) {
+      if (local != null) {
+        nonNullLocals.add(local);
+      }
+    }
+    return nonNullLocals.build();
+  }
+
+  int getLocalRegister(int index, Type type) {
+    assert index < localsSize;
+    if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
+      return index;
+    }
+    return Slot.isCategory1(type) ? index + localsSize : index + 2 * localsSize;
+  }
+
+  public DebugLocalInfo getLocalInfoForRegister(int register) {
+    if (register >= locals.length) {
+      return null;
+    }
+    Local local = getLocalForRegister(register);
+    return local == null ? null : local.info;
+  }
+
+  private Local getLocalForRegister(int register) {
+    return locals[register];
+  }
+
+  private Local getLocal(int index, Type type) {
+    return getLocalForRegister(getLocalRegister(index, type));
+  }
+
+  private Local setLocal(int index, Type type, DebugLocalInfo info) {
+    return setLocalForRegister(getLocalRegister(index, type), type, info);
+  }
+
+  private Local setLocalForRegister(int register, Type type, DebugLocalInfo info) {
+    Slot slot = new Slot(register, type);
+    Local local = new Local(slot, info);
+    locals[register] = local;
+    return local;
+  }
+
+  public int writeLocal(int index, Type type) {
+    assert type != null;
+    Local local = getLocal(index, type);
+    assert local == null || local.info == null || local.slot.isCompatibleWith(type);
+    // We cannot assume consistency for writes because we do not have complete information about the
+    // scopes of locals. We assume the program to be verified and overwrite if the types mismatch.
+    if (local == null || (local.info == null && !local.slot.type.equals(type))) {
+      local = setLocal(index, type, null);
+    }
+    return local.slot.register;
+  }
+
+  public Slot readLocal(int index, Type type) {
+    Local local = getLocal(index, type);
+    assert local != null;
+    assert local.slot.isCompatibleWith(type);
+    return local.slot;
+  }
+
+  // Stack procedures.
+
+  public int push(Type type) {
+    assert type != null;
+    int top = topOfStack;
+    // For simplicity, every stack slot (and local variable) is wide (uses two registers).
+    topOfStack += 2;
+    Slot slot = new Slot(top, type);
+    stack.push(slot);
+    return top;
+  }
+
+  public Slot peek() {
+    return stack.peek();
+  }
+
+  public Slot peek(Type type) {
+    Slot slot = stack.peek();
+    assert slot.isCompatibleWith(type);
+    return slot;
+  }
+
+  public Slot pop() {
+    assert topOfStack > startOfStack;
+    // For simplicity, every stack slot (and local variable) is wide (uses two registers).
+    topOfStack -= 2;
+    Slot slot = stack.pop();
+    assert slot.type != null;
+    assert slot.register == topOfStack;
+    return slot;
+  }
+
+  public Slot pop(Type type) {
+    Slot slot = pop();
+    assert slot.isCompatibleWith(type);
+    return slot;
+  }
+
+  public Slot[] popReverse(int count) {
+    Slot[] slots = new Slot[count];
+    for (int i = count - 1; i >= 0; i--) {
+      slots[i] = pop();
+    }
+    return slots;
+  }
+
+  public Slot[] popReverse(int count, Type type) {
+    Slot[] slots = popReverse(count);
+    assert verifySlots(slots, type);
+    return slots;
+  }
+
+  // State procedures.
+
+  public void invalidateState() {
+    for (int i = 0; i < locals.length; i++) {
+      locals[i] = INVALID_LOCAL;
+    }
+    stack.clear();
+    stack.push(INVALID_SLOT);
+    topOfStack = -1;
+  }
+
+  public boolean hasState(int offset) {
+    return targetStates.get(offset) != null;
+  }
+
+  public void restoreState(int offset) {
+    Snapshot snapshot = targetStates.get(offset);
+    assert snapshot != null;
+    assert locals.length == snapshot.locals.length;
+    for (int i = 0; i < locals.length; i++) {
+      assert locals[i] == INVALID_LOCAL;
+      locals[i] = snapshot.locals[i];
+    }
+    assert stack.peek() == INVALID_SLOT;
+    assert topOfStack == -1;
+    stack.clear();
+    stack.addAll(snapshot.stack);
+    topOfStack = startOfStack + 2 * stack.size();
+  }
+
+  public void recordStateForTarget(int target, JarSourceCode source) {
+    recordStateForTarget(target, locals.clone(), ImmutableList.copyOf(stack), source);
+  }
+
+  public void recordStateForExceptionalTarget(int target, JarSourceCode source) {
+    recordStateForTarget(target, locals.clone(), ImmutableList.of(), source);
+  }
+
+  private void recordStateForTarget(int target, Local[] locals, ImmutableList<Slot> stack,
+      JarSourceCode source) {
+    Snapshot snapshot = targetStates.get(target);
+    if (snapshot == null) {
+      if (!localVariables.isEmpty()) {
+        for (int i = 0; i < locals.length; i++) {
+          if (locals[i] != null) {
+            locals[i] = new Local(locals[i].slot, null);
+          }
+        }
+        // TODO(zerny): Precompute and sort the local ranges.
+        for (LocalVariableNode node : localVariables.keySet()) {
+          int startOffset = source.getOffset(node.start);
+          int endOffset = source.getOffset(node.end);
+          if (startOffset <= target && target < endOffset) {
+            int register = getLocalRegister(node.index, Type.getType(node.desc));
+            Local local = locals[register];
+            locals[register] = new Local(local.slot, localVariables.get(node));
+          }
+        }
+      }
+      targetStates.put(target, new Snapshot(locals, stack));
+      return;
+    }
+    assert verifyStack(stack, snapshot.stack);
+  }
+
+  private static boolean verifyStack(List<Slot> stack, List<Slot> other) {
+    assert stack.size() == other.size();
+    int i = 0;
+    for (Slot slot : stack) {
+      assert slot.isCompatibleWith(other.get(i++).type);
+    }
+    return true;
+  }
+
+  // Other helpers.
+
+  private static boolean verifySlots(Slot[] slots, Type type) {
+    for (Slot slot : slots) {
+      assert slot.isCompatibleWith(type);
+    }
+    return true;
+  }
+
+  // Printing helpers.
+
+  public String toString() {
+    return "locals: " + localsToString(Arrays.asList(locals)) + ", stack: " + stackToString(stack);
+  }
+
+  public static String stackToString(Collection<Slot> stack) {
+    List<String> strings = new ArrayList<>(stack.size());
+    for (Slot slot : stack) {
+      if (slot == INVALID_SLOT) {
+        return "<stack invalidated>";
+      }
+      strings.add(slot.type.toString());
+    }
+    StringBuilder builder = new StringBuilder("{ ");
+    for (int i = strings.size() - 1; i >= 0; i--) {
+      builder.append(strings.get(i));
+      if (i > 0) {
+        builder.append(", ");
+      }
+    }
+    builder.append(" }");
+    return builder.toString();
+  }
+
+  public static String localsToString(Collection<Local> locals) {
+    StringBuilder builder = new StringBuilder("{ ");
+    boolean first = true;
+    for (Local local : locals) {
+      if (local == INVALID_LOCAL) {
+        return "<locals invalidated>";
+      }
+      if (!first) {
+        builder.append(", ");
+      } else {
+        first = false;
+      }
+      if (local == null) {
+        builder.append("_");
+      } else if (local.info != null) {
+        builder.append(local.info);
+      } else {
+        builder.append(local.slot.type.toString());
+      }
+    }
+    builder.append(" }");
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
new file mode 100644
index 0000000..13507ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -0,0 +1,194 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.stream.Collectors;
+
+public class LensCodeRewriter {
+
+  private final GraphLense graphLense;
+  private final AppInfoWithSubtyping appInfo;
+
+  public LensCodeRewriter(GraphLense graphLense, AppInfoWithSubtyping appInfo) {
+    this.graphLense = graphLense;
+    this.appInfo = appInfo;
+  }
+
+  /**
+   * Replace invoke targets and field accesses with actual definitions.
+   */
+  public void rewrite(IRCode code, DexEncodedMethod method) {
+    ListIterator<BasicBlock> blocks = code.blocks.listIterator();
+    while (blocks.hasNext()) {
+      BasicBlock block = blocks.next();
+      InstructionListIterator iterator = block.listIterator();
+      while (iterator.hasNext()) {
+        Instruction current = iterator.next();
+        if (current.isInvokeCustom()) {
+          InvokeCustom invokeCustom = current.asInvokeCustom();
+          DexCallSite callSite = invokeCustom.getCallSite();
+          DexMethodHandle newBootstrapMethod = rewriteDexMethodHandle(method,
+              callSite.bootstrapMethod);
+          List<DexValue> newArgs = callSite.bootstrapArgs.stream().map(
+              (arg) -> {
+                if (arg instanceof DexValueMethodHandle) {
+                  return new DexValueMethodHandle(
+                      rewriteDexMethodHandle(method, ((DexValueMethodHandle) arg).value));
+                }
+                return arg;
+              })
+              .collect(Collectors.toList());
+
+          if (newBootstrapMethod != callSite.bootstrapMethod
+              || !newArgs.equals(callSite.bootstrapArgs)) {
+            DexCallSite newCallSite = appInfo.dexItemFactory.createCallSite(
+                callSite.methodName, callSite.methodProto, newBootstrapMethod, newArgs);
+            InvokeCustom newInvokeCustom = new InvokeCustom(newCallSite, invokeCustom.outValue(),
+                invokeCustom.inValues());
+            iterator.replaceCurrentInstruction(newInvokeCustom);
+          }
+        } else if (current.isInvokeMethod()) {
+          InvokeMethod invoke = current.asInvokeMethod();
+          DexMethod invokedMethod = invoke.getInvokedMethod();
+          DexType invokedHolder = invokedMethod.getHolder();
+          if (!invokedHolder.isClassType()) {
+            continue;
+          }
+          DexMethod actualTarget = graphLense.lookupMethod(invokedMethod, method);
+          if (actualTarget != invokedMethod) {
+            Invoke newInvoke = Invoke
+                .create(getInvokeType(invoke, actualTarget), actualTarget, null,
+                    invoke.outValue(), invoke.inValues());
+            iterator.replaceCurrentInstruction(newInvoke);
+            // Fix up the return type if needed.
+            if (actualTarget.proto.returnType != invokedMethod.proto.returnType
+                && newInvoke.outValue() != null) {
+              Value newValue = new Value(
+                  code.valueNumberGenerator.next(), -1, newInvoke.outType(), invoke.getDebugInfo());
+              newInvoke.outValue().replaceUsers(newValue);
+              CheckCast cast = new CheckCast(
+                  newValue,
+                  newInvoke.outValue(),
+                  invokedMethod.proto.returnType);
+              iterator.add(cast);
+              // If the current block has catch handlers split the check cast into its own block.
+              if (newInvoke.getBlock().hasCatchHandlers()) {
+                iterator.previous();
+                iterator.split(1, code, blocks);
+              }
+            }
+          }
+        } else if (current.isInstanceGet()) {
+          InstanceGet instanceGet = current.asInstanceGet();
+          DexField field = instanceGet.getField();
+          DexField actualField = graphLense.lookupField(field, method);
+          if (actualField != field) {
+            InstanceGet newInstanceGet =
+                new InstanceGet(
+                    instanceGet.getType(), instanceGet.dest(), instanceGet.object(), actualField);
+            iterator.replaceCurrentInstruction(newInstanceGet);
+          }
+        } else if (current.isInstancePut()) {
+          InstancePut instancePut = current.asInstancePut();
+          DexField field = instancePut.getField();
+          DexField actualField = graphLense.lookupField(field, method);
+          if (actualField != field) {
+            InstancePut newInstancePut =
+                new InstancePut(instancePut.getType(), instancePut.inValues(), actualField);
+            iterator.replaceCurrentInstruction(newInstancePut);
+          }
+        } else if (current.isStaticGet()) {
+          StaticGet staticGet = current.asStaticGet();
+          DexField field = staticGet.getField();
+          DexField actualField = graphLense.lookupField(field, method);
+          if (actualField != field) {
+            StaticGet newStaticGet =
+                new StaticGet(staticGet.getType(), staticGet.dest(), actualField);
+            iterator.replaceCurrentInstruction(newStaticGet);
+          }
+        } else if (current.isStaticPut()) {
+          StaticPut staticPut = current.asStaticPut();
+          DexField field = staticPut.getField();
+          DexField actualField = graphLense.lookupField(field, method);
+          if (actualField != field) {
+            StaticPut newStaticPut =
+                new StaticPut(staticPut.getType(), staticPut.inValue(), actualField);
+            iterator.replaceCurrentInstruction(newStaticPut);
+          }
+        }
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+
+  private DexMethodHandle rewriteDexMethodHandle(
+      DexEncodedMethod method, DexMethodHandle methodHandle) {
+    if (methodHandle.isMethodHandle()) {
+      DexMethod invokedMethod = methodHandle.asMethod();
+      DexMethod actualTarget = graphLense.lookupMethod(invokedMethod, method);
+      if (actualTarget != invokedMethod) {
+        DexClass clazz = appInfo.definitionFor(actualTarget.holder);
+        MethodHandleType newType = methodHandle.type;
+        if (clazz != null
+            && (newType.isInvokeInterface() || newType.isInvokeInstance())) {
+          newType = clazz.accessFlags.isInterface()
+              ? MethodHandleType.INVOKE_INTERFACE
+              : MethodHandleType.INVOKE_INSTANCE;
+        }
+        return new DexMethodHandle(newType, actualTarget);
+      }
+    } else {
+      DexField field = methodHandle.asField();
+      DexField actualField = graphLense.lookupField(field, method);
+      if (actualField != field) {
+        return new DexMethodHandle(methodHandle.type, actualField);
+      }
+    }
+    return methodHandle;
+  }
+
+  private Type getInvokeType(InvokeMethod invoke, DexMethod actualTarget) {
+    if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
+      // Get the invoke type of the actual definition.
+      DexClass clazz = appInfo.definitionFor(actualTarget.holder);
+      if (clazz == null) {
+        return invoke.getType();
+      } else {
+        return clazz.accessFlags.isInterface()
+            ? Type.INTERFACE
+            : Type.VIRTUAL;
+      }
+    } else {
+      return invoke.getType();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
new file mode 100644
index 0000000..ba03f82
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Switch.Type;
+
+/**
+ * Abstraction of the input/source code for the IRBuilder.
+ *
+ * Implementations of the abstraction need to compute/provide the block-structure of the source and
+ * delegate building of the actual instruction stream.
+ */
+public interface SourceCode {
+
+  // Accessors.
+  int instructionCount();
+  int instructionIndex(int instructionOffset);
+  int instructionOffset(int instructionIndex);
+
+  // True if the method needs a special entry block (prelude) that is not a targetable block.
+  // This is the case for methods with arguments or that need additional synchronization support.
+  boolean needsPrelude();
+
+  DebugLocalInfo getCurrentLocal(int register);
+
+  /**
+   * Trace block structure of the source-program.
+   *
+   * <p>The instruction at {@code index} is traced and its target blocks are marked by using
+   * {@code IRBuilder.ensureSuccessorBlock} (and {@code ensureBlockWithoutEnqueuing}).
+   *
+   * @return True if the instruction closes the current block, false otherwise.
+   */
+  boolean traceInstruction(int instructionIndex, IRBuilder builder);
+
+  void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex);
+  void closedCurrentBlock();
+
+  // Setup and release resources used temporarily during trace/build.
+  void setUp();
+  void clear();
+
+  // Delegates for IR building.
+  void buildPrelude(IRBuilder builder);
+  void buildInstruction(IRBuilder builder, int instructionIndex);
+  void buildPostlude(IRBuilder builder);
+
+  // Helper to resolve switch payloads and build switch instructions (dex code only).
+  void resolveAndBuildSwitch(Type type, int value, int fallthroughOffset, int payloadOffset,
+      IRBuilder builder);
+
+  // Helper to resolve fill-array data and build new-array instructions (dex code only).
+  void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset, IRBuilder builder);
+
+  CatchHandlers<Integer> getCurrentCatchHandlers();
+
+  // For debugging/verification purpose.
+  boolean verifyRegister(int register);
+  boolean verifyCurrentInstructionCanThrow();
+  boolean verifyLocalInScope(DebugLocalInfo local);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
new file mode 100644
index 0000000..5367a3a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion;
+
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.SwitchPayload;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for resolving payload information during IR construction.
+ */
+public class SwitchPayloadResolver {
+
+  public static class PayloadData {
+
+    public final static int NO_SIZE = -1;
+    public int userOffset;
+    public int fallthroughOffset;
+    public int[] absoluteTargets = null;
+    public int[] keys = null;
+    public int size = NO_SIZE;
+
+    public PayloadData(int userOffset, int fallthroughOffset) {
+      this.userOffset = userOffset;
+      this.fallthroughOffset = fallthroughOffset;
+    }
+  }
+
+  private final Map<Integer, SwitchPayload> unresolvedPayload = new HashMap<>();
+  private final Map<Integer, PayloadData> payloadToData = new HashMap<>();
+
+  public void addPayloadUser(Instruction dex) {
+    int offset = dex.getOffset();
+    int payloadOffset = offset + dex.getPayloadOffset();
+    payloadToData.put(payloadOffset, new PayloadData(offset, offset + dex.getSize()));
+    if (unresolvedPayload.containsKey(payloadOffset)) {
+      SwitchPayload payload = unresolvedPayload.remove(payloadOffset);
+      resolve(payload);
+    }
+  }
+
+  public void resolve(SwitchPayload payload) {
+    int payloadOffset = payload.getOffset();
+    PayloadData data = payloadToData.get(payloadOffset);
+    if (data == null) {
+      unresolvedPayload.put(payloadOffset, payload);
+      return;
+    }
+
+    int[] targets = payload.switchTargetOffsets();
+    int[] absoluteTargets = new int[targets.length];
+    for (int i = 0; i < targets.length; i++) {
+      absoluteTargets[i] = data.userOffset + targets[i];
+    }
+    data.absoluteTargets = absoluteTargets;
+    data.keys = payload.keys();
+    data.size = payload.numberOfKeys();
+  }
+
+  public int[] absoluteTargets(Instruction dex) {
+    assert dex.isSwitch();
+    return absoluteTargets(dex.getOffset() + dex.getPayloadOffset());
+  }
+
+  public int[] absoluteTargets(int payloadOffset) {
+    return payloadToData.get(payloadOffset).absoluteTargets;
+  }
+
+  public int[] getKeys(int payloadOffset) {
+    return payloadToData.get(payloadOffset).keys;
+  }
+
+  public int getSize(int payloadOffset) {
+    return payloadToData.get(payloadOffset).size;
+  }
+
+  public Collection<PayloadData> payloadDataSet() {
+    return payloadToData.values();
+  }
+
+  public void clear() {
+    payloadToData.clear();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
new file mode 100644
index 0000000..bee210c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing synthesized accessor method.
+final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {
+  AccessorMethodSourceCode(LambdaClass lambda) {
+    super(/* no receiver for static method */ null, lambda, lambda.target.callTarget);
+    // We should never need an accessor for interface methods since
+    // they are supposed to be public.
+    assert !descriptor().implHandle.type.isInvokeInterface();
+    assert checkSignatures();
+  }
+
+  private boolean checkSignatures() {
+    DexMethodHandle implHandle = descriptor().implHandle;
+    assert implHandle != null;
+
+    DexType[] accessorParams = proto.parameters.values;
+    DexMethod implMethod = implHandle.asMethod();
+    DexProto implProto = implMethod.proto;
+    DexType[] implParams = implProto.parameters.values;
+
+    int index = 0;
+    if (implHandle.type.isInvokeInstance()) {
+      assert accessorParams[index] == descriptor().getImplReceiverType();
+      index++;
+    }
+
+    for (DexType implParam : implParams) {
+      assert accessorParams[index] == implParam;
+      index++;
+    }
+    assert index == accessorParams.length;
+
+    assert delegatingToConstructor()
+        ? this.proto.returnType == implMethod.holder
+        : this.proto.returnType == implProto.returnType;
+    return true;
+  }
+
+  private boolean isPrivateMethod() {
+    // We should be able to find targets for all private impl-methods, so
+    // we can rely on knowing accessibility flags for them.
+    DexAccessFlags flags = descriptor().getAccessibility();
+    return flags != null && flags.isPrivate();
+  }
+
+  // Are we delegating to a constructor?
+  private boolean delegatingToConstructor() {
+    return descriptor().implHandle.type.isInvokeConstructor();
+  }
+
+  private Invoke.Type inferInvokeType() {
+    switch (descriptor().implHandle.type) {
+      case INVOKE_INSTANCE:
+        return isPrivateMethod() ? Invoke.Type.DIRECT : Invoke.Type.VIRTUAL;
+      case INVOKE_STATIC:
+        return Invoke.Type.STATIC;
+      case INVOKE_CONSTRUCTOR:
+        return Invoke.Type.DIRECT;
+      case INVOKE_INTERFACE:
+        throw new Unreachable("Accessor for an interface method?");
+      default:
+        throw new Unreachable();
+    }
+  }
+
+  @Override
+  void prepareInstructions() {
+    DexMethod implMethod = descriptor().implHandle.asMethod();
+    DexType[] accessorParams = proto.parameters.values;
+
+    // Prepare call arguments.
+    List<MoveType> argMoveTypes = new ArrayList<>();
+    List<Integer> argRegisters = new ArrayList<>();
+
+    // If we are delegating to a constructor, we need to create the instance
+    // first. This instance will be the first argument to the call.
+    if (delegatingToConstructor()) {
+      int instance = nextRegister(MoveType.OBJECT);
+      add(builder -> builder.addNewInstance(instance, implMethod.holder));
+      argMoveTypes.add(MoveType.OBJECT);
+      argRegisters.add(instance);
+    }
+
+    for (int i = 0; i < accessorParams.length; i++) {
+      DexType param = accessorParams[i];
+      argMoveTypes.add(MoveType.fromDexType(param));
+      argRegisters.add(getParamRegister(i));
+    }
+
+    // Method call to the original impl-method.
+    add(builder -> builder.addInvoke(inferInvokeType(),
+        implMethod, implMethod.proto, argMoveTypes, argRegisters));
+
+    // Does the method have return value?
+    if (proto.returnType == factory().voidType) {
+      add(IRBuilder::addReturn);
+    } else if (delegatingToConstructor()) {
+      // Return newly created instance
+      add(builder -> builder.addReturn(MoveType.OBJECT, argRegisters.get(0)));
+    } else {
+      MoveType moveType = MoveType.fromDexType(proto.returnType);
+      int tempValue = nextRegister(moveType);
+      add(builder -> builder.addMoveResult(moveType, tempValue));
+      add(builder -> builder.addReturn(moveType, tempValue));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
new file mode 100644
index 0000000..ba15c9d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -0,0 +1,231 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+// Default and static method interface desugaring processor for classes.
+// Adds default interface methods into the class when needed.
+final class ClassProcessor {
+  private final InterfaceMethodRewriter rewriter;
+  // Set of already processed classes.
+  private final Set<DexClass> processedClasses = Sets.newIdentityHashSet();
+  // Maps already created methods into default methods they were generated based on.
+  private final Map<DexEncodedMethod, DexEncodedMethod> createdMethods = new IdentityHashMap<>();
+  // Caches default interface method info for already processed interfaces.
+  private final Map<DexType, DefaultMethodsHelper.Collection> cache = new IdentityHashMap<>();
+
+  ClassProcessor(InterfaceMethodRewriter rewriter) {
+    this.rewriter = rewriter;
+  }
+
+  final Set<DexEncodedMethod> getForwardMethods() {
+    return createdMethods.keySet();
+  }
+
+  final void process(DexClass clazz) {
+    if (clazz.isInterface()) {
+      throw new CompilationError("Interface in superclass chain.");
+    }
+    if (!clazz.isProgramClass()) {
+      // We assume that library classes don't need to be processed, since they
+      // are provided by a runtime not supporting default interface methods.
+      // We also skip classpath classes, which results in sub-optimal behavior
+      // in case classpath superclass when processed adds a default method which
+      // could have been reused in this class otherwise.
+      return;
+    }
+    if (!processedClasses.add(clazz)) {
+      return; // Has already been processed.
+    }
+
+    // Ensure superclasses are processed first. We need it since we use information
+    // about methods added to superclasses when we decide if we want to add a default
+    // method to class `clazz`.
+    DexType superType = clazz.superType;
+    if (superType != null && superType != rewriter.factory.objectType) {
+      process(rewriter.findRequiredClass(superType));
+    }
+
+    if (clazz.interfaces.isEmpty()) {
+      // Since superclass has already been processed and it has all missing methods
+      // added, these methods will be inherited by `clazz`, and only need to be revised
+      // in case this class has *additional* interfaces implemented, which may change
+      // the entire picture of the default method selection in runtime.
+      return;
+    }
+
+    // Collect the default interface methods to be added to this class.
+    List<DexEncodedMethod> methodsToImplement = collectMethodsToImplement(clazz);
+    if (methodsToImplement.isEmpty()) {
+      return;
+    }
+
+    // Add the methods.
+    DexEncodedMethod[] existing = clazz.virtualMethods;
+    clazz.virtualMethods = new DexEncodedMethod[existing.length + methodsToImplement.size()];
+    System.arraycopy(existing, 0, clazz.virtualMethods, 0, existing.length);
+
+    for (int i = 0; i < methodsToImplement.size(); i++) {
+      DexEncodedMethod method = methodsToImplement.get(i);
+      assert method.accessFlags.isPublic() && !method.accessFlags.isAbstract();
+      DexEncodedMethod newMethod = addForwardingMethod(method, clazz);
+      clazz.virtualMethods[existing.length + i] = newMethod;
+      createdMethods.put(newMethod, method);
+    }
+  }
+
+  private DexEncodedMethod addForwardingMethod(DexEncodedMethod defaultMethod, DexClass clazz) {
+    DexMethod method = defaultMethod.method;
+    assert !rewriter.findRequiredClass(method.holder).isLibraryClass();
+    // New method will have the same name, proto, and also all the flags of the
+    // default method, including bridge flag.
+    DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
+    DexAccessFlags newFlags = new DexAccessFlags(defaultMethod.accessFlags.get());
+    return new DexEncodedMethod(newMethod, newFlags,
+        defaultMethod.annotations, defaultMethod.parameterAnnotations,
+        new SynthesizedCode(new ForwardMethodSourceCode(
+            clazz.type, method.proto, /* static method */ null,
+            rewriter.defaultAsMethodOfCompanionClass(method),
+            Invoke.Type.STATIC)));
+  }
+
+  // For a given class `clazz` inspects all interfaces it implements directly or
+  // indirectly and collect a set of all default methods to be implemented
+  // in this class.
+  private List<DexEncodedMethod> collectMethodsToImplement(DexClass clazz) {
+    DefaultMethodsHelper helper = new DefaultMethodsHelper();
+
+    // Collect candidate default methods by inspecting interfaces implemented
+    // by this class as well as its superclasses.
+    DexClass current = clazz;
+    while (true) {
+      for (DexType type : clazz.interfaces.values) {
+        helper.merge(getOrCreateInterfaceInfo(type));
+      }
+
+      DexType superType = current.superType;
+      if (superType == null || superType == rewriter.factory.objectType) {
+        // We assume here that interfaces implemented by java.lang.Object don't
+        // have default methods and don't hide any default interface methods since
+        // they must be library interfaces.
+        break;
+      }
+      current = rewriter.findRequiredClass(superType);
+    }
+
+    List<DexEncodedMethod> candidates = helper.createCandidatesList();
+    if (candidates.isEmpty()) {
+      return candidates;
+    }
+
+    // Remove from candidates methods defined in class or any of its superclasses.
+    List<DexEncodedMethod> toBeImplemented = new ArrayList<>(candidates.size());
+    current = clazz;
+    while (true) {
+      // Hide candidates by virtual method of the class.
+      hideCandidates(clazz.virtualMethods, candidates, toBeImplemented);
+      if (candidates.isEmpty()) {
+        return toBeImplemented;
+      }
+
+      DexType superType = current.superType;
+      if (superType == null || superType == rewriter.factory.objectType) {
+        // Note that default interface methods must never have same
+        // name/signature as any method in java.lang.Object (JLS §9.4.1.2).
+
+        // Everything still in candidate list is not hidden.
+        toBeImplemented.addAll(candidates);
+        return toBeImplemented;
+      }
+      current = rewriter.findRequiredClass(superType);
+    }
+  }
+
+  private void hideCandidates(DexEncodedMethod[] virtualMethods,
+      List<DexEncodedMethod> candidates, List<DexEncodedMethod> toBeImplemented) {
+    Iterator<DexEncodedMethod> it = candidates.iterator();
+    while (it.hasNext()) {
+      DexEncodedMethod candidate = it.next();
+      for (DexEncodedMethod encoded : virtualMethods) {
+        if (candidate.method.match(encoded)) {
+          // Found a methods hiding the candidate.
+          DexEncodedMethod basedOnCandidate = createdMethods.get(encoded);
+          if (basedOnCandidate != null) {
+            // The method we found is a method we have generated for a default interface
+            // method in a superclass. If the method is based on the same candidate we don't
+            // need to re-generate this method again since it is going to be inherited.
+            if (basedOnCandidate != candidate) {
+              // Need to re-generate since the inherited version is
+              // based on a different candidate.
+              toBeImplemented.add(candidate);
+            }
+          }
+
+          // Done with this candidate.
+          it.remove();
+          break;
+        }
+      }
+    }
+  }
+
+  private DefaultMethodsHelper.Collection getOrCreateInterfaceInfo(DexType iface) {
+    DefaultMethodsHelper.Collection collection = cache.get(iface);
+    if (collection != null) {
+      return collection;
+    }
+    collection = createInterfaceInfo(iface);
+    cache.put(iface, collection);
+    return collection;
+  }
+
+  private DefaultMethodsHelper.Collection createInterfaceInfo(DexType iface) {
+    DefaultMethodsHelper helper = new DefaultMethodsHelper();
+    DexClass clazz = rewriter.findRequiredClass(iface);
+    if (!clazz.isInterface()) {
+      throw new CompilationError(
+          "Type " + iface.toSourceString() + " is expected to be an interface.");
+    }
+    if (clazz.isLibraryClass()) {
+      // For library interfaces we always assume there are no default
+      // methods, since the interface is part of framework provided by
+      // runtime which does not support default interface methods.
+      return DefaultMethodsHelper.Collection.EMPTY;
+    }
+
+    // Merge information from all superinterfaces.
+    for (DexType superinterface : clazz.interfaces.values) {
+      helper.merge(getOrCreateInterfaceInfo(superinterface));
+    }
+
+    // Hide by virtual methods of this interface.
+    for (DexEncodedMethod virtual : clazz.virtualMethods) {
+      helper.hideMatches(virtual.method);
+    }
+
+    // Add all default methods of this interface.
+    for (DexEncodedMethod encoded : clazz.virtualMethods) {
+      if (rewriter.isDefaultMethod(encoded)) {
+        helper.addDefaultMethod(encoded);
+      }
+    }
+
+    return helper.wrapInCollection();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
new file mode 100644
index 0000000..2f25b19
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DefaultMethodsHelper.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+// Helper class implementing bunch of default interface method handling operations.
+final class DefaultMethodsHelper {
+  // Current set of default interface methods, may overlap with `hidden`.
+  private final Set<DexEncodedMethod> candidates = Sets.newIdentityHashSet();
+  // Current set of known hidden default interface methods.
+  private final Set<DexEncodedMethod> hidden = Sets.newIdentityHashSet();
+
+  // Represents information about default interface methods of an
+  // interface and its superinterfaces. Namely: a list of live (not hidden)
+  // and hidden default interface methods in this interface's hierarchy.
+  //
+  // Note that it is assumes that these lists should never be big.
+  final static class Collection {
+    static final Collection EMPTY =
+        new Collection(Collections.emptyList(), Collections.emptyList());
+
+    // All live default interface methods in this interface's hierarchy.
+    private final List<DexEncodedMethod> live;
+    // All hidden default interface methods in this interface's hierarchy.
+    private final List<DexEncodedMethod> hidden;
+
+    private Collection(List<DexEncodedMethod> live, List<DexEncodedMethod> hidden) {
+      this.live = live;
+      this.hidden = hidden;
+    }
+  }
+
+  final void merge(Collection collection) {
+    candidates.addAll(collection.live);
+    hidden.addAll(collection.hidden);
+  }
+
+  final void hideMatches(DexMethod method) {
+    Iterator<DexEncodedMethod> it = candidates.iterator();
+    while (it.hasNext()) {
+      DexEncodedMethod candidate = it.next();
+      if (method.match(candidate)) {
+        hidden.add(candidate);
+        it.remove();
+      }
+    }
+  }
+
+  final void addDefaultMethod(DexEncodedMethod encoded) {
+    candidates.add(encoded);
+  }
+
+  // Creates a list of default method candidates to be implemented in the class.
+  final List<DexEncodedMethod> createCandidatesList() {
+    this.candidates.removeAll(hidden);
+    if (this.candidates.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    // The list of non-hidden default methods. The list is not expected to be big,
+    // since it only consists of default methods which are maximally specific
+    // interface method of a particular class.
+    List<DexEncodedMethod> candidates = new LinkedList<>();
+
+    // Note that it is possible for a class to have more than one maximally specific
+    // interface method. But runtime requires that when a method is called, there must be
+    // found *only one* maximally specific interface method and this method should be
+    // non-abstract, otherwise a runtime error is generated.
+    //
+    // This code assumes that if such erroneous case exist for particular name/signature,
+    // a method with this name/signature must be defined in class or one of its superclasses,
+    // or otherwise it should never be called. This means that if we see two default method
+    // candidates with same name/signature, it is safe to assume that we don't need to add
+    // these method to the class, because if it was missing in class and its superclasses
+    // but still called in the original code, this call would have resulted in runtime error.
+    // So we are just leaving it unimplemented with the same effect (with a different runtime
+    // exception though).
+    for (DexEncodedMethod candidate : this.candidates) {
+      Iterator<DexEncodedMethod> it = candidates.iterator();
+      boolean conflict = false;
+      while (it.hasNext()) {
+        if (candidate.method.match(it.next())) {
+          conflict = true;
+          it.remove();
+        }
+      }
+      if (!conflict) {
+        candidates.add(candidate);
+      }
+    }
+    return candidates;
+  }
+
+  // Create default interface collection based on collected information.
+  final Collection wrapInCollection() {
+    candidates.removeAll(hidden);
+    return (candidates.isEmpty() && hidden.isEmpty()) ? Collection.EMPTY
+        : new Collection(Lists.newArrayList(candidates), Lists.newArrayList(hidden));
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java
new file mode 100644
index 0000000..808456b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ForwardMethodSourceCode.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing simple forwarding method.
+final class ForwardMethodSourceCode extends SingleBlockSourceCode {
+  private final DexType targetReceiver;
+  private final DexMethod target;
+  private final Invoke.Type invokeType;
+
+  ForwardMethodSourceCode(DexType receiver, DexProto proto,
+      DexType targetReceiver, DexMethod target, Invoke.Type invokeType) {
+    super(receiver, proto);
+    assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC);
+
+    this.target = target;
+    this.targetReceiver = targetReceiver;
+    this.invokeType = invokeType;
+    assert checkSignatures();
+
+    if (invokeType != Invoke.Type.STATIC) {
+      throw new Unimplemented("Invoke type " + invokeType + " is not yet supported.");
+    }
+  }
+
+  private boolean checkSignatures() {
+    List<DexType> sourceParams = new ArrayList<>();
+    if (receiver != null) {
+      sourceParams.add(receiver);
+    }
+    sourceParams.addAll(Lists.newArrayList(proto.parameters.values));
+
+    List<DexType> targetParams = new ArrayList<>();
+    if (targetReceiver != null) {
+      targetParams.add(targetReceiver);
+    }
+    targetParams.addAll(Lists.newArrayList(target.proto.parameters.values));
+
+    assert sourceParams.size() == targetParams.size();
+    for (int i = 0; i < sourceParams.size(); i++) {
+      DexType source = sourceParams.get(i);
+      DexType target = targetParams.get(i);
+
+      // We assume source is compatible with target if they both are classes.
+      // This check takes care of receiver widening conversion but does not
+      // many others, like conversion from an array to Object.
+      assert (source.isClassType() && target.isClassType()) || source == target;
+    }
+
+    assert this.proto.returnType == target.proto.returnType;
+    return true;
+  }
+
+  @Override
+  void prepareInstructions() {
+    // Prepare call arguments.
+    List<MoveType> argMoveTypes = new ArrayList<>();
+    List<Integer> argRegisters = new ArrayList<>();
+
+    if (receiver != null) {
+      argMoveTypes.add(MoveType.OBJECT);
+      argRegisters.add(getReceiverRegister());
+    }
+
+    DexType[] accessorParams = proto.parameters.values;
+    for (int i = 0; i < accessorParams.length; i++) {
+      argMoveTypes.add(MoveType.fromDexType(accessorParams[i]));
+      argRegisters.add(getParamRegister(i));
+    }
+
+    // Method call to the target method.
+    add(builder -> builder.addInvoke(this.invokeType,
+        this.target, this.target.proto, argMoveTypes, argRegisters));
+
+    // Does the method return value?
+    if (proto.returnType.isVoidType()) {
+      add(IRBuilder::addReturn);
+    } else {
+      MoveType moveType = MoveType.fromDexType(proto.returnType);
+      int tempValue = nextRegister(moveType);
+      add(builder -> builder.addMoveResult(moveType, tempValue));
+      add(builder -> builder.addReturn(moveType, tempValue));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
new file mode 100644
index 0000000..81576ec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -0,0 +1,265 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+//
+// Default and static interface method desugaring rewriter (note that lambda
+// desugaring should have already processed the code before this rewriter).
+//
+// In short, during default and static interface method desugaring
+// the following actions are performed:
+//
+//   (1) All static interface methods are moved into companion classes. All calls
+//       to these methods are redirected appropriately. All references to these
+//       methods from method handles are reported as errors.
+//
+// Companion class is a synthesized class (<interface-name>-CC) created to host
+// static and former default interface methods (see below) from the interface.
+//
+//   (2) All default interface methods are made static and moved into companion
+//       class.
+//
+//   (3) All calls to default interface methods made via 'super' are changed
+//       to directly call appropriate static methods in companion classes.
+//
+//   (4) All other calls or references to default interface methods are not changed.
+//
+//   (5) For all program classes explicitly implementing interfaces we analyze the
+//       set of default interface methods missing and add them, the created methods
+//       forward the call to an appropriate method in interface companion class.
+//
+public final class InterfaceMethodRewriter {
+  private static final String COMPANION_CLASS_NAME_SUFFIX = "-CC";
+  private static final String DEFAULT_METHOD_PREFIX = "$default$";
+
+  private final IRConverter converter;
+  final DexItemFactory factory;
+
+  // All forwarding methods generated during desugaring. We don't synchronize access
+  // to this collection since it is only filled in ClassProcessor running synchronously.
+  private final Set<DexEncodedMethod> forwardingMethods = Sets.newIdentityHashSet();
+
+  /** Defines a minor variation in desugaring. */
+  public enum Flavor {
+    /** Process all application resources. */
+    IncludeAllResources,
+    /** Process all but DEX application resources. */
+    ExcludeDexResources
+  }
+
+  public InterfaceMethodRewriter(IRConverter converter) {
+    assert converter != null;
+    this.converter = converter;
+    this.factory = converter.application.dexItemFactory;
+  }
+
+  // Rewrites the references to static and default interface methods.
+  // NOTE: can be called for different methods concurrently.
+  public void rewriteMethodReferences(DexEncodedMethod encodedMethod, IRCode code) {
+    if (forwardingMethods.contains(encodedMethod)) {
+      return;
+    }
+
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    while (blocks.hasNext()) {
+      BasicBlock block = blocks.next();
+      InstructionListIterator instructions = block.listIterator();
+      while (instructions.hasNext()) {
+        Instruction instruction = instructions.next();
+
+        if (instruction.isInvokeCustom()) {
+          // Check that static interface methods are not referenced
+          // from invoke-custom instructions via method handles.
+          DexCallSite callSite = instruction.asInvokeCustom().getCallSite();
+          reportStaticInterfaceMethodHandle(callSite.bootstrapMethod);
+          for (DexValue arg : callSite.bootstrapArgs) {
+            if (arg instanceof DexValue.DexValueMethodHandle) {
+              reportStaticInterfaceMethodHandle(((DexValue.DexValueMethodHandle) arg).value);
+            }
+          }
+          continue;
+        }
+
+        if (instruction.isInvokeStatic()) {
+          InvokeStatic invokeStatic = instruction.asInvokeStatic();
+          DexMethod method = invokeStatic.getInvokedMethod();
+          if (isInterfaceClass(method.holder)) {
+            // Retarget call to an appropriate method of companion class.
+            instructions.replaceCurrentInstruction(
+                new InvokeStatic(staticAsMethodOfCompanionClass(method),
+                    invokeStatic.outValue(), invokeStatic.arguments()));
+          }
+          continue;
+        }
+
+        if (instruction.isInvokeSuper()) {
+          InvokeSuper invokeSuper = instruction.asInvokeSuper();
+          DexMethod method = invokeSuper.getInvokedMethod();
+          if (isInterfaceClass(method.holder)) {
+            // Retarget call to an appropriate method of companion class.
+            instructions.replaceCurrentInstruction(
+                new InvokeStatic(defaultAsMethodOfCompanionClass(method),
+                    invokeSuper.outValue(), invokeSuper.arguments()));
+          }
+        }
+      }
+    }
+  }
+
+  private void reportStaticInterfaceMethodHandle(DexMethodHandle handle) {
+    if (handle.type.isInvokeStatic() && isInterfaceClass(handle.asMethod().holder)) {
+      throw new Unimplemented(
+          "Desugaring of static interface method handle in is not yet supported.");
+    }
+  }
+
+  private boolean isInterfaceClass(DexType type) {
+    return findRequiredClass(type).isInterface();
+  }
+
+  // Returns the class for the type specified, report errors for missing classes.
+  final DexClass findRequiredClass(DexType type) {
+    DexClass clazz = converter.appInfo.definitionFor(type);
+    if (clazz != null) {
+      return clazz;
+    }
+    throw new CompilationError("Type '" + type.toSourceString() +
+        "' required for default and static interface methods desugaring not found.");
+  }
+
+  // Gets the companion class for the interface `type`.
+  final DexType getCompanionClassType(DexType type) {
+    assert type.isClassType();
+    String descriptor = type.descriptor.toString();
+    String ccTypeDescriptor = descriptor.substring(0, descriptor.length() - 1)
+        + COMPANION_CLASS_NAME_SUFFIX + ";";
+    return factory.createType(ccTypeDescriptor);
+  }
+
+  private boolean isInMainDexList(DexType iface) {
+    ImmutableSet<DexType> list = converter.application.mainDexList;
+    return list != null && list.contains(iface);
+  }
+
+  // Represent a static interface method as a method of companion class.
+  final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
+    // No changes for static methods.
+    return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
+  }
+
+  // Represent a default interface method as a method of companion class.
+  final DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
+    // Add an implicit argument to represent the receiver.
+    DexType[] params = method.proto.parameters.values;
+    DexType[] newParams = new DexType[params.length + 1];
+    newParams[0] = method.holder;
+    System.arraycopy(params, 0, newParams, 1, params.length);
+
+    // Add prefix to avoid name conflicts.
+    return factory.createMethod(getCompanionClassType(method.holder),
+        factory.createProto(method.proto.returnType, newParams),
+        factory.createString(DEFAULT_METHOD_PREFIX + method.name.toString()));
+  }
+
+  /**
+   * Move static and default interface methods to companion classes,
+   * add missing methods to forward to moved default methods implementation.
+   */
+  public void desugarInterfaceMethods(Builder builder, Flavor flavour) {
+    // Process all classes first. Add missing forwarding methods to
+    // replace desugared default interface methods.
+    forwardingMethods.addAll(processClasses(builder, flavour));
+
+    // Process interfaces, create companion class if needed, move static methods
+    // to companion class, copy default interface methods to companion classes,
+    // make original default methods abstract, remove bridge methods.
+    Map<DexProgramClass, DexProgramClass> companionClasses =
+        processInterfaces(builder, flavour);
+
+    for (Map.Entry<DexProgramClass, DexProgramClass> entry : companionClasses.entrySet()) {
+      // Don't need to optimize synthesized class since all of its methods
+      // are just moved from interfaces and don't need to be re-processed.
+      builder.addSynthesizedClass(entry.getValue(), isInMainDexList(entry.getKey().type));
+    }
+
+    for (DexEncodedMethod method : forwardingMethods) {
+      converter.optimizeSynthesizedMethod(method);
+    }
+  }
+
+  private static boolean shouldProcess(
+      DexClassPromise promise, Flavor flavour, boolean mustBeInterface) {
+    return promise.isProgramClass()
+        && (promise.getOrigin() != DexClass.Origin.Dex || flavour == Flavor.IncludeAllResources)
+        && promise.get().isInterface() == mustBeInterface;
+  }
+
+  private Map<DexProgramClass, DexProgramClass> processInterfaces(
+      Builder builder, Flavor flavour) {
+    InterfaceProcessor processor = new InterfaceProcessor(this);
+    for (DexClassPromise promise : builder.classMap.values()) {
+      if (shouldProcess(promise, flavour, true)) {
+        processor.process(promise.get().asProgramClass());
+      }
+    }
+    return processor.companionClasses;
+  }
+
+  private Set<DexEncodedMethod> processClasses(Builder builder, Flavor flavour) {
+    ClassProcessor processor = new ClassProcessor(this);
+    for (DexClassPromise promise : builder.classMap.values()) {
+      if (shouldProcess(promise, flavour, false)) {
+        processor.process(promise.get());
+      }
+    }
+    return processor.getForwardMethods();
+  }
+
+  final boolean isDefaultMethod(DexEncodedMethod method) {
+    assert !method.accessFlags.isConstructor();
+    assert !method.accessFlags.isStatic();
+
+    if (method.accessFlags.isAbstract()) {
+      return false;
+    }
+    if (method.accessFlags.isNative()) {
+      throw new Unimplemented("Native default interface methods are not yet supported.");
+    }
+    if (!method.accessFlags.isPublic()) {
+      // NOTE: even though the class is allowed to have non-public interface methods
+      // with code, for example private methods, all such methods we are aware of are
+      // created by the compiler for stateful lambdas and they must be converted into
+      // static methods by lambda desugaring by this time.
+      throw new Unimplemented("Non public default interface methods are not yet supported.");
+    }
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
new file mode 100644
index 0000000..772d74c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+// Default and static method interface desugaring processor for interfaces.
+//
+// Makes default interface methods abstract, moves their implementation to
+// a companion class. Removes bridge default methods.
+//
+// Also moves static interface methods into a companion class.
+final class InterfaceProcessor {
+  private final InterfaceMethodRewriter rewriter;
+  // All created companion classes indexed by interface classes.
+  final Map<DexProgramClass, DexProgramClass> companionClasses = new IdentityHashMap<>();
+
+  InterfaceProcessor(InterfaceMethodRewriter rewriter) {
+    this.rewriter = rewriter;
+  }
+
+  void process(DexProgramClass iface) {
+    assert iface.isInterface();
+
+    // The list of methods to be created in companion class.
+    List<DexEncodedMethod> companionMethods = new ArrayList<>();
+
+    // Process virtual interface methods first.
+    List<DexEncodedMethod> remainingMethods = new ArrayList<>();
+    for (DexEncodedMethod virtual : iface.virtualMethods) {
+      if (rewriter.isDefaultMethod(virtual)) {
+        // Create a new method in a companion class to represent default method implementation.
+        DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(virtual.method);
+
+        Code code = virtual.getCode();
+        if (code == null) {
+          throw new CompilationError("Code is missing for default "
+              + "interface method: " + virtual.method.toSourceString());
+        }
+
+        DexAccessFlags newFlags = new DexAccessFlags(virtual.accessFlags.get());
+        newFlags.unsetBridge();
+        newFlags.setStatic();
+        DexCode dexCode = code.asDexCode();
+        // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
+        dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
+        assert (dexCode.getDebugInfo() == null)
+            || (companionMethod.proto.parameters.values.length
+                == dexCode.getDebugInfo().parameters.length);
+
+        companionMethods.add(new DexEncodedMethod(companionMethod,
+            newFlags, virtual.annotations, virtual.parameterAnnotations, code));
+
+        // Make the method abstract.
+        virtual.accessFlags.setAbstract();
+        virtual.removeCode();
+      }
+
+      // Remove bridge methods.
+      if (!virtual.accessFlags.isBridge()) {
+        remainingMethods.add(virtual);
+      }
+    }
+
+    // If at least one bridge methods was removed update the table.
+    if (remainingMethods.size() < iface.virtualMethods.length) {
+      iface.virtualMethods = remainingMethods.toArray(
+          new DexEncodedMethod[remainingMethods.size()]);
+    }
+    remainingMethods.clear();
+
+    // Process static methods, move them into companion class as well.
+    for (DexEncodedMethod direct : iface.directMethods) {
+      if (direct.accessFlags.isPrivate()) {
+        // We only expect to see private methods which are lambda$ methods,
+        // and they are supposed to be relaxed to package private static methods
+        // by this time by lambda rewriter.
+        throw new Unimplemented("Private method are not yet supported.");
+      }
+
+      if (isStaticMethod(direct)) {
+        companionMethods.add(new DexEncodedMethod(
+            rewriter.staticAsMethodOfCompanionClass(direct.method), direct.accessFlags,
+            direct.annotations, direct.parameterAnnotations, direct.getCode()));
+      } else {
+        // Since there are no interface constructors at this point,
+        // this should only be class constructor.
+        assert rewriter.factory.isClassConstructor(direct.method);
+        remainingMethods.add(direct);
+      }
+    }
+    if (remainingMethods.size() < iface.directMethods.length) {
+      iface.directMethods = remainingMethods.toArray(
+          new DexEncodedMethod[remainingMethods.size()]);
+    }
+
+    if (companionMethods.isEmpty()) {
+      return; // No methods to create, companion class not needed.
+    }
+
+    DexAccessFlags companionClassFlags = new DexAccessFlags(iface.accessFlags.get());
+    companionClassFlags.unsetAbstract();
+    companionClassFlags.unsetInterface();
+    companionClassFlags.setFinal();
+    companionClassFlags.setSynthetic();
+
+    // Create companion class.
+    DexType companionClassType = rewriter.getCompanionClassType(iface.type);
+    DexProgramClass companionClass = new DexProgramClass(
+        companionClassType,
+        DexClass.Origin.Synthetic,
+        companionClassFlags,
+        rewriter.factory.objectType,
+        DexTypeList.empty(),
+        iface.sourceFile,
+        DexAnnotationSet.empty(),
+        DexEncodedField.EMPTY_ARRAY,
+        DexEncodedField.EMPTY_ARRAY,
+        companionMethods.toArray(new DexEncodedMethod[companionMethods.size()]),
+        DexEncodedMethod.EMPTY_ARRAY
+    );
+    companionClasses.put(iface, companionClass);
+  }
+
+  private boolean isStaticMethod(DexEncodedMethod method) {
+    if (method.accessFlags.isNative()) {
+      throw new Unimplemented("Native interface methods are not yet supported.");
+    }
+    return method.accessFlags.isStatic() && !rewriter.factory.isClassConstructor(method.method);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
new file mode 100644
index 0000000..d35566a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing synthesized lambda bridge method.
+final class LambdaBridgeMethodSourceCode extends SynthesizedLambdaSourceCode {
+  private final DexMethod mainMethod;
+
+  LambdaBridgeMethodSourceCode(LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod) {
+    super(lambda, bridgeMethod);
+    this.mainMethod = mainMethod;
+  }
+
+  @Override
+  void prepareInstructions() {
+    DexType[] currentParams = proto.parameters.values;
+    DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
+
+    // Prepare call arguments.
+    List<MoveType> argMoveTypes = new ArrayList<>();
+    List<Integer> argRegisters = new ArrayList<>();
+
+    // Always add a receiver representing 'this' of the lambda class.
+    argMoveTypes.add(MoveType.OBJECT);
+    argRegisters.add(getReceiverRegister());
+
+    // Prepare arguments.
+    for (int i = 0; i < currentParams.length; i++) {
+      DexType expectedParamType = enforcedParams[i];
+      argMoveTypes.add(MoveType.fromDexType(expectedParamType));
+      argRegisters.add(enforceParameterType(
+          getParamRegister(i), currentParams[i], expectedParamType));
+    }
+
+    // Method call to the main functional interface method.
+    add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
+        this.mainMethod, this.mainMethod.proto, argMoveTypes, argRegisters));
+
+    // Does the method have return value?
+    if (proto.returnType == factory().voidType) {
+      add(IRBuilder::addReturn);
+    } else {
+      MoveType moveType = MoveType.fromDexType(proto.returnType);
+      int tempValue = nextRegister(moveType);
+      add(builder -> builder.addMoveResult(moveType, tempValue));
+      add(builder -> builder.addReturn(moveType, tempValue));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
new file mode 100644
index 0000000..e92f78f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -0,0 +1,498 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.ir.code.Invoke;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Represents lambda class generated for a lambda descriptor in context
+ * of lambda instantiation point.
+ *
+ * Even though call sites, and thus lambda descriptors, are canonicalized
+ * across the application, the context may require several lambda classes
+ * to be generated for the same lambda descriptor.
+ *
+ * One reason is that we always generate a lambda class in the same package
+ * lambda instantiation point is located in, so if same call site is used in
+ * two classes from different packages (which can happen if same public method
+ * is being references via method reference expression) we generate separate
+ * lambda classes in those packages.
+ *
+ * Another reason is that if we generate an accessor, we generate it in the
+ * class referencing the call site, and thus two such classes will require two
+ * separate lambda classes.
+ */
+final class LambdaClass {
+  final LambdaRewriter rewriter;
+  final DexType type;
+  final LambdaDescriptor descriptor;
+  final DexMethod constructor;
+  final DexMethod classConstructor;
+  final DexField instanceField;
+  final Target target;
+  final AtomicBoolean addToMainDexList = new AtomicBoolean(false);
+
+  LambdaClass(LambdaRewriter rewriter, DexType accessedFrom,
+      DexType lambdaClassType, LambdaDescriptor descriptor) {
+    assert rewriter != null;
+    assert lambdaClassType != null;
+    assert descriptor != null;
+
+    this.rewriter = rewriter;
+    this.type = lambdaClassType;
+    this.descriptor = descriptor;
+
+    DexItemFactory factory = rewriter.factory;
+    DexProto constructorProto = factory.createProto(
+        factory.voidType, descriptor.captures.values);
+    this.constructor = factory.createMethod(
+        lambdaClassType, constructorProto, rewriter.constructorName);
+
+    this.target = createTarget(accessedFrom);
+
+    boolean stateless = isStateless();
+    this.classConstructor = !stateless ? null
+        : factory.createMethod(lambdaClassType, constructorProto, rewriter.classConstructorName);
+    this.instanceField = !stateless ? null
+        : factory.createField(lambdaClassType, lambdaClassType, rewriter.instanceFieldName);
+  }
+
+  // Generate unique lambda class type for lambda descriptor and instantiation point context.
+  static DexType createLambdaClassType(
+      LambdaRewriter rewriter, DexType accessedFrom, LambdaDescriptor match) {
+    StringBuilder lambdaClassDescriptor = new StringBuilder("L");
+
+    // We always create lambda class in the same package where it is referenced.
+    String packageDescriptor = accessedFrom.getPackageDescriptor();
+    if (!packageDescriptor.isEmpty()) {
+      lambdaClassDescriptor.append(packageDescriptor).append('/');
+    }
+
+    // Lambda class name prefix
+    lambdaClassDescriptor.append(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX);
+
+    // If the lambda class should match 1:1 the class it is accessed from, we
+    // just add the name of this type to make lambda class name unique.
+    // It also helps link the class lambda originated from in some cases.
+    if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
+      lambdaClassDescriptor.append(accessedFrom.getName()).append('$');
+    }
+
+    // Add unique lambda descriptor id
+    lambdaClassDescriptor.append(match.uniqueId).append(';');
+    return rewriter.factory.createType(lambdaClassDescriptor.toString());
+  }
+
+  final DexProgramClass synthesizeLambdaClass() {
+    return new DexProgramClass(
+        type,
+        DexClass.Origin.Synthetic,
+        new DexAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC),
+        rewriter.factory.objectType,
+        buildInterfaces(),
+        rewriter.factory.createString("lambda"),
+        DexAnnotationSet.empty(),
+        synthesizeStaticFields(),
+        synthesizeInstanceFields(),
+        synthesizeDirectMethods(),
+        synthesizeVirtualMethods()
+    );
+  }
+
+  final DexField getCaptureField(int index) {
+    return rewriter.factory.createField(this.type,
+        descriptor.captures.values[index], rewriter.factory.createString("f$" + index));
+  }
+
+  final boolean isStateless() {
+    return descriptor.isStateless();
+  }
+
+  // Synthesize virtual methods.
+  private DexEncodedMethod[] synthesizeVirtualMethods() {
+    DexEncodedMethod[] methods = new DexEncodedMethod[1 + descriptor.bridges.size()];
+    int index = 0;
+
+    // Synthesize main method.
+    DexMethod mainMethod = rewriter.factory
+        .createMethod(type, descriptor.erasedProto, descriptor.name);
+    methods[index++] = new DexEncodedMethod(
+        mainMethod,
+        new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL),
+        DexAnnotationSet.empty(),
+        DexAnnotationSetRefList.empty(),
+        new SynthesizedCode(new LambdaMainMethodSourceCode(this, mainMethod)));
+
+    // Synthesize bridge methods.
+    for (DexProto bridgeProto : descriptor.bridges) {
+      DexMethod bridgeMethod = rewriter.factory.createMethod(type, bridgeProto, descriptor.name);
+      methods[index++] = new DexEncodedMethod(
+          bridgeMethod,
+          new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL
+              | Constants.ACC_SYNTHETIC | Constants.ACC_BRIDGE),
+          DexAnnotationSet.empty(),
+          DexAnnotationSetRefList.empty(),
+          new SynthesizedCode(
+              new LambdaBridgeMethodSourceCode(this, mainMethod, bridgeMethod)));
+    }
+    return methods;
+  }
+
+  // Synthesize direct methods.
+  private DexEncodedMethod[] synthesizeDirectMethods() {
+    boolean stateless = isStateless();
+    DexEncodedMethod[] methods = new DexEncodedMethod[stateless ? 2 : 1];
+
+    // Constructor.
+    methods[0] = new DexEncodedMethod(
+        constructor,
+        new DexAccessFlags((stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC) |
+            Constants.ACC_SYNTHETIC | Constants.ACC_CONSTRUCTOR),
+        DexAnnotationSet.empty(),
+        DexAnnotationSetRefList.empty(),
+        new SynthesizedCode(new LambdaConstructorSourceCode(this)));
+
+    // Class constructor for stateless lambda classes.
+    if (stateless) {
+      methods[1] = new DexEncodedMethod(
+          classConstructor,
+          new DexAccessFlags(
+              Constants.ACC_SYNTHETIC | Constants.ACC_CONSTRUCTOR | Constants.ACC_STATIC),
+          DexAnnotationSet.empty(),
+          DexAnnotationSetRefList.empty(),
+          new SynthesizedCode(new LambdaClassConstructorSourceCode(this)));
+    }
+    return methods;
+  }
+
+  // Synthesize instance fields to represent captured values.
+  private DexEncodedField[] synthesizeInstanceFields() {
+    DexType[] fieldTypes = descriptor.captures.values;
+    int fieldCount = fieldTypes.length;
+    DexEncodedField[] fields = new DexEncodedField[fieldCount];
+    for (int i = 0; i < fieldCount; i++) {
+      DexAccessFlags accessFlags = new DexAccessFlags(
+          Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PRIVATE);
+      fields[i] = new DexEncodedField(
+          getCaptureField(i), accessFlags, DexAnnotationSet.empty(), null);
+    }
+    return fields;
+  }
+
+  // Synthesize static fields to represent singleton instance.
+  private DexEncodedField[] synthesizeStaticFields() {
+    if (!isStateless()) {
+      return DexEncodedField.EMPTY_ARRAY;
+    }
+
+    // Create instance field for stateless lambda.
+    assert this.instanceField != null;
+    DexEncodedField[] fields = new DexEncodedField[1];
+    fields[0] = new DexEncodedField(
+        this.instanceField,
+        new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL
+            | Constants.ACC_SYNTHETIC | Constants.ACC_STATIC),
+        DexAnnotationSet.empty(),
+        DexValue.NULL);
+    return fields;
+  }
+
+  // Build a list of implemented interfaces.
+  private DexTypeList buildInterfaces() {
+    List<DexType> interfaces = descriptor.interfaces;
+    return interfaces.isEmpty() ? DexTypeList.empty()
+        : new DexTypeList(interfaces.toArray(new DexType[interfaces.size()]));
+  }
+
+  // Creates a delegation target for this particular lambda class. Note that we
+  // should always be able to create targets for the lambdas we support.
+  private Target createTarget(DexType accessedFrom) {
+    if (descriptor.delegatesToLambdaImplMethod()) {
+      return createLambdaImplMethodTarget(accessedFrom);
+    }
+
+    // Method referenced directly, without lambda$ method.
+    switch (descriptor.implHandle.type) {
+      case INVOKE_SUPER:
+        throw new Unimplemented("Method references to super methods are not yet supported");
+      case INVOKE_INTERFACE:
+        return createInterfaceMethodTarget(accessedFrom);
+      case INVOKE_CONSTRUCTOR:
+        return createConstructorTarget(accessedFrom);
+      case INVOKE_STATIC:
+        return createStaticMethodTarget(accessedFrom);
+      case INVOKE_INSTANCE:
+        return createInstanceMethodTarget(accessedFrom);
+      default:
+        throw new Unreachable("Unexpected method handle type in " + descriptor.implHandle);
+    }
+  }
+
+  private Target createLambdaImplMethodTarget(DexType accessedFrom) {
+    DexMethodHandle implHandle = descriptor.implHandle;
+    assert implHandle != null;
+    DexMethod implMethod = implHandle.asMethod();
+
+    // Lambda$ method. We must always find it.
+    assert implMethod.holder == accessedFrom;
+    assert descriptor.targetFoundInClass(accessedFrom);
+    assert descriptor.getAccessibility() != null;
+    assert descriptor.getAccessibility().isPrivate();
+    assert descriptor.getAccessibility().isSynthetic();
+
+    if (implHandle.type.isInvokeStatic()) {
+      return new StaticLambdaImplTarget();
+    }
+
+    assert implHandle.type.isInvokeInstance();
+
+    // If lambda$ method is an instance method we convert it into a static methods and
+    // relax its accessibility.
+    DexProto implProto = implMethod.proto;
+    DexType[] implParams = implProto.parameters.values;
+    DexType[] newParams = new DexType[implParams.length + 1];
+    newParams[0] = implMethod.holder;
+    System.arraycopy(implParams, 0, newParams, 1, implParams.length);
+
+    DexProto newProto = rewriter.factory.createProto(implProto.returnType, newParams);
+    return new InstanceLambdaImplTarget(
+        rewriter.factory.createMethod(implMethod.holder, newProto, implMethod.name));
+  }
+
+  // Create targets for instance method referenced directly without
+  // lambda$ methods. It may require creation of accessors in some cases.
+  private Target createInstanceMethodTarget(DexType accessedFrom) {
+    assert descriptor.implHandle.type.isInvokeInstance();
+
+    if (!descriptor.needsAccessor(accessedFrom)) {
+      return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL);
+    }
+    // We need to generate an accessor method in `accessedFrom` class/interface
+    // for accessing the original instance impl-method. Note that impl-method's
+    // holder does not have to be the same as `accessedFrom`.
+    DexMethod implMethod = descriptor.implHandle.asMethod();
+    DexProto implProto = implMethod.proto;
+    DexType[] implParams = implProto.parameters.values;
+
+    // The accessor method will be static, package private, and take the
+    // receiver as the first argument. The receiver must be captured and
+    // be the first captured value in case there are more than one.
+    DexType[] accessorParams = new DexType[1 + implParams.length];
+    accessorParams[0] = descriptor.getImplReceiverType();
+    System.arraycopy(implParams, 0, accessorParams, 1, implParams.length);
+    DexProto accessorProto = rewriter.factory.createProto(implProto.returnType, accessorParams);
+    DexMethod accessorMethod = rewriter.factory.createMethod(
+        accessedFrom, accessorProto, generateUniqueLambdaMethodName());
+
+    return new ClassMethodWithAccessorTarget(accessorMethod);
+  }
+
+  // Create targets for static method referenced directly without
+  // lambda$ methods. It may require creation of accessors in some cases.
+  private Target createStaticMethodTarget(DexType accessedFrom) {
+    assert descriptor.implHandle.type.isInvokeStatic();
+
+    if (!descriptor.needsAccessor(accessedFrom)) {
+      return new NoAccessorMethodTarget(Invoke.Type.STATIC);
+    }
+
+    // We need to generate an accessor method in `accessedFrom` class/interface
+    // for accessing the original static impl-method. The accessor method will be
+    // static, package private with exactly same signature and the original method.
+    DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
+        descriptor.implHandle.asMethod().proto, generateUniqueLambdaMethodName());
+    return new ClassMethodWithAccessorTarget(accessorMethod);
+  }
+
+  // Create targets for constructor referenced directly without lambda$ methods.
+  // It may require creation of accessors in some cases.
+  private Target createConstructorTarget(DexType accessedFrom) {
+    DexMethodHandle implHandle = descriptor.implHandle;
+    assert implHandle != null;
+    assert implHandle.type.isInvokeConstructor();
+
+    if (!descriptor.needsAccessor(accessedFrom)) {
+      return new NoAccessorMethodTarget(Invoke.Type.DIRECT);
+    }
+
+    // We need to generate an accessor method in `accessedFrom` class/interface for
+    // instantiating the class and calling constructor on it. The accessor method will
+    // be static, package private with exactly same parameters as the constructor,
+    // and return the newly created instance.
+    DexMethod implMethod = implHandle.asMethod();
+    DexType returnType = implMethod.holder;
+    DexProto accessorProto = rewriter.factory.createProto(
+        returnType, implMethod.proto.parameters.values);
+    DexMethod accessorMethod = rewriter.factory.createMethod(accessedFrom,
+        accessorProto, generateUniqueLambdaMethodName());
+    return new ClassMethodWithAccessorTarget(accessorMethod);
+  }
+
+  // Create targets for interface methods.
+  private Target createInterfaceMethodTarget(DexType accessedFrom) {
+    assert descriptor.implHandle.type.isInvokeInterface();
+    assert !descriptor.needsAccessor(accessedFrom);
+    return new NoAccessorMethodTarget(Invoke.Type.INTERFACE);
+  }
+
+  private DexString generateUniqueLambdaMethodName() {
+    return rewriter.factory.createString(
+        LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX + descriptor.uniqueId);
+  }
+
+  // Represents information about the method lambda class need to delegate the call to. It may
+  // be the same method as specified in lambda descriptor or a newly synthesized accessor.
+  // Also provides action for ensuring accessibility of the referenced symbols.
+  abstract class Target {
+    final DexMethod callTarget;
+    final Invoke.Type invokeType;
+
+    Target(DexMethod callTarget, Invoke.Type invokeType) {
+      assert callTarget != null;
+      assert invokeType != null;
+      this.callTarget = callTarget;
+      this.invokeType = invokeType;
+    }
+
+    // Ensure access of the referenced symbol(s).
+    abstract boolean ensureAccessibility(Builder builder);
+  }
+
+  // Used for targeting methods referenced directly without creating accessors.
+  private final class NoAccessorMethodTarget extends Target {
+    NoAccessorMethodTarget(Invoke.Type invokeType) {
+      super(descriptor.implHandle.asMethod(), invokeType);
+    }
+
+    @Override
+    boolean ensureAccessibility(Builder builder) {
+      return true;
+    }
+  }
+
+  // Used for static private lambda$ methods. Only needs access relaxation.
+  private final class StaticLambdaImplTarget extends Target {
+    StaticLambdaImplTarget() {
+      super(descriptor.implHandle.asMethod(), Invoke.Type.STATIC);
+    }
+
+    @Override
+    boolean ensureAccessibility(Builder builder) {
+      // We already found the static method to be called, just relax its accessibility.
+      assert descriptor.getAccessibility() != null;
+      descriptor.getAccessibility().unsetPrivate();
+      DexClassPromise promise = builder.classMap.get(descriptor.implHandle.asMethod().holder);
+      assert promise != null;
+      DexClass implMethodHolder = promise.get();
+      if (implMethodHolder.isInterface()) {
+        descriptor.getAccessibility().setPublic();
+      }
+      return true;
+    }
+  }
+
+  // Used for instance private lambda$ methods. Needs to be converted to
+  // a package-private static method.
+  private class InstanceLambdaImplTarget extends Target {
+    InstanceLambdaImplTarget(DexMethod staticMethod) {
+      super(staticMethod, Invoke.Type.STATIC);
+    }
+
+    @Override
+    boolean ensureAccessibility(Builder builder) {
+      // For all instantiation points for which compiler creates lambda$
+      // methods, it creates these methods in the same class/interface.
+      DexMethod implMethod = descriptor.implHandle.asMethod();
+      DexClassPromise promise = builder.classMap.get(implMethod.holder);
+      assert promise != null;
+      DexClass implMethodHolder = promise.get();
+
+      DexEncodedMethod[] directMethods = implMethodHolder.directMethods;
+      for (int i = 0; i < directMethods.length; i++) {
+        DexEncodedMethod encodedMethod = directMethods[i];
+        if (implMethod.match(encodedMethod)) {
+          // We need to create a new static method with the same code to be able to safely
+          // relax its accessibility without making it virtual.
+          DexEncodedMethod newMethod = new DexEncodedMethod(
+              callTarget, encodedMethod.accessFlags, encodedMethod.annotations,
+              encodedMethod.parameterAnnotations, encodedMethod.getCode());
+          // TODO(ager): Should we give the new first parameter an actual name? Maybe 'this'?
+          encodedMethod.accessFlags.setStatic();
+          encodedMethod.accessFlags.unsetPrivate();
+          if (implMethodHolder.isInterface()) {
+            encodedMethod.accessFlags.setPublic();
+          }
+          DexCode dexCode = newMethod.getCode().asDexCode();
+          dexCode.setDebugInfo(dexCode.debugInfoWithAdditionalFirstParameter(null));
+          assert (dexCode.getDebugInfo() == null)
+              || (callTarget.proto.parameters.values.length
+                  == dexCode.getDebugInfo().parameters.length);
+          directMethods[i] = newMethod;
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  // Used for instance/static methods or constructors accessed via
+  // synthesized accessor method. Needs accessor method to be created.
+  private class ClassMethodWithAccessorTarget extends Target {
+    ClassMethodWithAccessorTarget(DexMethod accessorMethod) {
+      super(accessorMethod, Invoke.Type.STATIC);
+    }
+
+    @Override
+    boolean ensureAccessibility(Builder builder) {
+      // Create a static accessor with proper accessibility.
+      DexClassPromise promise = builder.classMap.get(callTarget.holder);
+      assert promise != null && promise.isProgramClass();
+      DexClass accessorClass = promise.get();
+
+      DexAccessFlags accessorFlags = new DexAccessFlags(
+          Constants.ACC_SYNTHETIC | Constants.ACC_STATIC |
+              (accessorClass.isInterface() ? Constants.ACC_PUBLIC : 0));
+      DexEncodedMethod accessorEncodedMethod = new DexEncodedMethod(
+          callTarget, accessorFlags, DexAnnotationSet.empty(), DexAnnotationSetRefList.empty(),
+          new SynthesizedCode(new AccessorMethodSourceCode(LambdaClass.this)));
+      accessorClass.directMethods = appendMethod(
+          accessorClass.directMethods, accessorEncodedMethod);
+      rewriter.converter.optimizeSynthesizedMethod(accessorEncodedMethod);
+      return true;
+    }
+
+    private DexEncodedMethod[] appendMethod(DexEncodedMethod[] methods, DexEncodedMethod method) {
+      int size = methods.length;
+      DexEncodedMethod[] newMethods = new DexEncodedMethod[size + 1];
+      System.arraycopy(methods, 0, newMethods, 0, size);
+      newMethods[size] = method;
+      return newMethods;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
new file mode 100644
index 0000000..db8e1f5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.Collections;
+
+// Source code representing synthesized lambda class constructor.
+// Used for stateless lambdas to instantiate singleton instance.
+final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode {
+  LambdaClassConstructorSourceCode(LambdaClass lambda) {
+    super(null /* Class initializer is static */, lambda, lambda.classConstructor);
+    assert lambda.instanceField != null;
+  }
+
+  @Override
+  void prepareInstructions() {
+    // Create and initialize an instance.
+    int instance = nextRegister(MoveType.OBJECT);
+    add(builder -> builder.addNewInstance(instance, lambda.type));
+    add(builder -> builder.addInvoke(
+        Invoke.Type.DIRECT, lambda.constructor, lambda.constructor.proto,
+        Collections.singletonList(MoveType.OBJECT), Collections.singletonList(instance)));
+
+    // Assign to a field.
+    add(builder -> builder.addStaticPut(MemberType.OBJECT, instance, lambda.instanceField));
+
+    // Final return.
+    add(IRBuilder::addReturn);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
new file mode 100644
index 0000000..b4aef78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import java.util.Collections;
+
+// Source code representing synthesized lambda constructor.
+final class LambdaConstructorSourceCode extends SynthesizedLambdaSourceCode {
+  LambdaConstructorSourceCode(LambdaClass lambda) {
+    super(lambda, lambda.constructor);
+  }
+
+  @Override
+  void prepareInstructions() {
+    // Super constructor call (always java.lang.Object.<init>()).
+    DexMethod objectInitMethod = lambda.rewriter.objectInitMethod;
+    add(builder -> builder.addInvoke(Invoke.Type.DIRECT, objectInitMethod,
+        objectInitMethod.proto, Collections.singletonList(getReceiverValue())));
+
+    // Assign capture fields.
+    DexType[] capturedTypes = captures();
+    int capturedValues = capturedTypes.length;
+    if (capturedValues > 0) {
+      for (int i = 0; i < capturedValues; i++) {
+        MemberType memberType = MemberType.fromDexType(capturedTypes[i]);
+        DexField field = lambda.getCaptureField(i);
+        int idx = i;
+        add(builder -> builder.addInstancePut(memberType,
+            getParamRegister(idx), getReceiverRegister(), field));
+      }
+    }
+
+    // Final return.
+    add(IRBuilder::addReturn);
+  }
+
+  @Override
+  public int hashCode() {
+    // We want all zero-parameter constructor source code instances to
+    // be treated as equal, since it only has one call to super constructor,
+    // which is always java.lang.Object.<init>().
+    return captures().length == 0
+        ? System.identityHashCode(lambda.rewriter.objectInitMethod) : super.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (!(obj instanceof LambdaConstructorSourceCode)) {
+      return false;
+    }
+    if (captures().length == 0) {
+      // See comment in hashCode().
+      return ((LambdaConstructorSourceCode) obj).captures().length == 0;
+    }
+    return super.equals(obj);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
new file mode 100644
index 0000000..4a51135
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -0,0 +1,371 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DexValue;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+// Represents the lambda descriptor inferred from calls site.
+final class LambdaDescriptor {
+  private static final int LAMBDA_ALT_SERIALIZABLE = 1;
+  private static final int LAMBDA_ALT_HAS_EXTRA_INTERFACES = 2;
+  private static final int LAMBDA_ALT_HAS_BRIDGES = 4;
+  private static final int LAMBDA_ALT_MASK = LAMBDA_ALT_SERIALIZABLE
+      | LAMBDA_ALT_HAS_EXTRA_INTERFACES | LAMBDA_ALT_HAS_BRIDGES;
+
+  static final LambdaDescriptor MATCH_FAILED = new LambdaDescriptor();
+
+  final String uniqueId;
+  final DexString name;
+  final DexProto erasedProto;
+  final DexProto enforcedProto;
+  final DexMethodHandle implHandle;
+
+  final List<DexType> interfaces = new ArrayList<>();
+  final Set<DexProto> bridges = Sets.newIdentityHashSet();
+  final DexTypeList captures;
+
+  // Used for accessibility analysis and few assertions only.
+  private final DexEncodedMethod targetMethod;
+
+  private LambdaDescriptor() {
+    uniqueId = null;
+    name = null;
+    erasedProto = null;
+    enforcedProto = null;
+    implHandle = null;
+    captures = null;
+    targetMethod = null;
+  }
+
+  private LambdaDescriptor(LambdaRewriter rewriter, DexCallSite callSite,
+      DexString name, DexProto erasedProto, DexProto enforcedProto,
+      DexMethodHandle implHandle, DexType mainInterface, DexTypeList captures) {
+    assert rewriter != null;
+    assert callSite != null;
+    assert name != null;
+    assert erasedProto != null;
+    assert enforcedProto != null;
+    assert implHandle != null;
+    assert mainInterface != null;
+    assert captures != null;
+
+    this.uniqueId = callSite.getHash();
+    this.name = name;
+    this.erasedProto = erasedProto;
+    this.enforcedProto = enforcedProto;
+    this.implHandle = implHandle;
+    this.captures = captures;
+
+    this.interfaces.add(mainInterface);
+    this.targetMethod = lookupTargetMethod(rewriter);
+  }
+
+  final DexType getImplReceiverType() {
+    // The receiver of instance impl-method is captured as the first captured
+    // value or should be the first argument of the enforced method signature.
+    DexType[] params = enforcedProto.parameters.values;
+    DexType[] captures = this.captures.values;
+    assert captures.length > 0 || params.length > 0;
+    return captures.length > 0 ? captures[0] : params[0];
+  }
+
+  private DexEncodedMethod lookupTargetMethod(LambdaRewriter rewriter) {
+    // Find the lambda's impl-method target.
+    DexMethod method = implHandle.asMethod();
+    switch (implHandle.type) {
+      case INVOKE_INSTANCE: {
+        AppInfo appInfo = rewriter.converter.appInfo;
+        DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method);
+        if (target == null) {
+          target = appInfo.lookupDirectTarget(method);
+        }
+        assert target == null ||
+            (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic());
+        return target;
+      }
+
+      case INVOKE_STATIC: {
+        AppInfo appInfo = rewriter.converter.appInfo;
+        DexEncodedMethod target = appInfo.lookupStaticTarget(method);
+        assert target == null || target.accessFlags.isStatic();
+        return target;
+      }
+
+      case INVOKE_CONSTRUCTOR: {
+        AppInfo appInfo = rewriter.converter.appInfo;
+        DexEncodedMethod target = appInfo.lookupDirectTarget(method);
+        assert target == null || target.accessFlags.isConstructor();
+        return target;
+      }
+
+      case INVOKE_INTERFACE: {
+        AppInfo appInfo = rewriter.converter.appInfo;
+        DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method);
+        assert target == null ||
+            (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic());
+        return target;
+      }
+
+      default:
+        throw new Unreachable("Unexpected method handle kind in " + implHandle);
+    }
+  }
+
+  final DexAccessFlags getAccessibility() {
+    return targetMethod == null ? null : targetMethod.accessFlags;
+  }
+
+  final boolean targetFoundInClass(DexType type) {
+    return targetMethod != null && targetMethod.method.holder == type;
+  }
+
+  /** If the lambda delegates to lambda$ method. */
+  boolean delegatesToLambdaImplMethod() {
+    DexString methodName = implHandle.asMethod().name;
+    return methodName.toString().startsWith(LambdaRewriter.EXPECTED_LAMBDA_METHOD_PREFIX);
+  }
+
+  /** Is a stateless lambda, i.e. lambda does not capture any values */
+  final boolean isStateless() {
+    return captures.isEmpty();
+  }
+
+  /** Checks if call site needs a accessor when referenced from `accessedFrom`. */
+  boolean needsAccessor(DexType accessedFrom) {
+    if (delegatesToLambdaImplMethod()) {
+      return false;
+    }
+
+    if (implHandle.type.isInvokeInterface()) {
+      // Interface methods must be public.
+      return false;
+    }
+
+    boolean staticTarget = implHandle.type.isInvokeStatic();
+    boolean instanceTarget = implHandle.type.isInvokeInstance();
+    boolean initTarget = implHandle.type.isInvokeConstructor();
+    assert instanceTarget || staticTarget | initTarget;
+
+    if (targetMethod == null) {
+      // The target cannot be a private method, since otherwise it
+      // should have been found.
+
+      if (staticTarget || initTarget) {
+        // Create accessor only in case it is accessed from other
+        // package, since otherwise it can be called directly.
+        // NOTE: This case is different from regular instance method case
+        // because the method being called must be present in method holder,
+        // and not in one from its supertypes.
+        boolean accessedFromSamePackage =
+            accessedFrom.getPackageDescriptor().equals(
+                implHandle.asMethod().holder.getPackageDescriptor());
+        return !accessedFromSamePackage;
+      }
+
+      // Since instance method was not found, always generate an accessor
+      // since it may be a protected method located in another package.
+      return true;
+    }
+
+    DexAccessFlags flags = targetMethod.accessFlags;
+
+    // Private methods always need accessors.
+    if (flags.isPrivate()) {
+      return true;
+    }
+    if (flags.isPublic()) {
+      return false;
+    }
+
+    boolean accessedFromSamePackage =
+        accessedFrom.getPackageDescriptor().equals(
+            targetMethod.method.holder.getPackageDescriptor());
+    assert flags.isProtected() || accessedFromSamePackage;
+    return flags.isProtected() && !accessedFromSamePackage;
+  }
+
+  /**
+   * Matches call site for lambda metafactory invocation pattern and
+   * returns extracted match information, or null if match failed.
+   */
+  static LambdaDescriptor infer(LambdaRewriter rewriter, DexCallSite callSite) {
+    // We expect bootstrap method to be either `metafactory` or `altMetafactory` method
+    // of `java.lang.invoke.LambdaMetafactory` class. Both methods are static.
+    if (!callSite.bootstrapMethod.type.isInvokeStatic()) {
+      return LambdaDescriptor.MATCH_FAILED;
+    }
+
+    DexMethod bootstrapMethod = callSite.bootstrapMethod.asMethod();
+    boolean isMetafactoryMethod = bootstrapMethod == rewriter.metafactoryMethod;
+    boolean isAltMetafactoryMethod = bootstrapMethod == rewriter.metafactoryAltMethod;
+    if (!isMetafactoryMethod && !isAltMetafactoryMethod) {
+      // It is not a lambda, thus no need to manage this call site.
+      return LambdaDescriptor.MATCH_FAILED;
+    }
+
+    // 'Method name' operand of the invoke-custom instruction represents
+    // the name of the functional interface main method.
+    DexString funcMethodName = callSite.methodName;
+
+    // Signature of main functional interface method.
+    DexValue.DexValueMethodType funcErasedSignature =
+        getBootstrapArgument(callSite, 0, DexValue.DexValueMethodType.class);
+
+    // Method handle of the implementation method.
+    DexMethodHandle lambdaImplMethodHandle =
+        getBootstrapArgument(callSite, 1, DexValue.DexValueMethodHandle.class).value;
+    // Even though there are some limitations on which method handle kinds are
+    // allowed for lambda impl-methods, there is no way to detect unsupported
+    // handle kinds after they are transformed into DEX method handle.
+
+    // Signature to be enforced on main method.
+    DexValue.DexValueMethodType funcEnforcedSignature =
+        getBootstrapArgument(callSite, 2, DexValue.DexValueMethodType.class);
+    if (!isEnforcedSignatureValid(
+        rewriter, funcEnforcedSignature.value, funcErasedSignature.value)) {
+      throw new Unreachable(
+          "Enforced and erased signatures are inconsistent in " + callSite.toString());
+    }
+
+    // 'Method type' of the invoke-custom instruction represents the signature
+    // of the lambda method factory.
+    DexProto lambdaFactoryProto = callSite.methodProto;
+    // Main functional interface is the return type of the lambda factory method.
+    DexType mainFuncInterface = lambdaFactoryProto.returnType;
+    // Lambda captures are represented as parameters of the lambda factory method.
+    DexTypeList captures = lambdaFactoryProto.parameters;
+
+    // Create a match.
+    LambdaDescriptor match = new LambdaDescriptor(rewriter, callSite,
+        funcMethodName, funcErasedSignature.value, funcEnforcedSignature.value,
+        lambdaImplMethodHandle, mainFuncInterface, captures);
+
+    if (isMetafactoryMethod) {
+      if (callSite.bootstrapArgs.size() != 3) {
+        throw new Unreachable(
+            "Unexpected number of metafactory method arguments in " + callSite.toString());
+      }
+    } else {
+      extractExtraLambdaInfo(rewriter, callSite, match);
+    }
+
+    return match;
+  }
+
+  private static void extractExtraLambdaInfo(
+      LambdaRewriter rewriter, DexCallSite callSite, LambdaDescriptor match) {
+    int argIndex = 3;
+    int flagsArg = getBootstrapArgument(
+        callSite, argIndex++, DexValue.DexValueInt.class).value;
+    assert (flagsArg & ~LAMBDA_ALT_MASK) == 0;
+
+    // Load extra interfaces if any.
+    if ((flagsArg & LAMBDA_ALT_HAS_EXTRA_INTERFACES) != 0) {
+      int count = getBootstrapArgument(
+          callSite, argIndex++, DexValue.DexValueInt.class).value;
+      for (int i = 0; i < count; i++) {
+        DexType type = getBootstrapArgument(
+            callSite, argIndex++, DexValue.DexValueType.class).value;
+        if (!match.interfaces.contains(type)) {
+          match.interfaces.add(type);
+        }
+      }
+    }
+
+    // If the lambda is serializable, add it.
+    if ((flagsArg & LAMBDA_ALT_SERIALIZABLE) != 0) {
+      if (!match.interfaces.contains(rewriter.serializableType)) {
+        match.interfaces.add(rewriter.serializableType);
+      }
+    }
+
+    // Load bridges if any.
+    if ((flagsArg & LAMBDA_ALT_HAS_BRIDGES) != 0) {
+      int count = getBootstrapArgument(
+          callSite, argIndex++, DexValue.DexValueInt.class).value;
+      for (int i = 0; i < count; i++) {
+        DexProto bridgeProto = getBootstrapArgument(
+            callSite, argIndex++, DexValue.DexValueMethodType.class).value;
+        match.bridges.add(bridgeProto);
+      }
+    }
+
+    if (callSite.bootstrapArgs.size() != argIndex) {
+      throw new Unreachable(
+          "Unexpected number of metafactory method arguments in " + callSite.toString());
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <T> T getBootstrapArgument(DexCallSite callSite, int i, Class<T> clazz) {
+    List<DexValue> bootstrapArgs = callSite.bootstrapArgs;
+    if (bootstrapArgs.size() < i) {
+      throw new Unreachable("Expected to find at least "
+          + i + " bootstrap arguments in " + callSite.toString());
+    }
+    DexValue value = bootstrapArgs.get(i);
+    if (!clazz.isAssignableFrom(value.getClass())) {
+      throw new Unreachable("Unexpected type of "
+          + "bootstrap arguments #" + i + " in " + callSite.toString());
+    }
+    return (T) value;
+  }
+
+  private static boolean isEnforcedSignatureValid(
+      LambdaRewriter rewriter, DexProto enforced, DexProto erased) {
+    if (!isSameOrDerived(rewriter.factory, enforced.returnType, erased.returnType)) {
+      return false;
+    }
+    DexType[] enforcedValues = enforced.parameters.values;
+    DexType[] erasedValues = erased.parameters.values;
+    int count = enforcedValues.length;
+    if (count != erasedValues.length) {
+      return false;
+    }
+    for (int i = 0; i < count; i++) {
+      if (!isSameOrDerived(rewriter.factory, enforcedValues[i], erasedValues[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // Checks if the types are the same OR both types are reference types and
+  // `subType` is derived from `b`. Note that in the latter case we only check if
+  // both types are class types, for the reasons mentioned in isSameOrAdaptableTo(...).
+  static boolean isSameOrDerived(
+      DexItemFactory factory, DexType subType, DexType superType) {
+    if (subType == superType || (subType.isClassType() && superType.isClassType())) {
+      return true;
+    }
+
+    if (subType.isArrayType()) {
+      if (superType.isArrayType()) {
+        // X[] -> Y[].
+        return isSameOrDerived(factory,
+            subType.toArrayElementType(factory), superType.toArrayElementType(factory));
+      }
+      return superType == factory.objectType; // T[] -> Object.
+    }
+
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
new file mode 100644
index 0000000..9e4c570
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -0,0 +1,478 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.EMPTY_TYPE_ARRAY;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+// Source code representing synthesized lambda main method
+final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
+  LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod) {
+    super(lambda, mainMethod);
+  }
+
+  private boolean checkSignatures(
+      DexType[] captures, DexType[] enforcedParams, DexType enforcedReturnType,
+      List<DexType> implReceiverAndArgs, DexType implReturnType) {
+    List<DexType> capturesAndParams = new ArrayList<>();
+    capturesAndParams.addAll(Lists.newArrayList(captures));
+    capturesAndParams.addAll(Lists.newArrayList(enforcedParams));
+
+    int size = capturesAndParams.size();
+    if (size != implReceiverAndArgs.size()) {
+      return false;
+    }
+
+    for (int i = 0; i < size; i++) {
+      if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i))) {
+        return false;
+      }
+    }
+
+    if (!enforcedReturnType.isVoidType()
+        && !isSameOrAdaptableTo(implReturnType, enforcedReturnType)) {
+      return false;
+    }
+    return true;
+  }
+
+  private DexType getPrimitiveFromBoxed(DexType boxedPrimitive) {
+    DexString descriptor = boxedPrimitive.descriptor;
+    DexItemFactory factory = factory();
+    if (descriptor == factory.boxedBooleanDescriptor) {
+      return factory.booleanType;
+    }
+    if (descriptor == factory.boxedByteDescriptor) {
+      return factory.byteType;
+    }
+    if (descriptor == factory.boxedCharDescriptor) {
+      return factory.charType;
+    }
+    if (descriptor == factory.boxedShortDescriptor) {
+      return factory.shortType;
+    }
+    if (descriptor == factory.boxedIntDescriptor) {
+      return factory.intType;
+    }
+    if (descriptor == factory.boxedLongDescriptor) {
+      return factory.longType;
+    }
+    if (descriptor == factory.boxedFloatDescriptor) {
+      return factory.floatType;
+    }
+    if (descriptor == factory.boxedDoubleDescriptor) {
+      return factory.doubleType;
+    }
+    return null;
+  }
+
+  private DexType getBoxedForPrimitiveType(DexType primitive) {
+    switch (primitive.descriptor.content[0]) {
+      case 'Z':  // byte
+        return factory().boxedBooleanType;
+      case 'B':  // byte
+        return factory().boxedByteType;
+      case 'S':  // short
+        return factory().boxedShortType;
+      case 'C':  // char
+        return factory().boxedCharType;
+      case 'I':  // int
+        return factory().boxedIntType;
+      case 'J':  // long
+        return factory().boxedLongType;
+      case 'F':  // float
+        return factory().boxedFloatType;
+      case 'D':  // double
+        return factory().boxedDoubleType;
+      default:
+        throw new Unreachable("Invalid primitive type descriptor: " + primitive);
+    }
+  }
+
+  // Checks if the types are the same OR type `a` is adaptable to type `b`.
+  private boolean isSameOrAdaptableTo(DexType a, DexType b) {
+    if (a == b) {
+      return true;
+    }
+
+    DexItemFactory factory = factory();
+    if (a.isArrayType()) {
+      // Arrays are only adaptable to java.lang.Object.
+      return b == factory.objectType;
+    }
+
+    if (a.isPrimitiveType()) {
+      if (b.isPrimitiveType()) {
+        return isSameOrAdaptableTo(a.descriptor.content[0], b.descriptor.content[0]);
+      }
+
+      // `a` is primitive and `b` is a supertype of the boxed type `a`.
+      DexType boxedPrimitiveType = getBoxedForPrimitiveType(a);
+      if (b == boxedPrimitiveType || b == factory.objectType) {
+        return true;
+      }
+      return boxedPrimitiveType != factory.boxedCharType
+          && boxedPrimitiveType != factory.boxedBooleanType
+          && b.descriptor == factory.boxedNumberDescriptor;
+    }
+
+    if (b.isPrimitiveType()) {
+      // `a` is a boxed type for `a*` which can be
+      // widened to primitive type `b`.
+      DexType unboxedA = getPrimitiveFromBoxed(a);
+      return unboxedA != null &&
+          isSameOrAdaptableTo(unboxedA.descriptor.content[0], b.descriptor.content[0]);
+    }
+
+    // Otherwise `a` should be a reference type derived from `b`.
+    // NOTE: we don't check `b` for being actually a supertype, since we
+    // might not have full classpath with inheritance information to do that.
+    // We assume this requirement stands and will be caught by cast
+    // instruction anyways in most cases.
+    return a.isClassType() && b.isClassType();
+  }
+
+  // For two primitive types `a` is adjustable to `b` iff `a` is the same as `b`
+  // or can be converted to `b` via a primitive widening conversion.
+  private boolean isSameOrAdaptableTo(byte from, byte to) {
+    if (from == to) {
+      return true;
+    }
+    switch (from) {
+      case 'B':  // byte
+        return to == 'S' || to == 'I' || to == 'J' || to == 'F' || to == 'D';
+      case 'S':  // short
+      case 'C':  // char
+        return to == 'I' || to == 'J' || to == 'F' || to == 'D';
+      case 'I':  // int
+        return to == 'J' || to == 'F' || to == 'D';
+      case 'J':  // long
+        return to == 'F' || to == 'D';
+      case 'F':  // float
+        return to == 'D';
+      case 'Z':  // boolean
+      case 'D':  // double
+        return false;
+      default:
+        throw new Unreachable("Invalid primitive type descriptor: " + from);
+    }
+  }
+
+  @Override
+  void prepareInstructions() {
+    DexType[] capturedTypes = captures();
+    DexType[] erasedParams = descriptor().erasedProto.parameters.values;
+    DexType erasedReturnType = descriptor().erasedProto.returnType;
+    DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
+    DexType enforcedReturnType = descriptor().enforcedProto.returnType;
+
+    LambdaClass.Target target = lambda.target;
+    DexMethod methodToCall = target.callTarget;
+
+    // Only constructor call should use direct invoke type since super
+    // and private methods require accessor methods.
+    boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
+    assert !constructorTarget || methodToCall.name == factory().constructorMethodName;
+
+    List<DexType> implReceiverAndArgs = new ArrayList<>();
+    if (target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE) {
+      implReceiverAndArgs.add(methodToCall.holder);
+    }
+    implReceiverAndArgs.addAll(Lists.newArrayList(methodToCall.proto.parameters.values));
+    DexType implReturnType = methodToCall.proto.returnType;
+
+    assert target.invokeType == Invoke.Type.STATIC
+        || target.invokeType == Invoke.Type.VIRTUAL
+        || target.invokeType == Invoke.Type.DIRECT
+        || target.invokeType == Invoke.Type.INTERFACE;
+    assert checkSignatures(capturedTypes, enforcedParams,
+        enforcedReturnType, implReceiverAndArgs,
+        constructorTarget ? target.callTarget.holder : implReturnType);
+
+    // Prepare call arguments.
+    List<MoveType> argMoveTypes = new ArrayList<>();
+    List<Integer> argRegisters = new ArrayList<>();
+
+    // If the target is a constructor, we need to create the instance first.
+    // This instance will be the first argument to the call.
+    if (constructorTarget) {
+      int instance = nextRegister(MoveType.OBJECT);
+      add(builder -> builder.addNewInstance(instance, methodToCall.holder));
+      argMoveTypes.add(MoveType.OBJECT);
+      argRegisters.add(instance);
+    }
+
+    // Load captures if needed.
+    int capturedValues = capturedTypes.length;
+    for (int i = 0; i < capturedValues; i++) {
+      MemberType memberType = MemberType.fromDexType(capturedTypes[i]);
+      MoveType moveType = MemberType.moveTypeFor(memberType);
+      int register = nextRegister(moveType);
+
+      argMoveTypes.add(moveType);
+      argRegisters.add(register);
+
+      // Read field into tmp local.
+      DexField field = lambda.getCaptureField(i);
+      add(builder -> builder.addInstanceGet(
+          memberType, register, getReceiverRegister(), field));
+    }
+
+    // Prepare arguments.
+    for (int i = 0; i < erasedParams.length; i++) {
+      DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues);
+      argMoveTypes.add(MoveType.fromDexType(expectedParamType));
+      argRegisters.add(prepareParameterValue(
+          getParamRegister(i), erasedParams[i], enforcedParams[i], expectedParamType));
+    }
+
+    // Method call to the method implementing lambda or method-ref.
+    add(builder -> builder.addInvoke(target.invokeType,
+        methodToCall, methodToCall.proto, argMoveTypes, argRegisters));
+
+    // Does the method have return value?
+    if (enforcedReturnType.isVoidType()) {
+      add(IRBuilder::addReturn);
+    } else if (constructorTarget) {
+      // Return newly created instance
+      int instanceRegister = argRegisters.get(0);
+      int adjustedValue = prepareReturnValue(instanceRegister,
+          erasedReturnType, enforcedReturnType, methodToCall.holder);
+      add(builder -> builder.addReturn(
+          MoveType.fromDexType(erasedReturnType), adjustedValue));
+    } else {
+      MoveType implMoveType = MoveType.fromDexType(implReturnType);
+      int tempValue = nextRegister(implMoveType);
+      add(builder -> builder.addMoveResult(implMoveType, tempValue));
+      int adjustedValue = prepareReturnValue(tempValue,
+          erasedReturnType, enforcedReturnType, methodToCall.proto.returnType);
+      MoveType adjustedMoveType = MoveType.fromDexType(erasedReturnType);
+      add(builder -> builder.addReturn(adjustedMoveType, adjustedValue));
+    }
+  }
+
+  // Adds necessary casts and transformations to adjust the value
+  // returned by impl-method to expected return type of the method.
+  private int prepareReturnValue(int register,
+      DexType erasedType, DexType enforcedType, DexType actualType) {
+    // `actualType` must be adjusted to `enforcedType` first.
+    register = adjustType(register, actualType, enforcedType);
+
+    // `erasedType` and `enforcedType` may only differ when they both
+    // are class types and `erasedType` is a base type of `enforcedType`,
+    // so no transformation is actually needed.
+    assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, erasedType);
+    return register;
+  }
+
+  // Adds necessary casts and transformations to adjust parameter
+  // value to the expected type of method-impl argument.
+  //
+  // Note that the original parameter type (`erasedType`) may need to
+  // be converted to enforced parameter type (`enforcedType`), which,
+  // in its turn, may need to be adjusted to the parameter type of
+  // the impl-method (`expectedType`).
+  private int prepareParameterValue(int register,
+      DexType erasedType, DexType enforcedType, DexType expectedType) {
+    register = enforceParameterType(register, erasedType, enforcedType);
+    register = adjustType(register, enforcedType, expectedType);
+    return register;
+  }
+
+  private int adjustType(int register, DexType fromType, DexType toType) {
+    if (fromType == toType) {
+      return register;
+    }
+
+    boolean fromTypePrimitive = fromType.isPrimitiveType();
+    boolean toTypePrimitive = toType.isPrimitiveType();
+
+    // If both are primitive they must be convertible via primitive widening conversion.
+    if (fromTypePrimitive && toTypePrimitive) {
+      return addPrimitiveWideningConversion(register, fromType, toType);
+    }
+
+    // If the first one is a boxed primitive type and the second one is a primitive
+    // type, the value must be unboxed and converted to the resulting type via primitive
+    // widening conversion.
+    if (toTypePrimitive) {
+      DexType fromTypeAsPrimitive = getPrimitiveFromBoxed(fromType);
+      if (fromTypeAsPrimitive != null) {
+        int unboxedRegister = addPrimitiveUnboxing(register, fromTypeAsPrimitive, fromType);
+        return addPrimitiveWideningConversion(unboxedRegister, fromTypeAsPrimitive, toType);
+      }
+    }
+
+    // If the first one is a primitive type and the second one is a boxed
+    // type for this primitive type, just box the value.
+    if (fromTypePrimitive) {
+      DexType boxedFromType = getBoxedForPrimitiveType(fromType);
+      if (toType == boxedFromType ||
+          toType == factory().objectType ||
+          (boxedFromType != factory().booleanType &&
+              boxedFromType != factory().charType &&
+              toType == factory().boxedNumberType)) {
+        return addPrimitiveBoxing(register, fromType, boxedFromType);
+      }
+    }
+
+    if (fromType.isArrayType() && toType == factory().objectType) {
+      // If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
+      return register;
+    }
+
+    if (fromType.isClassType() && toType.isClassType()) {
+      // If `fromType` and `toType` are both reference types, `fromType` must
+      // be deriving from `toType`.
+      // NOTE: we don't check `toType` for being actually a supertype, since we
+      // might not have full classpath with inheritance information to do that.
+      return register;
+    }
+
+    throw new Unreachable("Unexpected type adjustment from "
+        + fromType.toSourceString() + " to " + toType);
+  }
+
+  private int addPrimitiveWideningConversion(int register, DexType fromType, DexType toType) {
+    assert fromType.isPrimitiveType() && toType.isPrimitiveType();
+    if (fromType == toType) {
+      return register;
+    }
+
+    NumericType from = NumericType.fromDexType(fromType);
+    NumericType to = NumericType.fromDexType(toType);
+
+    if (from != null && to != null) {
+      assert from != to;
+
+      switch (to) {
+        case SHORT: {
+          if (from != NumericType.BYTE) {
+            break; // Only BYTE can be converted to SHORT via widening conversion.
+          }
+          int result = nextRegister(MoveType.SINGLE);
+          add(builder -> builder.addConversion(to, NumericType.INT, result, register));
+          return result;
+        }
+
+        case INT:
+          if (from == NumericType.BYTE || from == NumericType.CHAR || from == NumericType.SHORT) {
+            return register; // No actual conversion is needed.
+          }
+          break;
+
+        case LONG: {
+          if (from == NumericType.FLOAT || from == NumericType.DOUBLE) {
+            break; // Not a widening conversion.
+          }
+          int result = nextRegister(MoveType.WIDE);
+          add(builder -> builder.addConversion(to, NumericType.INT, result, register));
+          return result;
+        }
+
+        case FLOAT: {
+          if (from == NumericType.DOUBLE) {
+            break; // Not a widening conversion.
+          }
+          int result = nextRegister(MoveType.SINGLE);
+          NumericType type = (from == NumericType.LONG) ? NumericType.LONG : NumericType.INT;
+          add(builder -> builder.addConversion(to, type, result, register));
+          return result;
+        }
+
+        case DOUBLE: {
+          int result = nextRegister(MoveType.WIDE);
+          NumericType type = (from == NumericType.FLOAT || from == NumericType.LONG)
+              ? from : NumericType.INT;
+          add(builder -> builder.addConversion(to, type, result, register));
+          return result;
+        }
+      }
+    }
+
+    throw new Unreachable("Type " + fromType.toSourceString() + " cannot be " +
+        "converted to " + toType.toSourceString() + " via primitive widening conversion.");
+  }
+
+  private DexMethod getUnboxMethod(byte primitive, DexType boxType) {
+    DexItemFactory factory = factory();
+    DexProto proto;
+    switch (primitive) {
+      case 'Z':  // byte
+        proto = factory.createProto(factory.booleanType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
+      case 'B':  // byte
+        proto = factory.createProto(factory.byteType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
+      case 'S':  // short
+        proto = factory.createProto(factory.shortType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
+      case 'C':  // char
+        proto = factory.createProto(factory.charType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
+      case 'I':  // int
+        proto = factory.createProto(factory.intType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
+      case 'J':  // long
+        proto = factory.createProto(factory.longType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
+      case 'F':  // float
+        proto = factory.createProto(factory.floatType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
+      case 'D':  // double
+        proto = factory.createProto(factory.doubleType, EMPTY_TYPE_ARRAY);
+        return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
+      default:
+        throw new Unreachable("Invalid primitive type descriptor: " + primitive);
+    }
+  }
+
+  private int addPrimitiveUnboxing(int register, DexType primitiveType, DexType boxType) {
+    DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType);
+
+    List<MoveType> argMoveTypes = Collections.singletonList(MoveType.OBJECT);
+    List<Integer> argRegisters = Collections.singletonList(register);
+    add(builder -> builder.addInvoke(Invoke.Type.VIRTUAL,
+        method, method.proto, argMoveTypes, argRegisters));
+
+    MoveType moveType = MoveType.fromDexType(primitiveType);
+    int result = nextRegister(moveType);
+    add(builder -> builder.addMoveResult(moveType, result));
+    return result;
+  }
+
+  private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) {
+    // Generate factory method fo boxing.
+    DexItemFactory factory = factory();
+    DexProto proto = factory.createProto(boxType, new DexType[] { primitiveType });
+    DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
+
+    MoveType moveType = MoveType.fromDexType(primitiveType);
+    List<MoveType> argMoveTypes = Collections.singletonList(moveType);
+    List<Integer> argRegisters = Collections.singletonList(register);
+    add(builder -> builder.addInvoke(Invoke.Type.STATIC,
+        method, method.proto, argMoveTypes, argRegisters));
+
+    int result = nextRegister(MoveType.OBJECT);
+    add(builder -> builder.addMoveResult(MoveType.OBJECT, result));
+    return result;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
new file mode 100644
index 0000000..11484fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -0,0 +1,295 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+/**
+ * Lambda desugaring rewriter.
+ *
+ * Performs lambda instantiation point matching,
+ * lambda class generation, and instruction patching.
+ */
+public class LambdaRewriter {
+  private static final String METAFACTORY_TYPE_DESCR = "Ljava/lang/invoke/LambdaMetafactory;";
+  private static final String CALLSITE_TYPE_DESCR = "Ljava/lang/invoke/CallSite;";
+  private static final String LOOKUP_TYPE_DESCR = "Ljava/lang/invoke/MethodHandles$Lookup;";
+  private static final String METHODTYPE_TYPE_DESCR = "Ljava/lang/invoke/MethodType;";
+  private static final String METHODHANDLE_TYPE_DESCR = "Ljava/lang/invoke/MethodHandle;";
+  private static final String OBJECT_ARRAY_TYPE_DESCR = "[Ljava/lang/Object;";
+  private static final String SERIALIZABLE_TYPE_DESCR = "Ljava/io/Serializable;";
+
+  private static final String METAFACTORY_METHOD_NAME = "metafactory";
+  private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
+
+  static final String LAMBDA_CLASS_NAME_PREFIX = "-$Lambda$";
+  static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
+  static final DexType[] EMPTY_TYPE_ARRAY = new DexType[0];
+  static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
+
+  final IRConverter converter;
+  final DexItemFactory factory;
+
+  final DexMethod metafactoryMethod;
+  final DexMethod objectInitMethod;
+
+  final DexMethod metafactoryAltMethod;
+  final DexType serializableType;
+
+  final DexString constructorName;
+  final DexString classConstructorName;
+  final DexString instanceFieldName;
+
+  // Maps call sites seen so far to inferred lambda descriptor. It is intended
+  // to help avoid re-matching call sites we already seen. Note that same call
+  // site may match one or several lambda classes.
+  //
+  // NOTE: synchronize concurrent access on `knownCallSites`.
+  private final Map<DexCallSite, LambdaDescriptor> knownCallSites = new IdentityHashMap<>();
+
+  // Maps lambda class type into lambda class representation. Since lambda class
+  // type uniquely defines lambda class, effectively canonicalizes lambda classes.
+  // NOTE: synchronize concurrent access on `knownLambdaClasses`.
+  private final Map<DexType, LambdaClass> knownLambdaClasses = new IdentityHashMap<>();
+
+  // Checks if the type starts with lambda-class prefix.
+  public static boolean hasLambdaClassPrefix(DexType clazz) {
+    return clazz.getName().startsWith(LAMBDA_CLASS_NAME_PREFIX);
+  }
+
+  public LambdaRewriter(IRConverter converter) {
+    assert converter != null;
+    this.converter = converter;
+    this.factory = converter.application.dexItemFactory;
+
+    DexType metafactoryType = factory.createType(METAFACTORY_TYPE_DESCR);
+    DexType callSiteType = factory.createType(CALLSITE_TYPE_DESCR);
+    DexType lookupType = factory.createType(LOOKUP_TYPE_DESCR);
+    DexType methodTypeType = factory.createType(METHODTYPE_TYPE_DESCR);
+    DexType methodHandleType = factory.createType(METHODHANDLE_TYPE_DESCR);
+    DexType objectArrayType = factory.createType(OBJECT_ARRAY_TYPE_DESCR);
+
+    this.metafactoryMethod = factory.createMethod(metafactoryType,
+        factory.createProto(callSiteType, new DexType[] {
+            lookupType, factory.stringType, methodTypeType,
+            methodTypeType, methodHandleType, methodTypeType
+        }),
+        factory.createString(METAFACTORY_METHOD_NAME));
+
+    this.metafactoryAltMethod = factory.createMethod(metafactoryType,
+        factory.createProto(callSiteType, new DexType[] {
+            lookupType, factory.stringType, methodTypeType, objectArrayType
+        }),
+        factory.createString(METAFACTORY_ALT_METHOD_NAME));
+
+    this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
+    DexProto initProto = factory.createProto(factory.voidType, EMPTY_TYPE_ARRAY);
+    this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
+    this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
+    this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
+    this.serializableType = factory.createType(SERIALIZABLE_TYPE_DESCR);
+  }
+
+  /**
+   * Detect and desugar lambdas and method references found in the code.
+   *
+   * NOTE: this method can be called concurrently for several different methods.
+   */
+  public void desugarLambdas(DexEncodedMethod encodedMethod, IRCode code) {
+    DexType currentType = encodedMethod.method.holder;
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    while (blocks.hasNext()) {
+      BasicBlock block = blocks.next();
+      InstructionListIterator instructions = block.listIterator();
+      while (instructions.hasNext()) {
+        Instruction instruction = instructions.next();
+        if (instruction.isInvokeCustom()) {
+          LambdaDescriptor descriptor = inferLambdaDescriptor(
+              instruction.asInvokeCustom().getCallSite());
+          if (descriptor == LambdaDescriptor.MATCH_FAILED) {
+            continue;
+          }
+
+          // We have a descriptor, get or create lambda class.
+          LambdaClass lambdaClass = getOrCreateLambdaClass(descriptor, currentType);
+          assert lambdaClass != null;
+
+          // We rely on patch performing its work in a way which
+          // keeps both `instructions` and `blocks` iterators in
+          // valid state so that we can continue iteration.
+          patchInstruction(lambdaClass, code, blocks, instructions);
+        }
+      }
+    }
+  }
+
+  /**
+   * Adjust accessibility of referenced application symbols or
+   * creates necessary accessors.
+   */
+  public void adjustAccessibility(Builder builder) {
+    // For each lambda class perform necessary adjustment of the
+    // referenced symbols to make them accessible. This can result in
+    // method access relaxation or creation of accessor method.
+    for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+      lambdaClass.target.ensureAccessibility(builder);
+    }
+  }
+
+  /** Generates lambda classes and adds them to the builder. */
+  public void synthesizeLambdaClasses(Builder builder) {
+    for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+      DexProgramClass synthesizedClass = lambdaClass.synthesizeLambdaClass();
+      converter.optimizeSynthesizedClass(synthesizedClass);
+      builder.addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
+    }
+  }
+
+  // Matches invoke-custom instruction operands to infer lambda descriptor
+  // corresponding to this lambda invocation point.
+  //
+  // Returns the lambda descriptor or `MATCH_FAILED`.
+  private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite) {
+    // We check the map before and after inferring lambda descriptor to minimize time
+    // spent in synchronized block. As a result we may throw away calculated descriptor
+    // in rare case when another thread has same call site processed concurrently,
+    // but this is a low price to pay comparing to making whole method synchronous.
+    LambdaDescriptor descriptor = getKnown(knownCallSites, callSite);
+    return descriptor != null ? descriptor
+        : putIfAbsent(knownCallSites, callSite, LambdaDescriptor.infer(this, callSite));
+  }
+
+  private boolean isInMainDexList(DexType type) {
+    return converter.application.mainDexList.contains(type);
+  }
+
+  // Returns a lambda class corresponding to the lambda descriptor and context,
+  // creates the class if it does not yet exist.
+  private LambdaClass getOrCreateLambdaClass(LambdaDescriptor descriptor, DexType accessedFrom) {
+    DexType lambdaClassType = LambdaClass.createLambdaClassType(this, accessedFrom, descriptor);
+    // We check the map twice to to minimize time spent in synchronized block.
+    LambdaClass lambdaClass = getKnown(knownLambdaClasses, lambdaClassType);
+    if (lambdaClass == null) {
+      lambdaClass = putIfAbsent(knownLambdaClasses, lambdaClassType,
+          new LambdaClass(this, accessedFrom, lambdaClassType, descriptor));
+    }
+    if (isInMainDexList(accessedFrom)) {
+      lambdaClass.addToMainDexList.set(true);
+    }
+    return lambdaClass;
+  }
+
+  private <K, V> V getKnown(Map<K, V> map, K key) {
+    synchronized (map) {
+      return map.get(key);
+    }
+  }
+
+  private <K, V> V putIfAbsent(Map<K, V> map, K key, V value) {
+    synchronized (map) {
+      V known = map.get(key);
+      if (known != null) {
+        return known;
+      }
+      map.put(key, value);
+      return value;
+    }
+  }
+
+  // Patches invoke-custom instruction to create or get an instance
+  // of the generated lambda class.
+  private void patchInstruction(LambdaClass lambdaClass, IRCode code,
+      ListIterator<BasicBlock> blocks, InstructionListIterator instructions) {
+    assert lambdaClass != null;
+    assert instructions != null;
+    assert instructions.peekPrevious().isInvokeCustom();
+
+    // Move to the previous instruction, must be InvokeCustom
+    InvokeCustom invoke = instructions.previous().asInvokeCustom();
+
+    // The value representing new lambda instance: we reuse the
+    // value from the original invoke-custom instruction, and thus
+    // all its usages.
+    Value lambdaInstanceValue = invoke.outValue();
+    if (lambdaInstanceValue == null) {
+      // The out value might be empty in case it was optimized out.
+      lambdaInstanceValue = new Value(
+          code.valueNumberGenerator.next(), -1, MoveType.OBJECT, null);
+    }
+
+    // For stateless lambdas we replace InvokeCustom instruction with StaticGet
+    // reading the value of INSTANCE field created for singleton lambda class.
+    if (lambdaClass.isStateless()) {
+      instructions.replaceCurrentInstruction(
+          new StaticGet(MemberType.OBJECT, lambdaInstanceValue, lambdaClass.instanceField));
+      // Note that since we replace one throwing operation with another we don't need
+      // to have any special handling for catch handlers.
+      return;
+    }
+
+    // For stateful lambdas we always create a new instance since we need to pass
+    // captured values to the constructor.
+    //
+    // We replace InvokeCustom instruction with a new NewInstance instruction
+    // instantiating lambda followed by InvokeDirect instruction calling a
+    // constructor on it.
+    //
+    //    original:
+    //      Invoke-Custom rResult <- { rArg0, rArg1, ... }; call site: ...
+    //
+    //    result:
+    //      NewInstance   rResult <-  LambdaClass
+    //      Invoke-Direct { rResult, rArg0, rArg1, ... }; method: void LambdaClass.<init>(...)
+    NewInstance newInstance = new NewInstance(lambdaClass.type, lambdaInstanceValue);
+    instructions.replaceCurrentInstruction(newInstance);
+
+    List<Value> arguments = new ArrayList<>();
+    arguments.add(lambdaInstanceValue);
+    arguments.addAll(invoke.arguments()); // Optional captures.
+    InvokeDirect constructorCall = new InvokeDirect(
+        lambdaClass.constructor, null /* no return value */, arguments);
+    instructions.add(constructorCall);
+
+    // If we don't have catch handlers we are done.
+    if (!constructorCall.getBlock().hasCatchHandlers()) {
+      return;
+    }
+
+    // Move the iterator back to position it between the two instructions, split
+    // the block between the two instructions, and copy the catch handlers.
+    instructions.previous();
+    assert instructions.peekNext().isInvokeDirect();
+    BasicBlock currentBlock = newInstance.getBlock();
+    BasicBlock nextBlock = instructions.split(code, blocks);
+    assert !instructions.hasNext();
+    nextBlock.copyCatchHandlers(code, blocks, currentBlock);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java
new file mode 100644
index 0000000..32631c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SingleBlockSourceCode.java
@@ -0,0 +1,212 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import static com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo.NO_THROW;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+abstract class SingleBlockSourceCode implements SourceCode {
+  final DexType receiver;
+  final DexProto proto;
+
+  // The next free register, note that we always
+  // assign each value a new (next available) register.
+  private int nextRegister = 0;
+
+  // Registers for receiver and parameters
+  private final int receiverRegister;
+  private int[] paramRegisters;
+  // Values representing receiver and parameters will be filled in
+  // buildPrelude() and should only be accessed via appropriate methods
+  private Value receiverValue;
+  private Value[] paramValues;
+
+  // Instruction constructors
+  private List<Consumer<IRBuilder>> constructors = new ArrayList<>();
+
+  SingleBlockSourceCode(DexType receiver, DexProto proto) {
+    assert proto != null;
+    this.receiver = receiver;
+    this.proto = proto;
+
+    // Initialize register values for receiver and arguments
+    this.receiverRegister = receiver != null ? nextRegister(MoveType.OBJECT) : -1;
+
+    DexType[] params = proto.parameters.values;
+    int paramCount = params.length;
+    this.paramRegisters = new int[paramCount];
+    this.paramValues = new Value[paramCount];
+    for (int i = 0; i < paramCount; i++) {
+      this.paramRegisters[i] = nextRegister(MoveType.fromDexType(params[i]));
+    }
+  }
+
+  final void add(Consumer<IRBuilder> constructor) {
+    constructors.add(constructor);
+  }
+
+  final int nextRegister(MoveType type) {
+    int value = nextRegister;
+    nextRegister += type == MoveType.WIDE ? 2 : 1;
+    return value;
+  }
+
+  final Value getReceiverValue() {
+    assert receiver != null;
+    assert receiverValue != null;
+    return receiverValue;
+  }
+
+  final int getReceiverRegister() {
+    assert receiver != null;
+    assert receiverRegister >= 0;
+    return receiverRegister;
+  }
+
+  final Value getParamValue(int paramIndex) {
+    assert paramIndex >= 0;
+    assert paramIndex < paramValues.length;
+    return paramValues[paramIndex];
+  }
+
+  final int getParamCount() {
+    return paramValues.length;
+  }
+
+  final int getParamRegister(int paramIndex) {
+    assert paramIndex >= 0;
+    assert paramIndex < paramRegisters.length;
+    return paramRegisters[paramIndex];
+  }
+
+  abstract void prepareInstructions();
+
+  @Override
+  public final boolean needsPrelude() {
+    return receiver != null || paramRegisters.length > 0;
+  }
+
+  @Override
+  public final int instructionCount() {
+    return constructors.size();
+  }
+
+  @Override
+  public final int instructionIndex(int instructionOffset) {
+    return instructionOffset;
+  }
+
+  @Override
+  public final int instructionOffset(int instructionIndex) {
+    return instructionIndex;
+  }
+
+  @Override
+  public DebugLocalInfo getCurrentLocal(int register) {
+    return null;
+  }
+
+  @Override
+  public final boolean traceInstruction(int instructionIndex, IRBuilder builder) {
+    return instructionIndex == constructors.size() - 1;
+  }
+
+  @Override
+  public final void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+  }
+
+  @Override
+  public final void closedCurrentBlock() {
+  }
+
+  @Override
+  public final void setUp() {
+    assert constructors.isEmpty();
+    prepareInstructions();
+    assert !constructors.isEmpty();
+  }
+
+  @Override
+  public final void clear() {
+    constructors = null;
+    paramRegisters = null;
+    paramValues = null;
+    receiverValue = null;
+  }
+
+  @Override
+  public final void buildPrelude(IRBuilder builder) {
+    if (receiver != null) {
+      receiverValue = builder.writeRegister(receiverRegister, MoveType.OBJECT, NO_THROW);
+      builder.add(new Argument(receiverValue));
+    }
+
+    // Fill in the Argument instructions in the argument block.
+    DexType[] parameters = proto.parameters.values;
+    for (int i = 0; i < parameters.length; i++) {
+      MoveType moveType = MoveType.fromDexType(parameters[i]);
+      Value paramValue = builder.writeRegister(paramRegisters[i], moveType, NO_THROW);
+      paramValues[i] = paramValue;
+      builder.add(new Argument(paramValue));
+    }
+  }
+
+  @Override
+  public final void buildPostlude(IRBuilder builder) {
+    // Intentionally left empty.
+  }
+
+  @Override
+  public final void buildInstruction(IRBuilder builder, int instructionIndex) {
+    constructors.get(instructionIndex).accept(builder);
+  }
+
+  @Override
+  public final void resolveAndBuildSwitch(
+      Switch.Type type, int value, int fallthroughOffset,
+      int payloadOffset, IRBuilder builder) {
+    throw new Unreachable("Unexpected call to resolveAndBuildSwitch");
+  }
+
+  @Override
+  public final void resolveAndBuildNewArrayFilledData(
+      int arrayRef, int payloadOffset, IRBuilder builder) {
+    throw new Unreachable("Unexpected call to resolveAndBuildNewArrayFilledData");
+  }
+
+  @Override
+  public final CatchHandlers<Integer> getCurrentCatchHandlers() {
+    return null;
+  }
+
+  @Override
+  public final boolean verifyCurrentInstructionCanThrow() {
+    return true;
+  }
+
+  @Override
+  public boolean verifyLocalInScope(DebugLocalInfo local) {
+    return true;
+  }
+
+  @Override
+  public final boolean verifyRegister(int register) {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java
new file mode 100644
index 0000000..3a07ef4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedCode.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+
+public final class SynthesizedCode extends Code {
+  private final SourceCode sourceCode;
+
+  public SynthesizedCode(SourceCode sourceCode) {
+    this.sourceCode = sourceCode;
+  }
+
+  @Override
+  public final IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+    return new IRBuilder(encodedMethod, sourceCode, options).build();
+  }
+
+  @Override
+  public final String toString() {
+    return toString(null);
+  }
+
+  @Override
+  public final void registerReachableDefinitions(UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  protected final int computeHashCode() {
+    return sourceCode.hashCode();
+  }
+
+  @Override
+  protected final boolean computeEquals(Object other) {
+    return other instanceof SynthesizedCode &&
+        this.sourceCode.equals(((SynthesizedCode) other).sourceCode);
+  }
+
+  @Override
+  public final String toString(ClassNameMapper naming) {
+    return "SynthesizedCode: " + sourceCode.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
new file mode 100644
index 0000000..7a63c32
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+
+// Represents source code of synthesized lambda class methods.
+abstract class SynthesizedLambdaSourceCode extends SingleBlockSourceCode {
+  final DexMethod currentMethod;
+  final LambdaClass lambda;
+
+  SynthesizedLambdaSourceCode(DexType receiver, LambdaClass lambda, DexMethod currentMethod) {
+    super(receiver, currentMethod.proto);
+    this.lambda = lambda;
+    this.currentMethod = currentMethod;
+  }
+
+  SynthesizedLambdaSourceCode(LambdaClass lambda, DexMethod currentMethod) {
+    this(lambda.type, lambda, currentMethod);
+  }
+
+  final LambdaDescriptor descriptor() {
+    return lambda.descriptor;
+  }
+
+  final DexType[] captures() {
+    DexTypeList captures = descriptor().captures;
+    assert captures != null;
+    return captures.values;
+  }
+
+  final DexItemFactory factory() {
+    return lambda.rewriter.factory;
+  }
+
+  final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
+    // `paramType` must be either same as `enforcedType` or both must be class
+    // types and `enforcedType` must be a subclass of `paramType` in which case
+    // a cast need to be inserted.
+    if (paramType != enforcedType) {
+      assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, paramType);
+      add(builder -> builder.addCheckCast(register, enforcedType));
+    }
+    return register;
+  }
+
+  @Override
+  public String toString() {
+    return currentMethod.toSourceString();
+  }
+}
+
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
new file mode 100644
index 0000000..27cf89a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/BasicBlockInstructionsEquivalence.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.base.Equivalence;
+import java.util.List;
+
+class BasicBlockInstructionsEquivalence extends Equivalence<BasicBlock> {
+
+  private static final int MAX_HASH_INSTRUCTIONS = 5;
+  private final RegisterAllocator allocator;
+
+  BasicBlockInstructionsEquivalence(RegisterAllocator allocator) {
+    this.allocator = allocator;
+  }
+
+  private boolean hasIdenticalInstructions(BasicBlock first, BasicBlock second) {
+    List<Instruction> instructions0 = first.getInstructions();
+    List<Instruction> instructions1 = second.getInstructions();
+    if (instructions0.size() != instructions1.size()) {
+      return false;
+    }
+    for (int i = 0; i < instructions0.size(); i++) {
+      Instruction i0 = instructions0.get(i);
+      Instruction i1 = instructions1.get(i);
+      if (!i0.identicalAfterRegisterAllocation(i1, allocator)) {
+        return false;
+      }
+    }
+    CatchHandlers<BasicBlock> handlers0 = first.getCatchHandlers();
+    CatchHandlers<BasicBlock> handlers1 = second.getCatchHandlers();
+    if (!handlers0.equals(handlers1)) {
+      return false;
+    }
+    // Normal successors are equal based on equality of the instruction stream. Verify that here.
+    assert verifyAllSuccessors(first.getSuccessors(), second.getSuccessors());
+    return true;
+  }
+
+  private boolean verifyAllSuccessors(List<BasicBlock> successors0, List<BasicBlock> successors1) {
+    if (successors0.size() != successors1.size()) {
+      return false;
+    }
+    for (int i = 0; i < successors0.size(); i++) {
+      if (successors0.get(i) != successors1.get(i)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  protected boolean doEquivalent(BasicBlock a, BasicBlock b) {
+    return hasIdenticalInstructions(a, b);
+  }
+
+  @Override
+  protected int doHash(BasicBlock basicBlock) {
+    List<Instruction> instructions = basicBlock.getInstructions();
+    int hash = instructions.size();
+    for (int i = 0; i < instructions.size() && i < MAX_HASH_INSTRUCTIONS; i++) {
+      Instruction instruction = instructions.get(i);
+      int hashPart = 0;
+      if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
+        hashPart += allocator.getRegisterForValue(instruction.outValue(), instruction.getNumber());
+      }
+      for (Value inValue : instruction.inValues()) {
+        hashPart = hashPart << 4;
+        if (inValue.needsRegister()) {
+          hashPart += allocator.getRegisterForValue(inValue, instruction.getNumber());
+        }
+      }
+      hash = hash * 3 + hashPart;
+    }
+    return hash;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
new file mode 100644
index 0000000..5e701ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -0,0 +1,941 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ArrayPut;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.Binop;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.DominatorTree;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.JumpInstruction;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.NewArrayFilledData;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.LongInterval;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ListMultimap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+public class CodeRewriter {
+
+  private static final int UNKNOWN_CAN_THROW = 0;
+  private static final int CAN_THROW = 1;
+  private static final int CANNOT_THROW = 2;
+  private static final int MAX_FILL_ARRAY_SIZE = 4 * Constants.KILOBYTE;
+
+  private final AppInfo appInfo;
+  private final DexItemFactory dexItemFactory;
+
+  public CodeRewriter(AppInfo appInfo) {
+    this.appInfo = appInfo;
+    this.dexItemFactory = appInfo.dexItemFactory;
+  }
+
+  /**
+   * Removes all debug positions that are not needed to maintain proper stack trace information.
+   * If a debug position is followed by another debug position and no instructions between the two
+   * can throw then it is unneeded (in a release build).
+   * If a block with a position has (normal) outgoing edges, this property depends on the
+   * possibility of the successors throwing before the next debug position is hit.
+   */
+  public static boolean removedUnneededDebugPositions(IRCode code) {
+    computeThrowsColorForAllBlocks(code);
+    for (BasicBlock block : code.blocks) {
+      InstructionListIterator iterator = block.listIterator();
+      while (iterator.hasNext()) {
+        Instruction instruction = iterator.next();
+        if (instruction.isDebugPosition()
+            && getThrowsColorForBlock(block, iterator.nextIndex()) == CANNOT_THROW) {
+          iterator.remove();
+        }
+      }
+    }
+    return true;
+  }
+
+  private static void computeThrowsColorForAllBlocks(IRCode code) {
+    // First pass colors blocks in reverse topological order, based on the instructions.
+    code.clearMarks();
+    List<BasicBlock> blocks = code.blocks;
+    ArrayList<BasicBlock> worklist = new ArrayList<>();
+    for (int i = blocks.size() - 1; i >= 0; i--) {
+      BasicBlock block = blocks.get(i);
+      // Mark the block as not-throwing if no successor implies otherwise.
+      // This ensures that a loop back to this block will be seen as non-throwing.
+      block.setColor(CANNOT_THROW);
+      int color = getThrowsColorForBlock(block, 0);
+      block.setColor(color);
+      if (color == UNKNOWN_CAN_THROW) {
+        worklist.add(block);
+      }
+    }
+    // A fixed point then ensures that we propagate the color backwards over normal edges.
+    ArrayList<BasicBlock> remaining = new ArrayList<>(worklist.size());
+    while (!worklist.isEmpty()) {
+      ImmutableList<BasicBlock> work = new ImmutableList.Builder<BasicBlock>()
+          .addAll(worklist)
+          .addAll(remaining)
+          .build();
+      worklist.clear();
+      remaining.clear();
+      for (BasicBlock block : work) {
+        if (!block.hasColor(UNKNOWN_CAN_THROW)) {
+          continue;
+        }
+        block.setColor(CANNOT_THROW);
+        int color = getThrowsColorForSuccessors(block);
+        block.setColor(color);
+        if (color == UNKNOWN_CAN_THROW) {
+          remaining.add(block);
+        } else {
+          for (BasicBlock predecessor : block.getNormalPredecessors()) {
+            if (predecessor.hasColor(UNKNOWN_CAN_THROW)) {
+              worklist.add(predecessor);
+            }
+          }
+        }
+      }
+    }
+    // Any remaining set of blocks represents a cycle of blocks containing no throwing instructions.
+    for (BasicBlock block : remaining) {
+      assert !block.canThrow();
+      block.setColor(CANNOT_THROW);
+    }
+  }
+
+  private static int getThrowsColorForBlock(BasicBlock block, int index) {
+    InstructionListIterator iterator = block.listIterator(index);
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (instruction.isDebugPosition()) {
+        return CANNOT_THROW;
+      }
+      if (instruction.instructionTypeCanThrow()) {
+        return CAN_THROW;
+      }
+    }
+    return getThrowsColorForSuccessors(block);
+  }
+
+  private static int getThrowsColorForSuccessors(BasicBlock block) {
+    int color = CANNOT_THROW;
+    for (BasicBlock successor : block.getNormalSucessors()) {
+      if (successor.hasColor(CAN_THROW)) {
+        return CAN_THROW;
+      }
+      if (successor.hasColor(UNKNOWN_CAN_THROW)) {
+        color = UNKNOWN_CAN_THROW;
+      }
+    }
+    return color;
+  }
+
+  private static boolean removedTrivialGotos(IRCode code) {
+    ListIterator<BasicBlock> iterator = code.listIterator();
+    assert iterator.hasNext();
+    BasicBlock block = iterator.next();
+    BasicBlock nextBlock;
+    do {
+      nextBlock = iterator.hasNext() ? iterator.next() : null;
+      // Trivial goto block are only kept if they are self-targeting or are targeted by
+      // fallthroughs.
+      BasicBlock blk = block;  // Additional local for lambda below.
+      assert !block.isTrivialGoto()
+          || block.exit().asGoto().getTarget() == block
+          || block.getPredecessors().stream().anyMatch((b) -> b.exit().fallthroughBlock() == blk);
+      // Trivial goto blocks never target the next block (in that case there should just be a
+      // fallthrough).
+      assert !block.isTrivialGoto() || block.exit().asGoto().getTarget() != nextBlock;
+      block = nextBlock;
+    } while (block != null);
+    return true;
+  }
+
+  private static BasicBlock endOfGotoChain(BasicBlock block) {
+    block.mark();
+    BasicBlock target = block;
+    while (target.isTrivialGoto()) {
+      BasicBlock nextTarget = target.exit().asGoto().getTarget();
+      if (nextTarget.isMarked()) {
+        clearTrivialGotoMarks(block);
+        return nextTarget;
+      }
+      nextTarget.mark();
+      target = nextTarget;
+    }
+    clearTrivialGotoMarks(block);
+    return target;
+  }
+
+  private static void clearTrivialGotoMarks(BasicBlock block) {
+    while (block.isMarked()) {
+      block.clearMark();
+      if (block.isTrivialGoto()) {
+        block = block.exit().asGoto().getTarget();
+      }
+    }
+  }
+
+  private static void collapsTrivialGoto(
+      BasicBlock block, BasicBlock nextBlock, List<BasicBlock> blocksToRemove) {
+
+    // This is the base case for GOTO loops.
+    if (block.exit().asGoto().getTarget() == block) {
+      return;
+    }
+
+    BasicBlock target = endOfGotoChain(block);
+
+    boolean needed = false;
+    if (target != nextBlock) {
+      for (BasicBlock pred : block.getPredecessors()) {
+        if (pred.exit().fallthroughBlock() == block) {
+          needed = true;
+          break;
+        }
+      }
+    }
+
+    // This implies we are in a loop of GOTOs. In that case, we will iteratively remove each trival
+    // GOTO one-by-one until the above base case (one block targeting itself) is left.
+    if (target == block) {
+      target = block.exit().asGoto().getTarget();
+    }
+
+    if (!needed) {
+      blocksToRemove.add(block);
+      for (BasicBlock pred : block.getPredecessors()) {
+        pred.replaceSuccessor(block, target);
+      }
+      for (BasicBlock succ : block.getSuccessors()) {
+        succ.getPredecessors().remove(block);
+      }
+      for (BasicBlock pred : block.getPredecessors()) {
+        if (!target.getPredecessors().contains(pred)) {
+          target.getPredecessors().add(pred);
+        }
+      }
+    }
+  }
+
+  private static void collapsIfTrueTarget(BasicBlock block) {
+    If insn = block.exit().asIf();
+    BasicBlock target = insn.getTrueTarget();
+    BasicBlock newTarget = endOfGotoChain(target);
+    BasicBlock fallthrough = insn.fallthroughBlock();
+    BasicBlock newFallthrough = endOfGotoChain(fallthrough);
+    if (target != newTarget) {
+      insn.getBlock().replaceSuccessor(target, newTarget);
+      target.getPredecessors().remove(block);
+      if (!newTarget.getPredecessors().contains(block)) {
+        newTarget.getPredecessors().add(block);
+      }
+    }
+    if (block.exit().isIf()) {
+      insn = block.exit().asIf();
+      if (insn.getTrueTarget() == newFallthrough) {
+        // Replace if with the same true and fallthrough target with a goto to the fallthrough.
+        block.replaceSuccessor(insn.getTrueTarget(), fallthrough);
+        assert block.exit().isGoto();
+        assert block.exit().asGoto().getTarget() == fallthrough;
+      }
+    }
+  }
+
+  private static void collapsNonFallthroughSwitchTargets(BasicBlock block) {
+    Switch insn = block.exit().asSwitch();
+    BasicBlock fallthroughBlock = insn.fallthroughBlock();
+    Set<BasicBlock> replacedBlocks = new HashSet<>();
+    for (int j = 0; j < insn.targetBlockIndices().length; j++) {
+      BasicBlock target = insn.targetBlock(j);
+      if (target != fallthroughBlock) {
+        BasicBlock newTarget = endOfGotoChain(target);
+        if (target != newTarget && !replacedBlocks.contains(target)) {
+          insn.getBlock().replaceSuccessor(target, newTarget);
+          target.getPredecessors().remove(block);
+          if (!newTarget.getPredecessors().contains(block)) {
+            newTarget.getPredecessors().add(block);
+          }
+          replacedBlocks.add(target);
+        }
+      }
+    }
+  }
+
+  /**
+   * Rewrite all branch targets to the destination of trivial goto chains when possible.
+   * Does not rewrite fallthrough targets as that would require block reordering and the
+   * transformation only makes sense after SSA destruction where there are no phis.
+   */
+  public static void collapsTrivialGotos(DexEncodedMethod method, IRCode code) {
+    assert code.isConsistentGraph();
+    List<BasicBlock> blocksToRemove = new ArrayList<>();
+    // Rewrite all non-fallthrough targets to the end of trivial goto chains and remove
+    // first round of trivial goto blocks.
+    ListIterator<BasicBlock> iterator = code.listIterator();
+    assert iterator.hasNext();
+    BasicBlock block = iterator.next();
+    BasicBlock nextBlock;
+
+    // The marks will be used for cycle detection.
+    code.clearMarks();
+    do {
+      nextBlock = iterator.hasNext() ? iterator.next() : null;
+      if (block.isTrivialGoto()) {
+        collapsTrivialGoto(block, nextBlock, blocksToRemove);
+      }
+      if (block.exit().isIf()) {
+        collapsIfTrueTarget(block);
+      }
+      if (block.exit().isSwitch()) {
+        collapsNonFallthroughSwitchTargets(block);
+      }
+      block = nextBlock;
+    } while (nextBlock != null);
+    code.removeBlocks(blocksToRemove);
+    // Get rid of gotos to the next block.
+    while (!blocksToRemove.isEmpty()) {
+      blocksToRemove = new ArrayList<>();
+      iterator = code.listIterator();
+      block = iterator.next();
+      do {
+        nextBlock = iterator.hasNext() ? iterator.next() : null;
+        if (block.isTrivialGoto()) {
+          collapsTrivialGoto(block, nextBlock, blocksToRemove);
+        }
+        block = nextBlock;
+      } while (block != null);
+      code.removeBlocks(blocksToRemove);
+    }
+    assert removedTrivialGotos(code);
+    assert code.isConsistentGraph();
+  }
+
+  public void identifyReturnsArgument(
+      DexEncodedMethod method, IRCode code) {
+    if (code.getNormalExitBlock() != null) {
+      Return ret = code.getNormalExitBlock().exit().asReturn();
+      if (!ret.isReturnVoid()) {
+        Value returnValue = ret.returnValue();
+        if (returnValue.isArgument()) {
+          // Find the argument number.
+          int index = code.collectArguments().indexOf(returnValue);
+          assert index != -1;
+          method.markReturnsArgument(index);
+        }
+        if (returnValue.isConstant() && returnValue.definition.isConstNumber()) {
+          long value = returnValue.definition.asConstNumber().getRawValue();
+          method.markReturnsConstant(value);
+        }
+        if (returnValue.isNeverNull()) {
+          method.markNeverReturnsNull();
+        }
+      }
+    }
+  }
+
+  private boolean checkArgumentType(InvokeMethod invoke, DexMethod target, int argumentIndex) {
+    DexType returnType = invoke.getInvokedMethod().proto.returnType;
+    // TODO(sgjesse): Insert cast if required.
+    if (invoke.isInvokeStatic()) {
+      return invoke.getInvokedMethod().proto.parameters.values[argumentIndex] == returnType;
+    } else {
+      if (argumentIndex == 0) {
+        return invoke.getInvokedMethod().getHolder() == returnType;
+      } else {
+        return invoke.getInvokedMethod().proto.parameters.values[argumentIndex - 1] == returnType;
+      }
+    }
+  }
+
+  // Replace result uses for methods where something is known about what is returned.
+  public void rewriteMoveResult(IRCode code) {
+    if (!appInfo.hasSubtyping()) {
+      return;
+    }
+    InstructionIterator iterator = code.instructionIterator();
+    while (iterator.hasNext()) {
+      Instruction current = iterator.next();
+      if (current.isInvokeMethod()) {
+        InvokeMethod invoke = current.asInvokeMethod();
+        if (invoke.outValue() != null) {
+          DexEncodedMethod target = invoke.computeSingleTarget(appInfo.withSubtyping());
+          if (target != null) {
+            DexMethod invokedMethod = target.method;
+            // Check if the invoked method is known to return one of its arguments.
+            DexEncodedMethod definition = appInfo.definitionFor(invokedMethod);
+            if (definition != null && definition.getOptimizationInfo().returnsArgument()) {
+              int argumentIndex = definition.getOptimizationInfo().getReturnedArgument();
+              // Replace the out value of the invoke with the argument and ignore the out value.
+              if (argumentIndex != -1 && checkArgumentType(invoke, target.method, argumentIndex)) {
+                Value argument = invoke.arguments().get(argumentIndex);
+                assert (invoke.outType() == argument.outType()) ||
+                    (invoke.outType() == MoveType.OBJECT
+                        && argument.outType() == MoveType.SINGLE
+                        && argument.getConstInstruction().asConstNumber().isZero());
+                invoke.outValue().replaceUsers(argument);
+                invoke.setOutValue(null);
+              }
+            }
+          }
+        }
+      }
+    }
+    assert code.isConsistentGraph();
+  }
+
+  private boolean canBeFolded(Instruction instruction) {
+    return (instruction.isBinop() && instruction.asBinop().canBeFolded()) ||
+        (instruction.isUnop() && instruction.asUnop().canBeFolded());
+  }
+
+  public void foldConstants(IRCode code) {
+    Queue<BasicBlock> worklist = new LinkedList<>();
+    worklist.addAll(code.blocks);
+    for (BasicBlock block = worklist.poll(); block != null; block = worklist.poll()) {
+      InstructionIterator iterator = block.iterator();
+      while (iterator.hasNext()) {
+        Instruction current = iterator.next();
+        Instruction folded;
+        if (canBeFolded(current)) {
+          folded = current.fold(code.valueNumberGenerator);
+          iterator.replaceCurrentInstruction(folded);
+          folded.outValue().uniqueUsers()
+              .forEach(instruction -> worklist.add(instruction.getBlock()));
+        }
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+
+  // Constants are canonicalized in the entry block. We split some of them when it is likely
+  // that having them canonicalized in the entry block will lead to poor code quality.
+  public void splitConstants(IRCode code) {
+    for (BasicBlock block : code.blocks) {
+      // Split constants that flow into phis. It is likely that these constants will have moves
+      // generated for them anyway and we might as well insert a const instruction in the right
+      // predecessor block.
+      splitPhiConstants(code, block);
+      // Split constants that flow into ranged invokes. This gives the register allocator more
+      // freedom in assigning register to ranged invokes which can greatly reduce the number
+      // of register needed (and thereby code size as well).
+      splitRangedInvokeConstants(code, block);
+    }
+  }
+
+  private void splitRangedInvokeConstants(IRCode code, BasicBlock block) {
+    InstructionListIterator it = block.listIterator();
+    while (it.hasNext()) {
+      Instruction current = it.next();
+      if (current.isInvoke() && current.asInvoke().requiredArgumentRegisters() > 5) {
+        Invoke invoke = current.asInvoke();
+        it.previous();
+        Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
+        for (int i = 0; i < invoke.inValues().size(); i++) {
+          Value value = invoke.inValues().get(i);
+          if (value.isConstant() && value.numberOfUsers() > 1) {
+            ConstNumber definition = value.getConstInstruction().asConstNumber();
+            Value originalValue = definition.outValue();
+            ConstNumber newNumber = oldToNew.get(definition);
+            if (newNumber == null) {
+              newNumber = ConstNumber.copyOf(code, definition);
+              it.add(newNumber);
+              oldToNew.put(definition, newNumber);
+            }
+            invoke.inValues().set(i, newNumber.outValue());
+            originalValue.removeUser(invoke);
+            newNumber.outValue().addUser(invoke);
+          }
+        }
+        it.next();
+      }
+    }
+  }
+
+  private void splitPhiConstants(IRCode code, BasicBlock block) {
+    for (int i = 0; i < block.getPredecessors().size(); i++) {
+      Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
+      BasicBlock predecessor = block.getPredecessors().get(i);
+      for (Phi phi : block.getPhis()) {
+        Value operand = phi.getOperand(i);
+        if (!operand.isPhi() && operand.isConstant()) {
+          ConstNumber definition = operand.getConstInstruction().asConstNumber();
+          ConstNumber newNumber = oldToNew.get(definition);
+          Value originalValue = definition.outValue();
+          if (newNumber == null) {
+            newNumber = ConstNumber.copyOf(code, definition);
+            oldToNew.put(definition, newNumber);
+            insertConstantInBlock(newNumber, predecessor);
+          }
+          phi.getOperands().set(i, newNumber.outValue());
+          originalValue.removePhiUser(phi);
+          newNumber.outValue().addPhiUser(phi);
+        }
+      }
+    }
+  }
+
+  public void shortenLiveRanges(IRCode code) {
+    // Currently, we are only shortening the live range of constants in the entry block.
+    // TODO(ager): Generalize this to shorten live ranges for more instructions? Currently
+    // doing so seems to make things worse.
+    DominatorTree dominatorTree = new DominatorTree(code);
+    BasicBlock block = code.blocks.get(0);
+    InstructionListIterator it = block.listIterator();
+    List<Instruction> toInsertInThisBlock = new ArrayList<>();
+    while (it.hasNext()) {
+      Instruction instruction = it.next();
+      if (instruction.isConstNumber()) {
+        // Collect the blocks for all users of the constant.
+        List<BasicBlock> userBlocks = new LinkedList<>();
+        for (Instruction user : instruction.outValue().uniqueUsers()) {
+          userBlocks.add(user.getBlock());
+        }
+        for (Phi phi : instruction.outValue().uniquePhiUsers()) {
+          userBlocks.add(phi.getBlock());
+        }
+        // Locate the closest dominator block for all user blocks.
+        BasicBlock dominator = dominatorTree.closestDominator(userBlocks);
+        // If the closest dominator block is a block that uses the constant for a phi the constant
+        // needs to go in the immediate dominator block so that it is available for phi moves.
+        for (Phi phi : instruction.outValue().uniquePhiUsers()) {
+          if (phi.getBlock() == dominator) {
+            dominator = dominatorTree.immediateDominator(dominator);
+            break;
+          }
+        }
+        // Move the const instruction as close to its uses as possible.
+        it.detach();
+        if (dominator != block) {
+          insertConstantInBlock(instruction, dominator);
+        } else {
+          toInsertInThisBlock.add(instruction);
+        }
+      }
+    }
+    for (Instruction toInsert : toInsertInThisBlock) {
+      insertConstantInBlock(toInsert, block);
+    }
+  }
+
+  private void insertConstantInBlock(Instruction instruction, BasicBlock block) {
+    boolean hasCatchHandlers = block.hasCatchHandlers();
+    InstructionListIterator insertAt = block.listIterator();
+    // Place the instruction as late in the block as we can. It needs to go before users
+    // and if we have catch handlers it needs to be placed before the throwing instruction.
+    insertAt.nextUntil(i -> {
+      return i.inValues().contains(instruction.outValue())
+          || i.isJumpInstruction()
+          || (hasCatchHandlers && i.instructionInstanceCanThrow());
+    });
+    insertAt.previous();
+    insertAt.add(instruction);
+  }
+
+  private short[] computeArrayFilledData(
+      NewArrayEmpty newArray, int size, BasicBlock block, int elementSize) {
+    ConstNumber[] values = computeConstantArrayValues(newArray, block, size);
+    if (values == null) {
+      return null;
+    }
+    if (elementSize == 1) {
+      short[] result = new short[(size + 1) / 2];
+      for (int i = 0; i < size; i += 2) {
+        assert values[i].getIntValue() <= Constants.S8BIT_MAX
+            && values[i].getIntValue() >= Constants.S8BIT_MIN;
+        short value = (short) (values[i].getIntValue() & 0xFF);
+        if (i + 1 < size) {
+          value |= (short) ((values[i + 1].getIntValue() & 0xFF) << 8);
+        }
+        result[i / 2] = value;
+      }
+      return result;
+    }
+    assert elementSize == 2 || elementSize == 4 || elementSize == 8;
+    int shortsPerConstant = elementSize / 2;
+    short[] result = new short[size * shortsPerConstant];
+    for (int i = 0; i < size; i++) {
+      long value = values[i].getRawValue();
+      for (int part = 0; part < shortsPerConstant; part++) {
+        result[i * shortsPerConstant + part] = (short) ((value >> (16 * part)) & 0xFFFFL);
+      }
+    }
+    return result;
+  }
+
+  private ConstNumber[] computeConstantArrayValues(
+      NewArrayEmpty newArray, BasicBlock block, int size) {
+    if (size > MAX_FILL_ARRAY_SIZE) {
+      return null;
+    }
+    ConstNumber[] values = new ConstNumber[size];
+    int remaining = size;
+    Set<Instruction> users = newArray.outValue().uniqueUsers();
+    // We allow the array instantiations to cross block boundaries as long as it hasn't encountered
+    // an instruction instance that can throw an exception.
+    InstructionListIterator it = block.listIterator();
+    it.nextUntil(i -> i == newArray);
+    do {
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        // If we encounter an instruction that can throw an exception we need to bail out of the
+        // optimization so that we do not transform half-initialized arrays into fully initialized
+        // arrays on exceptional edges.
+        if (instruction.instructionInstanceCanThrow()) {
+          return null;
+        }
+        if (!users.contains(instruction)) {
+          continue;
+        }
+        // If the initialization sequence is broken by another use we cannot use a
+        // fill-array-data instruction.
+        if (!instruction.isArrayPut()) {
+          return null;
+        }
+        ArrayPut arrayPut = instruction.asArrayPut();
+        if (!arrayPut.source().isConstant()) {
+          return null;
+        }
+        assert arrayPut.index().isConstant();
+        int index = arrayPut.index().getConstInstruction().asConstNumber().getIntValue();
+        assert index >= 0 && index < values.length;
+        if (values[index] != null) {
+          return null;
+        }
+        ConstNumber value = arrayPut.source().getConstInstruction().asConstNumber();
+        values[index] = value;
+        --remaining;
+        if (remaining == 0) {
+          return values;
+        }
+      }
+      block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
+      it = block != null ? block.listIterator() : null;
+    } while (it != null);
+    return null;
+  }
+
+  private boolean isPrimitiveNewArrayWithConstantPositiveSize(Instruction instruction) {
+    if (!(instruction instanceof NewArrayEmpty)) {
+      return false;
+    }
+    NewArrayEmpty newArray = instruction.asNewArrayEmpty();
+    if (!newArray.size().isConstant()) {
+      return false;
+    }
+    int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+    if (size < 1) {
+      return false;
+    }
+    if (!newArray.type.isPrimitiveArrayType()) {
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Replace NewArrayEmpty followed by stores of constants to all entries with NewArrayEmpty
+   * and FillArrayData.
+   */
+  public void simplifyArrayConstruction(IRCode code) {
+    for (BasicBlock block : code.blocks) {
+      // Map from the array value to the number of array put instruction to remove for that value.
+      Map<Value, Integer> storesToRemoveForArray = new HashMap<>();
+      // First pass: identify candidates and insert fill array data instruction.
+      InstructionListIterator it = block.listIterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        if (!isPrimitiveNewArrayWithConstantPositiveSize(instruction)) {
+          continue;
+        }
+        NewArrayEmpty newArray = instruction.asNewArrayEmpty();
+        int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+        // If there is only one element it is typically smaller to generate the array put
+        // instruction instead of fill array data.
+        if (size == 1) {
+          continue;
+        }
+        int elementSize = newArray.type.elementSizeForPrimitiveArrayType();
+        short[] contents = computeArrayFilledData(newArray, size, block, elementSize);
+        if (contents == null) {
+          continue;
+        }
+        storesToRemoveForArray.put(newArray.outValue(), size);
+        int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
+        NewArrayFilledData fillArray = new NewArrayFilledData(
+            newArray.outValue(), elementSize, arraySize, contents);
+        it.add(fillArray);
+      }
+      // Second pass: remove all the array put instructions for the array for which we have
+      // inserted a fill array data instruction instead.
+      if (!storesToRemoveForArray.isEmpty()) {
+        do {
+          it = block.listIterator();
+          while (it.hasNext()) {
+            Instruction instruction = it.next();
+            if (instruction.isArrayPut()) {
+              Value array = instruction.asArrayPut().array();
+              Integer toRemoveCount = storesToRemoveForArray.get(array);
+              if (toRemoveCount != null && toRemoveCount > 0) {
+                storesToRemoveForArray.put(array, toRemoveCount - 1);
+                it.remove();
+              }
+            }
+          }
+          block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
+        } while (block != null);
+      }
+    }
+  }
+
+  private class ExpressionEquivalence extends Equivalence<Instruction> {
+
+    @Override
+    protected boolean doEquivalent(Instruction a, Instruction b) {
+      if (a.getClass() != b.getClass() || !a.identicalNonValueParts(b)) {
+        return false;
+      }
+      // For commutative binary operations any order of in-values are equal.
+      if (a.isBinop() && a.asBinop().isCommutative()) {
+        Value a0 = a.inValues().get(0);
+        Value a1 = a.inValues().get(1);
+        Value b0 = b.inValues().get(0);
+        Value b1 = b.inValues().get(1);
+        return (a0.equals(b0) && a1.equals(b1)) || (a0.equals(b1) && a1.equals(b0));
+      } else {
+        // Compare all in-values.
+        assert a.inValues().size() == b.inValues().size();
+        for (int i = 0; i < a.inValues().size(); i++) {
+          if (!a.inValues().get(i).equals(b.inValues().get(i))) {
+            return false;
+          }
+        }
+        return true;
+      }
+    }
+
+    @Override
+    protected int doHash(Instruction instruction) {
+      final int prime = 29;
+      int hash = instruction.getClass().hashCode();
+      if (instruction.isBinop()) {
+        Binop binop = instruction.asBinop();
+        Value in0 = instruction.inValues().get(0);
+        Value in1 = instruction.inValues().get(1);
+        if (binop.isCommutative()) {
+          hash += hash * prime + in0.hashCode() * in1.hashCode();
+        } else {
+          hash += hash * prime + in0.hashCode();
+          hash += hash * prime + in1.hashCode();
+        }
+        return hash;
+      } else {
+        for (Value value : instruction.inValues()) {
+          hash += hash * prime + value.hashCode();
+        }
+      }
+      return hash;
+    }
+  }
+
+  private boolean shareCatchHandlers(Instruction i0, Instruction i1) {
+    if (!i0.instructionTypeCanThrow()) {
+      assert !i1.instructionTypeCanThrow();
+      return true;
+    }
+    assert i1.instructionTypeCanThrow();
+    // TODO(sgjesse): This could be even better by checking for the exceptions thrown, e.g. div
+    // and rem only ever throw ArithmeticException.
+    CatchHandlers<BasicBlock> ch0 = i0.getBlock().getCatchHandlers();
+    CatchHandlers<BasicBlock> ch1 = i1.getBlock().getCatchHandlers();
+    return ch0.equals(ch1);
+  }
+
+  public void commonSubexpressionElimination(IRCode code) {
+    final ListMultimap<Wrapper<Instruction>, Value> instructionToValue = ArrayListMultimap.create();
+    final DominatorTree dominatorTree = new DominatorTree(code);
+    final ExpressionEquivalence equivalence = new ExpressionEquivalence();
+
+    for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
+      BasicBlock block = dominatorTree.getSortedBlocks()[i];
+      Iterator<Instruction> iterator = block.iterator();
+      while (iterator.hasNext()) {
+        Instruction instruction = iterator.next();
+        if (instruction.isBinop()
+            || instruction.isUnop()
+            || instruction.isInstanceOf()
+            || instruction.isCheckCast()) {
+          List<Value> candidates = instructionToValue.get(equivalence.wrap(instruction));
+          boolean eliminated = false;
+          if (candidates.size() > 0) {
+            for (Value candidate : candidates) {
+              if (dominatorTree.dominatedBy(block, candidate.definition.getBlock()) &&
+                  shareCatchHandlers(instruction, candidate.definition)) {
+                instruction.outValue().replaceUsers(candidate);
+                eliminated = true;
+                iterator.remove();
+                break;  // Don't try any more candidates.
+              }
+            }
+          }
+          if (!eliminated) {
+            instructionToValue.put(equivalence.wrap(instruction), instruction.outValue());
+          }
+        }
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+
+  public void simplifyIf(IRCode code) {
+    DominatorTree dominator = new DominatorTree(code);
+    code.clearMarks();
+    for (BasicBlock block : code.blocks) {
+      if (block.isMarked()) {
+        continue;
+      }
+      JumpInstruction exit = block.exit();
+      if (exit.isIf()) {
+        If theIf = exit.asIf();
+        List<Value> inValues = theIf.inValues();
+        int cond;
+        if (inValues.get(0).isConstant()
+            && (theIf.isZeroTest() || inValues.get(1).isConstant())) {
+          // Zero test with a constant of comparison between between two constants.
+          if (theIf.isZeroTest()) {
+            cond = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
+          } else {
+            int left = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
+            int right = inValues.get(1).getConstInstruction().asConstNumber().getIntValue();
+            cond = left - right;
+          }
+        } else if (inValues.get(0).hasValueRange()
+            && (theIf.isZeroTest() || inValues.get(1).hasValueRange())) {
+          // Zero test with a value range, or comparison between between two values,
+          // each with a value ranges.
+          if (theIf.isZeroTest()) {
+            if (inValues.get(0).isValueInRange(0)) {
+              // Zero in in the range - can't determine the comparison.
+              continue;
+            }
+            cond = Long.signum(inValues.get(0).getValueRange().getMin());
+          } else {
+            LongInterval leftRange = inValues.get(0).getValueRange();
+            LongInterval rightRange = inValues.get(1).getValueRange();
+            if (leftRange.overlapsWith(rightRange)) {
+              // Ranges overlap - can't determine the comparison.
+              continue;
+            }
+            // There is no overlap.
+            cond = Long.signum(leftRange.getMin() - rightRange.getMin());
+          }
+        } else {
+          continue;
+        }
+        BasicBlock target = theIf.targetFromCondition(cond);
+        BasicBlock deadTarget =
+            target == theIf.getTrueTarget() ? theIf.fallthroughBlock() : theIf.getTrueTarget();
+        List<BasicBlock> removedBlocks = block.unlink(deadTarget, dominator);
+        for (BasicBlock removedBlock : removedBlocks) {
+          if (!removedBlock.isMarked()) {
+            removedBlock.mark();
+          }
+        }
+        assert theIf == block.exit();
+        InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
+        iterator.previous();
+        iterator.replaceCurrentInstruction(new Goto());
+        assert block.exit().isGoto();
+        assert block.exit().asGoto().getTarget() == target;
+      }
+    }
+    code.removeMarkedBlocks();
+    assert code.isConsistentSSA();
+  }
+
+  public void removeUnneededCatchHandlers(IRCode code) {
+    DominatorTree dominator = new DominatorTree(code);
+    code.clearMarks();
+    for (BasicBlock block : code.blocks) {
+      if (block.hasCatchHandlers() && !block.canThrow()) {
+        CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
+        for (BasicBlock target : handlers.getUniqueTargets()) {
+          for (BasicBlock unlinked : block.unlink(target, dominator)) {
+            if (!unlinked.isMarked()) {
+              unlinked.mark();
+            }
+          }
+        }
+      }
+    }
+    code.removeMarkedBlocks();
+    assert code.isConsistentSSA();
+  }
+
+  public void rewriteLongCompareAndRequireNonNull(IRCode code, boolean canUseObjectsNonNull) {
+    InstructionIterator iterator = code.instructionIterator();
+
+    while (iterator.hasNext()) {
+      Instruction current = iterator.next();
+      if (current.isInvokeMethod()) {
+        DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
+        if (invokedMethod == dexItemFactory.longMethods.compare) {
+            List<Value> inValues = current.inValues();
+            assert inValues.size() == 2;
+            iterator.replaceCurrentInstruction(
+                new Cmp(NumericType.LONG, Bias.NONE, current.outValue(), inValues.get(0),
+                    inValues.get(1)));
+        } else if (!canUseObjectsNonNull
+            && invokedMethod == dexItemFactory.objectsMethods.requireNonNull) {
+          // Rewrite calls to Objects.requireNonNull(Object) because Javac 9 start to use it for
+          // synthesized null checks.
+          InvokeVirtual callToGetClass = new InvokeVirtual(dexItemFactory.objectMethods.getClass,
+              null, current.inValues());
+          if (current.outValue() != null) {
+            current.outValue().replaceUsers(current.inValues().get(0));
+            current.setOutValue(null);
+          }
+          iterator.replaceCurrentInstruction(callToGetClass);
+        }
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
new file mode 100644
index 0000000..d6023ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+public class DeadCodeRemover {
+
+  public static void removeDeadCode(
+      IRCode code, CodeRewriter codeRewriter, InternalOptions options) {
+    Queue<BasicBlock> worklist = new LinkedList<>();
+    worklist.addAll(code.blocks);
+    for (BasicBlock block = worklist.poll(); block != null; block = worklist.poll()) {
+      removeDeadInstructions(worklist, block, options);
+      removeDeadPhis(worklist, block, options);
+    }
+    codeRewriter.rewriteMoveResult(code);
+  }
+
+  private static void removeDeadPhis(
+      Queue<BasicBlock> worklist, BasicBlock block, InternalOptions options) {
+    List<Phi> toRemove = new ArrayList<>();
+    for (Phi phi : block.getPhis()) {
+      if (phi.isDead(options)) {
+        toRemove.add(phi);
+        for (Value operand : phi.getOperands()) {
+          operand.removePhiUser(phi);
+          if (operand.isPhi()) {
+            worklist.add(operand.asPhi().getBlock());
+          } else {
+            worklist.add(operand.definition.getBlock());
+          }
+        }
+      }
+    }
+    if (!toRemove.isEmpty()) {
+      List<Phi> newPhis = new ArrayList<>(block.getPhis().size() - toRemove.size());
+      int toRemoveIndex = 0;
+      List<Phi> phis = block.getPhis();
+      int i = 0;
+      for (; i < phis.size() && toRemoveIndex < toRemove.size(); i++) {
+        Phi phi = phis.get(i);
+        if (phi == toRemove.get(toRemoveIndex)) {
+          toRemoveIndex++;
+        } else {
+          newPhis.add(phi);
+        }
+      }
+      newPhis.addAll(phis.subList(i, phis.size()));
+      block.setPhis(newPhis);
+    }
+  }
+
+  private static void removeDeadInstructions(
+      Queue<BasicBlock> worklist, BasicBlock block, InternalOptions options) {
+    InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
+    while (iterator.hasPrevious()) {
+      Instruction current = iterator.previous();
+      // Remove unused invoke results.
+      if (current.isInvoke()
+          && current.outValue() != null
+          && current.outValue().numberOfAllUsers() == 0) {
+        current.setOutValue(null);
+      }
+      // Never remove instructions that can have side effects.
+      if (!current.canBeDeadCode(options)) {
+        continue;
+      }
+      Value outValue = current.outValue();
+      // Instructions with no out value cannot be dead code by the current definition
+      // (unused out value). They typically side-effect input values or deals with control-flow.
+      assert outValue != null;
+      if (!outValue.isDead(options)) {
+        continue;
+      }
+      for (Value inValue : current.inValues()) {
+        if (inValue.isPhi()) {
+          worklist.add(inValue.asPhi().getBlock());
+        } else {
+          worklist.add(inValue.definition.getBlock());
+        }
+      }
+      Value previousLocalValue = current.getPreviousLocalValue();
+      if (previousLocalValue != null) {
+        if (previousLocalValue.isPhi()) {
+          worklist.add(previousLocalValue.asPhi().getBlock());
+        } else {
+          worklist.add(previousLocalValue.definition.getBlock());
+        }
+      }
+      // All users will be removed for this instruction. Eagerly clear them so further inspection
+      // of this instruction during dead code elimination will terminate here.
+      outValue.clearUsers();
+      iterator.remove();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
new file mode 100644
index 0000000..bcf1a81
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -0,0 +1,251 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.CallGraph;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+public class Inliner {
+
+  private static final int INLINING_INSTRUCTION_LIMIT = 5;
+
+  protected final AppInfoWithSubtyping appInfo;
+  private final InternalOptions options;
+
+  // State for inlining methods which are known to be called twice.
+  public boolean applyDoubleInlining = false;
+  public final Set<DexEncodedMethod> doubleInlineCallers = Sets.newIdentityHashSet();
+  public final Set<DexEncodedMethod> doubleInlineSelectedTargets = Sets.newIdentityHashSet();
+  public final Map<DexEncodedMethod, DexEncodedMethod> doubleInlineeCandidates = new HashMap<>();
+
+  public Inliner(
+      AppInfoWithSubtyping appInfo, InternalOptions options) {
+    this.appInfo = appInfo;
+    this.options = options;
+  }
+
+  private InliningConstraint instructionAllowedForInlining(
+      DexEncodedMethod method, Instruction instruction) {
+    InliningConstraint result = instruction.inliningConstraint(appInfo, method.method.holder);
+    if ((result == InliningConstraint.NEVER) && instruction.isDebugInstruction()) {
+      return InliningConstraint.ALWAYS;
+    }
+    return result;
+  }
+
+  public InliningConstraint identifySimpleMethods(IRCode code, DexEncodedMethod method) {
+    DexCode dex = method.getCode().asDexCode();
+    // We have generated code for a method and we want to figure out whether the method is a
+    // candidate for inlining. The code is the final IR after optimizations.
+    if (dex.instructions.length > INLINING_INSTRUCTION_LIMIT) {
+      return InliningConstraint.NEVER;
+    }
+    InliningConstraint result = InliningConstraint.ALWAYS;
+    ListIterator<BasicBlock> iterator = code.listIterator();
+    assert iterator.hasNext();
+    BasicBlock block = iterator.next();
+    BasicBlock nextBlock;
+    do {
+      nextBlock = iterator.hasNext() ? iterator.next() : null;
+      InstructionListIterator it = block.listIterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        InliningConstraint state = instructionAllowedForInlining(method, instruction);
+        if (state == InliningConstraint.NEVER) {
+          return InliningConstraint.NEVER;
+        }
+        if (state.ordinal() < result.ordinal()) {
+          result = state;
+        }
+      }
+      block = nextBlock;
+    } while (block != null);
+    return result;
+  }
+
+  protected boolean hasInliningAccess(DexEncodedMethod method, DexEncodedMethod target) {
+    if (target.accessFlags.isPublic()) {
+      return true;
+    }
+    DexType methodHolder = method.method.getHolder();
+    DexType targetHolder = target.method.getHolder();
+    if (target.accessFlags.isPrivate()) {
+      return methodHolder == targetHolder;
+    }
+    if (target.accessFlags.isProtected() &&
+        methodHolder.isSubtypeOf(targetHolder, appInfo)) {
+      return true;
+    }
+    return methodHolder.isSamePackage(targetHolder);
+  }
+
+  public enum InliningConstraint {
+    // The ordinal values are important so please do not reorder.
+    NEVER,    // Never inline this.
+    PRIVATE,  // Only inline this into methods with same holder.
+    PACKAGE,  // Only inline this into methods with holders from same package.
+    ALWAYS,   // No restrictions for inlining this.
+  }
+
+  static public class InlineAction {
+    public final DexEncodedMethod target;
+    public final Invoke invoke;
+    public final boolean forceInline;
+
+    public InlineAction(
+        DexEncodedMethod target, Invoke invoke, boolean forceInline) {
+      this.target = target;
+      this.invoke = invoke;
+      this.forceInline = forceInline;
+    }
+
+    public InlineAction(DexEncodedMethod target, Invoke invoke) {
+      this.target = target;
+      this.invoke = invoke;
+      this.forceInline = target.getOptimizationInfo().forceInline();
+    }
+
+    public IRCode buildIR(ValueNumberGenerator generator, InternalOptions options) {
+      assert target.isProcessed();
+      assert target.getCode().isDexCode();
+      return target.buildIR(generator, options);
+    }
+  }
+
+ private int numberOfInstructions(IRCode code) {
+    int numOfInstructions = 0;
+    for (BasicBlock block : code.blocks) {
+      numOfInstructions += block.getInstructions().size();
+    }
+    return numOfInstructions;
+  }
+
+  private boolean legalConstructorInline(DexEncodedMethod method, IRCode code) {
+    // In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
+    // Newly Created Objects" it says:
+    //
+    // Before that method invokes another instance initialization method of myClass or its direct
+    // superclass on this, the only operation the method can perform on this is assigning fields
+    // declared within myClass.
+    //
+
+    // Allow inlining a constructor into a constructor, as the constructor code is expected to
+    // adhere to the VM specification.
+    if (method.accessFlags.isConstructor()) {
+      return true;
+    }
+
+    // Don't allow inlining a constructor into a non-constructor if the first use of the
+    // un-initialized object is not an argument of an invoke of <init>.
+    InstructionIterator iterator = code.instructionIterator();
+    Instruction instruction = iterator.next();
+    // A constructor always has the un-initialized object as the first argument.
+    assert instruction.isArgument();
+    Value unInitializedObject = instruction.outValue();
+    while (iterator.hasNext()) {
+      instruction = iterator.next();
+      if (instruction.inValues().contains(unInitializedObject)) {
+        return instruction.isInvokeDirect();
+      }
+    }
+    assert false : "Execution should never reach this point";
+    return false;
+  }
+
+  /// Computer the receiver value for the holder method.
+  private Value receiverValue(DexEncodedMethod method, IRCode code) {
+    // Ignore static methods.
+    if (method.accessFlags.isStatic()) {
+      return null;
+    }
+    // Find the outValue of the first argument instruction in the first block.
+    return code.collectArguments().get(0);
+  }
+
+  public void performInlining(DexEncodedMethod method, IRCode code, CallGraph callGraph) {
+    int instruction_allowance = 1500;
+    instruction_allowance -= numberOfInstructions(code);
+    if (instruction_allowance < 0) {
+      return;
+    }
+    Value receiver = receiverValue(method, code);
+    InliningOracle oracle = new InliningOracle(this, method, receiver, callGraph);
+
+    List<BasicBlock> blocksToRemove = new ArrayList<>();
+    ListIterator<BasicBlock> blockIterator = code.listIterator();
+    while (blockIterator.hasNext() && (instruction_allowance >= 0)) {
+      BasicBlock block = blockIterator.next();
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
+      InstructionListIterator iterator = block.listIterator();
+      while (iterator.hasNext() && (instruction_allowance >= 0)) {
+        Instruction current = iterator.next();
+        if (current.isInvokeMethod()) {
+          InvokeMethod invoke = current.asInvokeMethod();
+          InlineAction result = invoke.computeInlining(oracle);
+          if (result != null) {
+            IRCode inlinee = result.buildIR(code.valueNumberGenerator, options);
+            if (inlinee != null) {
+              // TODO(sgjesse): Get rid of this additional check by improved inlining.
+              if (block.hasCatchHandlers() && inlinee.getNormalExitBlock() == null) {
+                continue;
+              }
+              // Make sure constructor inlining is legal.
+              DexEncodedMethod target = appInfo.lookup(invoke.getType(), invoke.getInvokedMethod());
+              if (target.accessFlags.isConstructor() && !legalConstructorInline(method, inlinee)) {
+                continue;
+              }
+              if (invoke.isInvokeInterface()
+                  || invoke.isInvokeVirtual()
+                  || invoke.isInvokeDirect()) {
+                // If the invoke has a receiver but the declared method holder is different
+                // from the computed target holder, inlining is cancelled due to verification
+                // issues.
+                if (result.target.method.getHolder() != target.method.getHolder()) {
+                  continue;
+                }
+              }
+              // Inline the inlinee code in place of the invoke instruction
+              // Back up before the invoke instruction.
+              iterator.previous();
+              instruction_allowance -= numberOfInstructions(inlinee);
+              if (instruction_allowance >= 0 || result.forceInline) {
+                iterator.inlineInvoke(code, inlinee, blockIterator, blocksToRemove);
+              }
+              // If we inlined the invoke from a bridge method, it is no longer a bridge method.
+              if (method.accessFlags.isBridge()) {
+                method.accessFlags.unsetSynthetic();
+                method.accessFlags.unsetBridge();
+              }
+            }
+          }
+        }
+      }
+    }
+    oracle.finish();
+    code.removeBlocks(blocksToRemove);
+    assert code.isConsistentSSA();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningInfo.java
new file mode 100644
index 0000000..543aba4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningInfo.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import java.util.ArrayList;
+import java.util.List;
+
+
+// Class for collecting inlining information for one compiled DexEncodedMethod.
+public class InliningInfo {
+
+  static class Edge {
+    final Type type;
+    final DexMethod declared;
+    final Node inlinee;
+
+    public Edge(Type type, DexMethod declared, Node inlinee) {
+      this.type = type;
+      this.declared = declared;
+      this.inlinee = inlinee;
+    }
+
+    void appendOn(StringBuffer buffer) {
+      if (declared != null) {
+        buffer.append(declared.toSourceString());
+        buffer.append(' ');
+      }
+      inlinee.appendOn(buffer);
+    }
+  }
+
+  static abstract class Node {
+    abstract void appendOn(StringBuffer buffer);
+  }
+
+  static class Inlining extends Node {
+    final DexEncodedMethod target;
+
+    Inlining(DexEncodedMethod target) {
+      this.target = target;
+    }
+
+    void appendOn(StringBuffer buffer) {
+      buffer.append("<< INLINED");
+    }
+  }
+
+  static class NotInlining extends Node {
+
+    final String reason;
+
+    NotInlining(String reason) {
+      this.reason = reason;
+    }
+
+    public void appendOn(StringBuffer buffer) {
+      buffer.append("-- no inlining: ");
+      buffer.append(reason);
+    }
+  }
+
+  final DexEncodedMethod method;
+  final List<Edge> edges = new ArrayList<>();
+
+  public InliningInfo(DexEncodedMethod method) {
+    this.method = method;
+  }
+
+  public void include(Type type, DexEncodedMethod target) {
+    edges.add(new Edge(type, target.method, new Inlining(target)));
+  }
+
+  public void exclude(InvokeMethod invoke, String reason) {
+    edges.add(new Edge(invoke.getType(), invoke.getInvokedMethod(), new NotInlining(reason)));
+  }
+
+  public String toString() {
+    StringBuffer buffer = new StringBuffer(method.method.toSourceString());
+    buffer.append(" {\n");
+    for (Edge edge : edges) {
+      buffer.append("  ");
+      edge.appendOn(buffer);
+      buffer.append(".\n");
+    }
+    buffer.append("}\n");
+    return buffer.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
new file mode 100644
index 0000000..aa34226
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -0,0 +1,282 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokePolymorphic;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeSuper;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.CallGraph;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
+import com.android.tools.r8.logging.Log;
+
+/**
+ *  The InliningOracle contains information needed for when inlining
+ *  other methods into @method.
+ */
+public class InliningOracle {
+  final Inliner inliner;
+  final DexEncodedMethod method;
+  final Value receiver;
+  final CallGraph callGraph;
+  final private InliningInfo info;
+
+  public InliningOracle(
+      Inliner inliner,
+      DexEncodedMethod method,
+      Value receiver,
+      CallGraph callGraph) {
+    this.inliner = inliner;
+    this.method = method;
+    this.receiver = receiver;
+    this.callGraph = callGraph;
+    info = Log.ENABLED ? new InliningInfo(method) : null;
+  }
+
+  public void finish() {
+    if (Log.ENABLED) {
+      System.out.println(info.toString());
+    }
+  }
+
+  DexEncodedMethod validateCandidate(InvokeMethod invoke) {
+    DexEncodedMethod candidate = invoke.computeSingleTarget(inliner.appInfo);
+    if ((candidate == null)
+        || (candidate.getCode() == null)
+        || inliner.appInfo.definitionFor(candidate.method.getHolder()).isLibraryClass()) {
+      if (info != null) {
+        info.exclude(invoke, "No inlinee");
+      }
+      return null;
+    }
+    if (method == candidate) {
+      // Cannot handle recursive inlining at this point.
+      // Bridge methods should never have recursive calls.
+      assert !candidate.getOptimizationInfo().forceInline();
+      return null;
+    }
+    if (candidate.accessFlags.isSynchronized()) {
+      // Don't inline if target is synchronized.
+      if (info != null) {
+        info.exclude(invoke, "Inlinee candidate is synchronized");
+      }
+      return null;
+    }
+    if (!inliner.hasInliningAccess(method, candidate)) {
+      if (info != null) {
+        info.exclude(invoke, "Inlinee candidate does not have right access flags");
+      }
+      return null;
+    }
+    return candidate;
+  }
+
+  private DexEncodedMethod doubleInlining(DexEncodedMethod candidate) {
+    if (!inliner.applyDoubleInlining) {
+      if (inliner.doubleInlineeCandidates.containsKey(candidate)) {
+        // Both calls can be inlined.
+        inliner.doubleInlineCallers.add(inliner.doubleInlineeCandidates.get(candidate));
+        inliner.doubleInlineCallers.add(method);
+        inliner.doubleInlineSelectedTargets.add(candidate);
+      } else {
+        // First call can be inlined.
+        inliner.doubleInlineeCandidates.put(candidate, method);
+      }
+      // Just preparing for double inlining.
+      return null;
+    } else {
+      // Don't perform the actual inlining if this was not selected.
+      if (!inliner.doubleInlineSelectedTargets.contains(candidate)) {
+        return null;
+      }
+    }
+    return candidate;
+  }
+
+  public InlineAction computeForInvokeWithReceiver(InvokeMethod invoke) {
+    boolean receiverIsNeverNull = invoke.arguments().get(0).isNeverNull();
+    if (!receiverIsNeverNull) {
+      if (info != null) {
+        info.exclude(invoke, "receiver for candidate can be null");
+      }
+      return null;
+    }
+    DexEncodedMethod target = invoke.computeSingleTarget(inliner.appInfo);
+    if (target == null) {
+      // Abort inlining attempt if we cannot find single target.
+      if (info != null) {
+        info.exclude(invoke, "could not find single target");
+      }
+      return null;
+    }
+
+    boolean fromBridgeMethod = target.getOptimizationInfo().forceInline();
+
+    if (target == method) {
+      // Bridge methods should never have recursive calls.
+      assert !fromBridgeMethod;
+      return null;
+    }
+
+    if (target.getCode() == null) {
+      return null;
+    }
+
+    DexClass holder = inliner.appInfo.definitionFor(target.method.getHolder());
+    if (holder.isInterface()) {
+      // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at runtime.
+      if (info != null) {
+        info.exclude(invoke, "Do not inline target if method holder is an interface class");
+      }
+      return null;
+    }
+
+    if (holder.isLibraryClass()) {
+      // Library functions should not be inlined.
+      return null;
+    }
+
+    // Don't inline if target is synchronized.
+    if (target.accessFlags.isSynchronized()) {
+      if (info != null) {
+        info.exclude(invoke, "target is synchronized");
+      }
+      return null;
+    }
+
+    boolean doubleInlineTarget =
+        callGraph.hasDoubleCallSite(target)
+            && target.getCode().isDexCode()
+            && (target.getCode().asDexCode().instructions.length
+                <= 10); // 10 is found from measuring.
+    // Determine if this should be inlined no matter how big it is.
+    boolean forceInline =
+        fromBridgeMethod | callGraph.hasSingleCallSite(target) | doubleInlineTarget;
+    if (!target.isInliningCandidate(method, forceInline)) {
+      // Abort inlining attempt if the single target is not an inlining candidate.
+      if (info != null) {
+        info.exclude(invoke, "target is not identified for inlining");
+      }
+      return null;
+    }
+
+    // Abort inlining attempt if method -> target access is not right.
+    if (!inliner.hasInliningAccess(method, target)) {
+      if (info != null) {
+        info.exclude(invoke, "target does not have right access");
+      }
+      return null;
+    }
+
+    // Attempt to inline a candidate that is only called twice.
+    if (doubleInlineTarget && (doubleInlining(target) == null)) {
+      if (info != null) {
+        info.exclude(invoke, "target is not ready for double inlining");
+      }
+      return null;
+    }
+
+    if (info != null) {
+      info.include(invoke.getType(), target);
+    }
+    return new InlineAction(target, invoke, forceInline);
+  }
+
+  public InlineAction computeForInvokeVirtual(InvokeVirtual invoke) {
+    return computeForInvokeWithReceiver(invoke);
+  }
+
+  public InlineAction computeForInvokeInterface(InvokeInterface invoke) {
+    return computeForInvokeWithReceiver(invoke);
+  }
+
+  public InlineAction computeForInvokeDirect(InvokeDirect invoke) {
+    return computeForInvokeWithReceiver(invoke);
+  }
+
+  private boolean canInlineStaticInvoke(DexEncodedMethod method, DexEncodedMethod target) {
+    // Only proceed with inlining a static invoke if:
+    // - the holder for the target equals the holder for the method, or
+    // - there is no class initializer.
+    DexType targetHolder = target.method.getHolder();
+    if (method.method.getHolder() == targetHolder) {
+      return true;
+    }
+    DexClass clazz = inliner.appInfo.definitionFor(targetHolder);
+    return (clazz != null) && (clazz.getClassInitializer(inliner.appInfo.dexItemFactory) == null);
+  }
+
+  public InlineAction computeForInvokeStatic(InvokeStatic invoke) {
+    DexEncodedMethod candidate = validateCandidate(invoke);
+    if (candidate == null) {
+      return null;
+    }
+    boolean fromBridgeMethod = candidate.getOptimizationInfo().forceInline();
+    boolean doubleInlineTarget =
+        callGraph.hasDoubleCallSite(candidate)
+            && candidate.getCode().isDexCode()
+            && (candidate.getCode().asDexCode().instructions.length
+                <= 10); // 10 is found from measuring.
+    // Determine if this should be inlined no matter how big it is.
+    boolean forceInline =
+        fromBridgeMethod | callGraph.hasSingleCallSite(candidate) | doubleInlineTarget;
+    if (!candidate.isInliningCandidate(method, forceInline)) {
+      // Abort inlining attempt if the single target is not an inlining candidate.
+      if (info != null) {
+        info.exclude(invoke, "target is not identified for inlining");
+      }
+      return null;
+    }
+
+    // Abort inlining attempt if we can not guarantee class for static target has been initialized.
+    if (!canInlineStaticInvoke(method, candidate)) {
+      if (info != null) {
+        info.exclude(invoke, "target is static but we cannot guarantee class has been initialized");
+      }
+      return null;
+    }
+
+    // Attempt to inline a candidate that is only called twice.
+    if (doubleInlineTarget && (doubleInlining(candidate) == null)) {
+      if (info != null) {
+        info.exclude(invoke, "target is not ready for double inlining");
+      }
+      return null;
+    }
+
+    if (info != null) {
+      info.include(invoke.getType(), candidate);
+    }
+    return new InlineAction(candidate, invoke);
+  }
+
+  public InlineAction computeForInvokeSuper(InvokeSuper invoke) {
+    DexEncodedMethod candidate = validateCandidate(invoke);
+    if (candidate == null) {
+      if (info != null) {
+        info.exclude(invoke, "not a valid inlining target");
+      }
+      return null;
+    }
+    if (info != null) {
+      info.include(invoke.getType(), candidate);
+    }
+    return new InlineAction(candidate, invoke);
+  }
+
+  public InlineAction computeForInvokePolymorpic(InvokePolymorphic invoke) {
+    // TODO: No inlining of invoke polymorphic for now.
+    if (info != null) {
+      info.exclude(invoke, "inlining through invoke signature polymorpic is not supported");
+    }
+    return null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java b/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
new file mode 100644
index 0000000..f9c0b30
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InstructionEquivalence.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.base.Equivalence;
+
+public class InstructionEquivalence extends Equivalence<Instruction> {
+  private final RegisterAllocator allocator;
+
+  InstructionEquivalence(RegisterAllocator allocator) {
+    this.allocator = allocator;
+  }
+
+  @Override
+  protected boolean doEquivalent(Instruction a, Instruction b) {
+    return a.identicalAfterRegisterAllocation(b, allocator);
+  }
+
+  @Override
+  protected int doHash(Instruction instruction) {
+    int hash = 0;
+    if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
+      hash += allocator.getRegisterForValue(instruction.outValue(), instruction.getNumber());
+    }
+    for (Value inValue : instruction.inValues()) {
+      hash = hash<< 4;
+      if (inValue.needsRegister()) {
+        hash += allocator.getRegisterForValue(inValue, instruction.getNumber());
+      }
+    }
+    return hash;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
new file mode 100644
index 0000000..5842609
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -0,0 +1,250 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.ProguardMemberRule;
+
+public class MemberValuePropagation {
+
+  private final AppInfo appInfo;
+  private final AppInfoWithLiveness liveSet;
+
+  private enum RuleType {
+    NONE,
+    ASSUME_NO_SIDE_EFFECTS,
+    ASSUME_VALUES
+  }
+
+  private class ProguardMemberRuleLookup {
+
+    final RuleType type;
+    final ProguardMemberRule rule;
+
+    ProguardMemberRuleLookup(RuleType type, ProguardMemberRule rule) {
+      this.type = type;
+      this.rule = rule;
+    }
+  }
+
+  public MemberValuePropagation(AppInfo appInfo) {
+    this.appInfo = appInfo;
+    this.liveSet = appInfo.withLiveness();
+  }
+
+  private ProguardMemberRuleLookup lookupMemberRule(DexItem item) {
+    if (liveSet == null) {
+      return null;
+    }
+    ProguardMemberRule rule = liveSet.noSideEffects.get(item);
+    if (rule != null) {
+      return new ProguardMemberRuleLookup(RuleType.ASSUME_NO_SIDE_EFFECTS, rule);
+    }
+    rule = liveSet.assumedValues.get(item);
+    if (rule != null) {
+      return new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
+    }
+    return null;
+  }
+
+  private Instruction constantReplacementFromProguardRule(
+      ProguardMemberRule rule, IRCode code, Instruction instruction) {
+    // Check if this value can be assumed constant.
+    Instruction replacement = null;
+    MoveType moveType = instruction.outValue().outType();
+    if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
+      assert moveType != MoveType.OBJECT;
+      Value value = new Value(
+          code.valueNumberGenerator.next(), -1, moveType, instruction.getDebugInfo());
+      replacement = new ConstNumber(
+          ConstType.fromMoveType(moveType), value, rule.getReturnValue().getSingleValue());
+    }
+    if (replacement == null &&
+        rule != null && rule.hasReturnValue() && rule.getReturnValue().isField()) {
+      DexField field = rule.getReturnValue().getField();
+      DexEncodedField staticField = appInfo.lookupStaticTarget(field.clazz, field);
+      if (staticField != null) {
+        Value value = new Value(
+            code.valueNumberGenerator.next(), -1, moveType, instruction.getDebugInfo());
+        replacement = staticField.staticValue.asConstInstruction(false, value);
+      } else {
+        throw new CompilationError(field.clazz.toSourceString() + "." + field.name.toString() +
+            " used in assumevalues rule does not exist.");
+      }
+    }
+    return replacement;
+  }
+
+  private void setValueRangeFromProguardRule(ProguardMemberRule rule, Value value) {
+    if (rule.hasReturnValue() && rule.getReturnValue().isValueRange()) {
+      assert !rule.getReturnValue().isSingleValue();
+      value.setValueRange(rule.getReturnValue().getValueRange());
+    }
+  }
+
+  private void replaceInstructionFromProguardRule(RuleType ruleType, InstructionIterator iterator,
+      Instruction current, Instruction replacement) {
+    if (ruleType == RuleType.ASSUME_NO_SIDE_EFFECTS) {
+      iterator.replaceCurrentInstruction(replacement);
+    } else {
+      if (current.outValue() != null) {
+        assert replacement.outValue() != null;
+        current.outValue().replaceUsers(replacement.outValue());
+      }
+      iterator.add(replacement);
+    }
+  }
+
+  /**
+   * Replace invoke targets and field accesses with constant values where possible.
+   * <p>
+   * Also assigns value ranges to values where possible.
+   */
+  public void rewriteWithConstantValues(IRCode code) {
+    InstructionIterator iterator = code.instructionIterator();
+    while (iterator.hasNext()) {
+      Instruction current = iterator.next();
+      if (current.isInvokeMethod()) {
+        InvokeMethod invoke = current.asInvokeMethod();
+        DexMethod invokedMethod = invoke.getInvokedMethod();
+        DexType invokedHolder = invokedMethod.getHolder();
+        if (!invokedHolder.isClassType()) {
+          continue;
+        }
+        DexEncodedMethod definition = appInfo.lookup(invoke.getType(), invokedMethod);
+
+        // Process invokes marked as having no side effects.
+        boolean invokeReplaced = false;
+        ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
+        if (lookup != null) {
+          if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
+              && (invoke.outValue() == null || invoke.outValue().numberOfAllUsers() == 0)) {
+            iterator.remove();
+            invokeReplaced = true;
+          } else {
+            // Check to see if a constant value can be assumed.
+            Instruction replacement =
+                constantReplacementFromProguardRule(lookup.rule, code, invoke);
+            if (replacement != null) {
+              replaceInstructionFromProguardRule(lookup.type, iterator, current, replacement);
+              invokeReplaced = true;
+            } else {
+              // Check to see if a value range can be assumed.
+              setValueRangeFromProguardRule(lookup.rule, current.outValue());
+            }
+          }
+        }
+
+        // If no Proguard rule could replace the instruction check for knowledge about the
+        // return value.
+        if (!invokeReplaced && liveSet != null && invoke.outValue() != null) {
+          DexEncodedMethod target = invoke.computeSingleTarget(liveSet);
+          if (target != null) {
+            if (target.getOptimizationInfo().neverReturnsNull()) {
+              invoke.outValue().markNeverNull();
+            }
+            if (target.getOptimizationInfo().returnsConstant()) {
+              long constant = target.getOptimizationInfo().getReturnedConstant();
+              MoveType moveType = invoke.outType();
+              if (moveType == MoveType.OBJECT) {
+                assert constant == 0;
+                moveType = MoveType.SINGLE;
+              }
+              Value value = new Value(code.valueNumberGenerator.next(), -1, moveType, null);
+              // TODO(ager): Attempt to get a more precise const type from the method analysis?
+              Instruction knownConstReturn =
+                  new ConstNumber(ConstType.fromMoveType(moveType), value, constant);
+              invoke.outValue().replaceUsers(value);
+              iterator.add(knownConstReturn);
+            }
+          }
+        }
+      } else if (current.isInstancePut()) {
+        InstancePut instancePut = current.asInstancePut();
+        DexField field = instancePut.getField();
+        DexEncodedField target = appInfo.lookupInstanceTarget(field.getHolder(), field);
+        if (target != null) {
+          // Remove writes to dead (i.e. never read) fields.
+          if (!isFieldRead(target, false) && instancePut.object().isNeverNull()) {
+            iterator.remove();
+          }
+        }
+      } else if (current.isStaticGet()) {
+        StaticGet staticGet = current.asStaticGet();
+        DexField field = staticGet.getField();
+        Instruction replacement = null;
+        DexEncodedField target = appInfo.lookupStaticTarget(field.getHolder(), field);
+        ProguardMemberRuleLookup lookup = null;
+        if (target != null) {
+          // Check if a this value is known const.
+          replacement = target.valueAsConstInstruction(appInfo, staticGet.dest());
+          if (replacement == null) {
+            lookup = lookupMemberRule(target);
+            if (lookup != null) {
+              replacement = constantReplacementFromProguardRule(lookup.rule, code, staticGet);
+            }
+          }
+          if (replacement == null) {
+            // If no const replacement was found, at least store the range information.
+            if (lookup != null) {
+              setValueRangeFromProguardRule(lookup.rule, staticGet.dest());
+            }
+          }
+          if (replacement != null) {
+            // Ignore assumenosideeffects for fields.
+            if (lookup != null && lookup.type == RuleType.ASSUME_VALUES) {
+              replaceInstructionFromProguardRule(lookup.type, iterator, current, replacement);
+            } else {
+              iterator.replaceCurrentInstruction(replacement);
+            }
+          }
+        }
+      } else if (current.isStaticPut()) {
+        StaticPut staticPut = current.asStaticPut();
+        DexField field = staticPut.getField();
+        DexEncodedField target = appInfo.lookupStaticTarget(field.getHolder(), field);
+        if (target != null) {
+          // Remove writes to dead (i.e. never read) fields.
+          if (!isFieldRead(target, true)) {
+            iterator.remove();
+          }
+        }
+      }
+    }
+    assert code.isConsistentSSA();
+  }
+
+  private boolean isFieldRead(DexEncodedField field, boolean isStatic) {
+    // Without live set information we cannot tell and assume true.
+    if (liveSet == null
+        || isStatic && liveSet.staticFieldsRead.contains(field.field)
+        || !isStatic && liveSet.instanceFieldsRead.contains(field.field)
+        || liveSet.pinnedItems.contains(field)) {
+      return true;
+    }
+    // For library classes we don't know whether a field is read.
+    DexClass holder = appInfo.definitionFor(field.field.clazz);
+    return holder == null || holder.isLibraryClass();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
new file mode 100644
index 0000000..0d93561
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MoveEliminator.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import java.util.HashSet;
+
+class MoveEliminator {
+  private final HashSet<Move> activeMoves = new HashSet<>();
+  private final RegisterAllocator allocator;
+
+  MoveEliminator(RegisterAllocator allocator) {
+    this.allocator = allocator;
+  }
+
+  public boolean shouldBeEliminated(Instruction instruction) {
+    if (instruction.isMove()) {
+      Move move = instruction.asMove();
+      int moveSrcRegister = allocator.getRegisterForValue(move.src(), move.getNumber());
+      int moveDstRegister = allocator.getRegisterForValue(move.dest(), move.getNumber());
+      if (moveSrcRegister == moveDstRegister) {
+        return true;
+      }
+      for (Move activeMove : activeMoves) {
+        int activeMoveSrcRegister =
+            allocator.getRegisterForValue(activeMove.src(), activeMove.getNumber());
+        int activeMoveDstRegister =
+            allocator.getRegisterForValue(activeMove.dest(), activeMove.getNumber());
+        if (activeMoveSrcRegister == moveSrcRegister
+            && activeMoveDstRegister == moveDstRegister) {
+          return true;
+        }
+      }
+    }
+    if (instruction.outValue() != null && instruction.outValue().needsRegister()) {
+      Value defined = instruction.outValue();
+      int definedRegister = allocator.getRegisterForValue(defined, instruction.getNumber());
+      activeMoves.removeIf((m) -> {
+        int moveSrcRegister = allocator.getRegisterForValue(m.inValues().get(0), m.getNumber());
+        int moveDstRegister = allocator.getRegisterForValue(m.outValue(), m.getNumber());
+        for (int i = 0; i < defined.requiredRegisters(); i++) {
+          if (definedRegister + i == moveDstRegister || definedRegister + i == moveSrcRegister) {
+            return true;
+          }
+        }
+        return false;
+      });
+    }
+    if (instruction.isMove()) {
+      activeMoves.add(instruction.asMove());
+    }
+    return false;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
new file mode 100644
index 0000000..80a5975
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -0,0 +1,1046 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlock.ThrowingInfo;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.Div;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Rem;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class Outliner {
+
+  private final InternalOptions options;
+  private final Map<Outline, List<DexEncodedMethod>> candidates = new HashMap<>();
+  private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
+  private final Set<DexEncodedMethod> methodsSelectedForOutlining = Sets.newIdentityHashSet();
+
+  static final int MAX_IN_SIZE = 5;  // Avoid using ranged calls for outlined code.
+
+  final private AppInfo appInfo;
+  final private DexItemFactory dexItemFactory;
+
+  // Representation of an outline.
+  // This includes the instructions in the outline, and a map from the arguments of this outline
+  // to the values in the instructions.
+  //
+  // E.g. an outline of two StringBuilder.append(String) calls could look like this:
+  //
+  //  InvokeVirtual       { v5 v6 } Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+  //  InvokeVirtual       { v5 v9 } Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+  //  ReturnVoid
+  //
+  // It takes three arguments
+  //
+  //   v5, v6, v9
+  //
+  // and the argument map is a list mapping the arguments to the in-values of all instructions in
+  // the order they are encountered, in this case:
+  //
+  //   [0, 1, 0, 2]
+  //
+  // The actual value numbers (in this example v5, v6, v9 are "arbitrary", as the instruction in
+  // the outline are taken from the block where they are collected as candidates. The comparison
+  // of two outlines rely on the instructions and the argument mapping *not* the concrete values.
+  public class Outline implements Comparable<Outline> {
+
+    final List<Value> arguments;
+    final List<DexType> argumentTypes;
+    final List<Integer> argumentMap;
+    final List<Instruction> templateInstructions = new ArrayList<>();
+    final public DexType returnType;
+
+    private DexProto proto;
+
+    // Build an outline over the instructions [start, end[.
+    // The arguments are the arguments to pass to an outline of these instructions.
+    Outline(BasicBlock block, List<Value> arguments, List<DexType> argumentTypes,
+        List<Integer> argumentMap, DexType returnType, int start, int end) {
+      this.arguments = arguments;
+      this.argumentTypes = argumentTypes;
+      this.argumentMap = argumentMap;
+      this.returnType = returnType;
+
+      List<Instruction> instructions = block.getInstructions();
+      for (int i = start; i < end; i++) {
+        Instruction current = instructions.get(i);
+        if (current.isInvoke() || current.isNewInstance() || current.isArithmeticBinop()) {
+          templateInstructions.add(current);
+        } else if (current.isConstInstruction()) {
+          // Don't include const instructions in the template.
+        } else {
+          assert false : "Unexpected type of instruction in outlining template.";
+        }
+      }
+    }
+
+    int argumentCount() {
+      return arguments.size();
+    }
+
+    DexProto buildProto() {
+      if (proto == null) {
+        DexType[] argumentTypesArray = argumentTypes.toArray(new DexType[argumentTypes.size()]);
+        proto = dexItemFactory.createProto(returnType, argumentTypesArray);
+      }
+      return proto;
+    }
+
+    // Build the DexMethod for this outline.
+    DexMethod buildMethod(DexType clazz, DexString name) {
+      return dexItemFactory.createMethod(clazz, buildProto(), name);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (!(other instanceof Outline)) {
+        return false;
+      }
+      List<Instruction> instructions0 = this.templateInstructions;
+      List<Instruction> instructions1 = ((Outline) other).templateInstructions;
+      if (instructions0.size() != instructions1.size()) {
+        return false;
+      }
+      for (int i = 0; i < instructions0.size(); i++) {
+        Instruction i0 = instructions0.get(i);
+        Instruction i1 = instructions1.get(i);
+        if (i0.getClass() != i1.getClass() || !i0.identicalNonValueParts(i1)) {
+          return false;
+        }
+        if ((i0.outValue() != null) != (i1.outValue() != null)) {
+          return false;
+        }
+      }
+      return argumentMap.equals(((Outline) other).argumentMap)
+          && returnType == ((Outline) other).returnType;
+    }
+
+    @Override
+    public int hashCode() {
+      final int MAX_HASH_INSTRUCTIONS = 5;
+
+      int hash = templateInstructions.size();
+      int hashPart = 0;
+      for (int i = 0; i < templateInstructions.size() && i < MAX_HASH_INSTRUCTIONS; i++) {
+        Instruction instruction = templateInstructions.get(i);
+        if (instruction.isInvokeMethod()) {
+          hashPart = hashPart << 4;
+          hashPart += instruction.asInvokeMethod().getInvokedMethod().hashCode();
+        }
+        hash = hash * 3 + hashPart;
+      }
+      return hash;
+    }
+
+    @Override
+    public int compareTo(Outline other) {
+      if (this == other) {
+        return 0;
+      }
+      // First compare the proto.
+      int result;
+      result = buildProto().slowCompareTo(other.buildProto());
+      if (result != 0) {
+        assert !equals(other);
+        return result;
+      }
+      assert argumentCount() == other.argumentCount();
+      // Then compare the instructions (non value part).
+      List<Instruction> instructions0 = templateInstructions;
+      List<Instruction> instructions1 = other.templateInstructions;
+      result = instructions0.size() - instructions1.size();
+      if (result != 0) {
+        assert !equals(other);
+        return result;
+      }
+      for (int i = 0; i < instructions0.size(); i++) {
+        Instruction i0 = instructions0.get(i);
+        Instruction i1 = instructions1.get(i);
+        result = i0.getInstructionName().compareTo(i1.getInstructionName());
+        if (result != 0) {
+          assert !equals(other);
+          return result;
+        }
+        result = i0.inValues().size() - i1.inValues().size();
+        if (result != 0) {
+          assert !equals(other);
+          return result;
+        }
+        result = (i0.outValue() != null ? 1 : 0) - (i1.outValue() != null ? 1 : 0);
+        if (result != 0) {
+          assert !equals(other);
+          return result;
+        }
+        result = i0.compareNonValueParts(i1);
+        if (result != 0) {
+          assert !equals(other);
+          return result;
+        }
+      }
+      // Finally compare the value part.
+      result = argumentMap.size() - other.argumentMap.size();
+      if (result != 0) {
+        assert !equals(other);
+        return result;
+      }
+      for (int i = 0; i < argumentMap.size(); i++) {
+        result = argumentMap.get(i) - other.argumentMap.get(i);
+        if (result != 0) {
+          assert !equals(other);
+          return result;
+        }
+      }
+      assert equals(other);
+      return 0;
+    }
+
+    @Override
+    public String toString() {
+      // The printing of the code for an outline maps the value numbers to the arguments numbers.
+      int outRegisterNumber = arguments.size();
+      StringBuilder builder = new StringBuilder();
+      builder.append(returnType);
+      builder.append(" anOutline");
+      StringUtils.append(builder, argumentTypes, ", ", BraceType.PARENS);
+      builder.append("\n");
+      int argumentMapIndex = 0;
+      for (Instruction instruction : templateInstructions) {
+        String name = instruction.getInstructionName();
+        StringUtils.appendRightPadded(builder, name, 20);
+        if (instruction.outValue() != null) {
+          builder.append("v" + outRegisterNumber);
+          builder.append(" <- ");
+        }
+        for (int i = 0; i < instruction.inValues().size(); i++) {
+          builder.append(i > 0 ? ", " : "");
+          builder.append("v");
+          int index = argumentMap.get(argumentMapIndex++);
+          if (index >= 0) {
+            builder.append(index);
+          } else {
+            builder.append(outRegisterNumber);
+          }
+        }
+        if (instruction.isInvoke()) {
+          builder.append("; method: ");
+          builder.append(instruction.asInvokeMethod().getInvokedMethod().toSourceString());
+        }
+        if (instruction.isNewInstance()) {
+          builder.append(instruction.asNewInstance().clazz.toSourceString());
+        }
+        builder.append("\n");
+      }
+      if (returnType == dexItemFactory.voidType) {
+        builder.append("Return-Void");
+      } else {
+        StringUtils.appendRightPadded(builder, "Return", 20);
+        builder.append("v" + (outRegisterNumber));
+      }
+      builder.append("\n");
+      builder.append(argumentMap);
+      return builder.toString();
+    }
+  }
+
+  // Spot the outline opportunities in a basic block.
+  // This is the superclass for both collection candidates and actually replacing code.
+  // TODO(sgjesse): Collect more information in the candidate collection and reuse that for
+  // replacing.
+  abstract private class OutlineSpotter {
+
+    final DexEncodedMethod method;
+    final BasicBlock block;
+    final List<Instruction> instructions;
+
+    int start;
+    int index;
+    int actualInstructions;
+    List<Value> arguments;
+    List<DexType> argumentTypes;
+    List<Integer> argumentsMap;
+    int argumentRegisters;
+    DexType returnType;
+    Value returnValue;
+    int returnValueUsersLeft;
+    int pendingNewInstanceIndex = -1;
+
+    OutlineSpotter(DexEncodedMethod method, BasicBlock block) {
+      this.method = method;
+      this.block = block;
+      this.instructions = block.getInstructions();
+      reset(0);
+    }
+
+    protected void process() {
+      List<Instruction> instructions = block.getInstructions();
+      while (index < instructions.size()) {
+        Instruction current = instructions.get(index);
+        processInstruction(current);
+      }
+    }
+
+    // Get int in-values for an instruction. For commutative binary operations using the current
+    // return value (active out-value) make sure that that value is the left value.
+    protected List<Value> orderedInValues(Instruction instruction, Value returnValue) {
+      List<Value> inValues = instruction.inValues();
+      if (instruction.isBinop() && instruction.asBinop().isCommutative()) {
+        if (inValues.get(1) == returnValue) {
+          Value tmp = inValues.get(0);
+          inValues.set(0, inValues.get(1));
+          inValues.set(1, tmp);
+        }
+      }
+      return inValues;
+    }
+
+    // Process instruction. Returns true if an outline candidate was found.
+    private void processInstruction(Instruction instruction) {
+      // Figure out whether to include the instruction.
+      boolean include = false;
+      int instructionIncrement = 1;
+      if (instruction.isConstInstruction()) {
+        if (index == start) {
+          // Restart for first const instruction.
+          reset(index + 1);
+          return;
+        } else {
+          // Otherwise include const instructions.
+          include = true;
+          instructionIncrement = 0;
+        }
+      } else {
+        include = canIncludeInstruction(instruction);
+      }
+
+      if (include) {
+        actualInstructions += instructionIncrement;
+
+        // Add this instruction.
+        includeInstruction(instruction);
+        // Check if this instruction ends the outline.
+        if (actualInstructions >= options.outline.maxSize) {
+          candidate(start, index + 1);
+        } else {
+          index++;
+        }
+      } else if (index > start) {
+        // Do not add this instruction, candidate ends with previous instruction.
+        candidate(start, index);
+      } else {
+        // Restart search from next instruction.
+        reset(index + 1);
+      }
+    }
+
+    // Check if the current instruction can be included in the outline.
+    private boolean canIncludeInstruction(Instruction instruction) {
+      // Find the users of the active out-value (potential return value).
+      int returnValueUsersLeftIfIncluded = returnValueUsersLeft;
+      if (returnValue != null) {
+        for (Value value : instruction.inValues()) {
+          if (value == returnValue) {
+            returnValueUsersLeftIfIncluded--;
+          }
+        }
+      }
+
+      // If this instruction has an out-value, but the previous one is still active end the
+      // outline.
+      if (instruction.outValue() != null && returnValueUsersLeftIfIncluded > 0) {
+        return false;
+      }
+
+      // Allow all new-instance instructions in a outline.
+      if (instruction.isNewInstance()) {
+        if (instruction.outValue().numberOfAllUsers() > 0) {
+          // Track the new-instance value to make sure the <init> call is part of the outline.
+          pendingNewInstanceIndex = index;
+        }
+        return true;
+      }
+
+      if (instruction.isArithmeticBinop()) {
+        return true;
+      }
+
+      // Otherwise we only allow invoke.
+      if (!instruction.isInvokeMethod()) {
+        return false;
+      }
+      InvokeMethod invoke = instruction.asInvokeMethod();
+      boolean constructor = dexItemFactory.isConstructor(invoke.getInvokedMethod());
+
+      // Lookup the encoded method.
+      DexMethod invokedMethod = invoke.getInvokedMethod();
+      DexEncodedMethod target;
+      if (invoke.isInvokeStatic()) {
+        target = appInfo.lookupStaticTarget(invokedMethod);
+      } else if (invoke.isInvokeDirect()) {
+        target = appInfo.lookupDirectTarget(invokedMethod);
+      } else {
+        if (invokedMethod.getHolder().isArrayType()) {
+          return false;
+        }
+        target = appInfo.lookupVirtualTarget(invokedMethod.getHolder(), invokedMethod);
+      }
+      // If the encoded method is found check the access flags.
+      if (target != null) {
+        if (!target.accessFlags.isPublic()) {
+          return false;
+        }
+        DexClass holder = appInfo.definitionFor(invokedMethod.getHolder());
+        if (!holder.accessFlags.isPublic()) {
+          return false;
+        }
+      }
+
+      // Find the number of in-going arguments, if adding this instruction.
+      int newArgumentRegisters = argumentRegisters;
+      if (instruction.inValues().size() > 0) {
+        List<Value> inValues = orderedInValues(instruction, returnValue);
+        for (int i = 0; i < inValues.size(); i++) {
+          Value value = inValues.get(i);
+          if (value == returnValue) {
+            continue;
+          }
+          if (invoke.isInvokeStatic()) {
+            newArgumentRegisters += value.requiredRegisters();
+          } else {
+            // For virtual calls only re-use the receiver argument.
+            if (i > 0 || !arguments.contains(value)) {
+              newArgumentRegisters += value.requiredRegisters();
+            }
+          }
+        }
+      }
+      if (newArgumentRegisters > MAX_IN_SIZE) {
+        return false;
+      }
+
+      // Only include constructors if the previous instruction is the corresponding new-instance.
+      if (constructor) {
+        if (start == index) {
+          return false;
+        }
+        assert index > 0;
+        int offset = 0;
+        Instruction previous;
+        do {
+          offset++;
+          previous = block.getInstructions().get(index - offset);
+        } while (previous.isConstInstruction());
+        if (!previous.isNewInstance() || previous.outValue() != returnValue) {
+          return false;
+        }
+        // Clear pending new instance flag as the last thing, now the matching constructor is known
+        // to be included.
+        pendingNewInstanceIndex = -1;
+      }
+      return true;
+    }
+
+    // Add the current instruction to the outline.
+    private void includeInstruction(Instruction instruction) {
+      List<Value> inValues = orderedInValues(instruction, returnValue);
+
+      Value prevReturnValue = returnValue;
+      if (returnValue != null) {
+        for (Value value : inValues) {
+          if (value == returnValue) {
+            assert returnValueUsersLeft > 0;
+            returnValueUsersLeft--;
+          }
+          if (returnValueUsersLeft == 0) {
+            returnValue = null;
+            returnType = dexItemFactory.voidType;
+          }
+        }
+      }
+
+      if (instruction.isNewInstance()) {
+        assert returnValue == null;
+        updateReturnValueState(instruction.outValue(), instruction.asNewInstance().clazz);
+        return;
+      }
+
+      assert instruction.isInvoke()
+          || instruction.isConstInstruction()
+          || instruction.isArithmeticBinop();
+      if (inValues.size() > 0) {
+        for (int i = 0; i < inValues.size(); i++) {
+          Value value = inValues.get(i);
+          if (value == prevReturnValue) {
+            argumentsMap.add(-1);
+            continue;
+          }
+          if (instruction.isInvoke()
+              && instruction.asInvoke().getType() != Type.STATIC
+              && instruction.asInvoke().getType() != Type.CUSTOM) {
+            InvokeMethod invoke = instruction.asInvokeMethod();
+            int argumentIndex = arguments.indexOf(value);
+            // For virtual calls only re-use the receiver argument.
+            if (i == 0 && argumentIndex != -1) {
+              argumentsMap.add(argumentIndex);
+            } else {
+              arguments.add(value);
+              argumentRegisters += value.requiredRegisters();
+              if (i == 0) {
+                argumentTypes.add(invoke.getInvokedMethod().getHolder());
+              } else {
+                DexProto methodProto;
+                if (instruction.asInvoke().getType() == Type.POLYMORPHIC) {
+                  // Type of argument of a polymorphic call must be take from the call site
+                  methodProto = instruction.asInvokePolymorphic().getProto();
+                } else {
+                  methodProto = invoke.getInvokedMethod().proto;
+                }
+                // -1 due to receiver
+                argumentTypes.add(methodProto.parameters.values[i - 1]);
+              }
+              argumentsMap.add(arguments.size() - 1);
+            }
+          } else {
+            arguments.add(value);
+            if (instruction.isInvokeMethod()) {
+              argumentTypes
+                  .add(instruction.asInvokeMethod().getInvokedMethod().proto.parameters.values[i]);
+            } else {
+              argumentTypes.add(instruction.asBinop().getNumericType().dexTypeFor(dexItemFactory));
+            }
+            argumentsMap.add(arguments.size() - 1);
+          }
+        }
+      }
+      if (!instruction.isConstInstruction() && instruction.outValue() != null) {
+        assert returnValue == null;
+        if (instruction.isInvokeMethod()) {
+          updateReturnValueState(
+              instruction.outValue(),
+              instruction.asInvokeMethod().getInvokedMethod().proto.returnType);
+        } else {
+          updateReturnValueState(
+              instruction.outValue(),
+              instruction.asBinop().getNumericType().dexTypeFor(dexItemFactory));
+        }
+      }
+    }
+
+    private void updateReturnValueState(Value newReturnValue, DexType newReturnType) {
+      returnValueUsersLeft = newReturnValue.numberOfAllUsers();
+      // If the return value is not used don't track it.
+      if (returnValueUsersLeft == 0) {
+        returnValue = null;
+        returnType = dexItemFactory.voidType;
+      } else {
+        returnValue = newReturnValue;
+        returnType = newReturnType;
+      }
+    }
+
+
+    protected abstract void handle(int start, int end, Outline outline);
+
+    private void candidate(int start, int index) {
+      assert !instructions.get(start).isConstInstruction();
+
+      if (pendingNewInstanceIndex != -1) {
+        if (pendingNewInstanceIndex == start) {
+          reset(index);
+        } else {
+          reset(pendingNewInstanceIndex);
+        }
+        return;
+      }
+
+      // Back out of any const instructions ending this candidate.
+      int end = index;
+      while (instructions.get(end - 1).isConstInstruction()) {
+        end--;
+      }
+
+      // Check if the candidate qualifies.
+      int nonConstInstructions = 0;
+      for (int i = start; i < end; i++) {
+        if (!instructions.get(i).isConstInstruction()) {
+          nonConstInstructions++;
+        }
+      }
+      if (nonConstInstructions < options.outline.minSize) {
+        reset(start + 1);
+        return;
+      }
+
+      Outline outline = new Outline(
+          block, arguments, argumentTypes, argumentsMap, returnType, start, end);
+      handle(start, end, outline);
+
+      // Start a new candidate search from the next instruction after this outline.
+      reset(index);
+    }
+
+    // Restart the collection of outline candidate to the given instruction start index.
+    private void reset(int startIndex) {
+      start = startIndex;
+      index = startIndex;
+      actualInstructions = 0;
+      arguments = new ArrayList<>(MAX_IN_SIZE);
+      argumentTypes = new ArrayList<>(MAX_IN_SIZE);
+      argumentsMap = new ArrayList<>(MAX_IN_SIZE);
+      argumentRegisters = 0;
+      returnType = dexItemFactory.voidType;
+      returnValue = null;
+      returnValueUsersLeft = 0;
+      pendingNewInstanceIndex = -1;
+    }
+  }
+
+  // Collect outlining candidates with the methods that can use them.
+  // TODO(sgjesse): This does not take several usages in the same method into account.
+  private class OutlineIdentifier extends OutlineSpotter {
+
+    OutlineIdentifier(DexEncodedMethod method, BasicBlock block) {
+      super(method, block);
+    }
+
+    protected void handle(int start, int end, Outline outline) {
+      synchronized (candidates) {
+        candidates.computeIfAbsent(outline, k -> new ArrayList<>()).add(method);
+      }
+    }
+  }
+
+  // Replace instructions with a call to the outlined method.
+  private class OutlineRewriter extends OutlineSpotter {
+
+    private final IRCode code;
+    private final ListIterator<BasicBlock> blocksIterator;
+    private final List<Integer> toRemove;
+    int argumentsMapIndex;
+    Value returnValue;
+
+    OutlineRewriter(
+        DexEncodedMethod method, IRCode code,
+        ListIterator<BasicBlock> blocksIterator, BasicBlock block, List<Integer> toRemove) {
+      super(method, block);
+      this.code = code;
+      this.blocksIterator = blocksIterator;
+      this.toRemove = toRemove;
+    }
+
+    protected void handle(int start, int end, Outline outline) {
+      if (candidates.containsKey(outline)) {
+        DexMethod m = generatedOutlines.get(outline);
+        assert m != null;
+        List<Instruction> instructions = block.getInstructions();
+        List<Value> in = new ArrayList<>();
+        returnValue = null;
+        argumentsMapIndex = 0;
+        for (int i = start; i < end; i++) {
+          Instruction current = instructions.get(i);
+          if (current.isConstInstruction()) {
+            // Leave any const instructions.
+            continue;
+          }
+
+          // Prepare to remove the instruction.
+          List<Value> inValues = orderedInValues(current, returnValue);
+          for (int j = 0; j < inValues.size(); j++) {
+            Value value = inValues.get(j);
+            value.removeUser(current);
+            int argumentIndex = outline.argumentMap.get(argumentsMapIndex++);
+            if (argumentIndex >= in.size()) {
+              assert argumentIndex == in.size();
+              in.add(value);
+            }
+          }
+          if (current.outValue() != null) {
+            returnValue = current.outValue();
+          }
+          // The invoke of the outline method will be placed at the last instruction index,
+          // so don't mark that for removal.
+          if (i < end - 1) {
+            toRemove.add(i);
+          }
+        }
+        assert m.proto.shorty.toString().length() - 1 == in.size();
+        if (returnValue != null && returnValue.numberOfAllUsers() == 0) {
+          returnValue = null;
+        }
+        Invoke outlineInvoke = new InvokeStatic(m, returnValue, in);
+        outlineInvoke.setBlock(block);
+        instructions.get(end - 1).clearBlock();
+        instructions.set(end - 1, outlineInvoke);
+        if (block.hasCatchHandlers()) {
+          // If the inserted invoke is inserted in a block with handlers, split the block after
+          // the inserted invoke.
+          // TODO(sgjesse): Integrate the use of an instruction iterator into the code above.
+          block.listIterator(end).split(code, blocksIterator);
+        }
+      }
+    }
+  }
+
+  public Outliner(AppInfo appInfo, InternalOptions options) {
+    this.appInfo = appInfo;
+    this.dexItemFactory = appInfo.dexItemFactory;
+    this.options = options;
+  }
+
+  public void identifyCandidates(IRCode code, DexEncodedMethod method) {
+    assert !(method.getCode() instanceof OutlineCode);
+    for (BasicBlock block : code.blocks) {
+      new OutlineIdentifier(method, block).process();
+    }
+  }
+
+  public boolean selectMethodsForOutlining() {
+    assert methodsSelectedForOutlining.size() == 0;
+    List<Outline> toRemove = new ArrayList<>();
+    for (Entry<Outline, List<DexEncodedMethod>> entry : candidates.entrySet()) {
+      if (entry.getValue().size() < options.outline.threshold) {
+        toRemove.add(entry.getKey());
+      } else {
+        methodsSelectedForOutlining.addAll(entry.getValue());
+      }
+    }
+    for (Outline outline : toRemove) {
+      candidates.remove(outline);
+    }
+    return methodsSelectedForOutlining.size() > 0;
+  }
+
+  public Set<DexEncodedMethod> getMethodsSelectedForOutlining() {
+    return methodsSelectedForOutlining;
+  }
+
+  public void applyOutliningCandidate(IRCode code, DexEncodedMethod method) {
+    assert !(method.getCode() instanceof OutlineCode);
+    ListIterator<BasicBlock> blocksIterator = code.blocks.listIterator();
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      List<Integer> toRemove = new ArrayList<>();
+      new OutlineRewriter(method, code, blocksIterator, block, toRemove).process();
+      block.removeInstructions(toRemove);
+    }
+  }
+
+  static public void noProcessing(IRCode code, DexEncodedMethod method) {
+    // No operation.
+  }
+
+  private class OutlineSourceCode implements SourceCode {
+
+    final private Outline outline;
+    int argumentMapIndex = 0;
+
+    OutlineSourceCode(Outline outline) {
+      this.outline = outline;
+    }
+
+    @Override
+    public boolean needsPrelude() {
+      return outline.argumentCount() > 0;
+    }
+
+    @Override
+    public int instructionCount() {
+      return outline.templateInstructions.size() + 1;
+    }
+
+    @Override
+    public int instructionIndex(int instructionOffset) {
+      return instructionOffset;
+    }
+
+    @Override
+    public int instructionOffset(int instructionIndex) {
+      return instructionIndex;
+    }
+
+    @Override
+    public DebugLocalInfo getCurrentLocal(int register) {
+      return null;
+    }
+
+    @Override
+    public boolean traceInstruction(int instructionIndex, IRBuilder builder) {
+      // There is just one block, and after the last instruction it is closed.
+      return instructionIndex == outline.templateInstructions.size();
+    }
+
+    @Override
+    public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+    }
+
+    @Override
+    public void closedCurrentBlock() {
+    }
+
+    @Override
+    public void setUp() {
+    }
+
+    @Override
+    public void clear() {
+    }
+
+    @Override
+    public void buildPrelude(IRBuilder builder) {
+      // Fill in the Argument instructions in the argument block.
+      for (int i = 0; i < outline.arguments.size(); i++) {
+        MoveType moveType = outline.arguments.get(i).outType();
+        builder.addNonThisArgument(i, moveType);
+      }
+    }
+
+    @Override
+    public void buildPostlude(IRBuilder builder) {
+      // Intentionally left empty. (Needed for Java-bytecode-frontend synchronization support.)
+    }
+
+    @Override
+    public void buildInstruction(IRBuilder builder, int instructionIndex) {
+      if (instructionIndex == outline.templateInstructions.size()) {
+        if (outline.returnType == dexItemFactory.voidType) {
+          builder.addReturn();
+        } else {
+          builder.addReturn(MoveType.fromDexType(outline.returnType), outline.argumentCount());
+        }
+        return;
+      }
+      // Build IR from the template.
+      Instruction template = outline.templateInstructions.get(instructionIndex);
+      List<Value> inValues = new ArrayList<>(template.inValues().size());
+      List<Value> templateInValues = template.inValues();
+      // The template in-values are not re-ordered for commutative binary operations, as it does
+      // not matter.
+      for (int i = 0; i < templateInValues.size(); i++) {
+        Value value = templateInValues.get(i);
+        int register = outline.argumentMap.get(argumentMapIndex++);
+        if (register == -1) {
+          register = outline.argumentCount();
+        }
+        inValues.add(
+            builder.readRegister(register, value.outType()));
+      }
+      Value outValue = null;
+      if (template.outValue() != null) {
+        Value value = template.outValue();
+        outValue = builder
+            .writeRegister(outline.argumentCount(), value.outType(), ThrowingInfo.CAN_THROW);
+      }
+
+      Instruction newInstruction = null;
+      if (template.isInvoke()) {
+        Invoke templateInvoke = template.asInvoke();
+        newInstruction = Invoke.createFromTemplate(templateInvoke, outValue, inValues);
+      } else if (template.isAdd()) {
+        Add templateInvoke = template.asAdd();
+        newInstruction = new Add(
+            templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+      } else if (template.isMul()) {
+        Mul templateInvoke = template.asMul();
+        newInstruction = new Mul(
+            templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+      } else if (template.isSub()) {
+        Sub templateInvoke = template.asSub();
+        newInstruction = new Sub(
+            templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+      } else if (template.isDiv()) {
+        Div templateInvoke = template.asDiv();
+        newInstruction = new Div(
+            templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+      } else if (template.isRem()) {
+        Rem templateInvoke = template.asRem();
+        newInstruction = new Rem(
+            templateInvoke.getNumericType(), outValue, inValues.get(0), inValues.get(1));
+      } else {
+        assert template.isNewInstance();
+        NewInstance templateNewInstance = template.asNewInstance();
+        newInstruction = new NewInstance(templateNewInstance.clazz, outValue);
+      }
+      builder.add(newInstruction);
+    }
+
+    @Override
+    public void resolveAndBuildSwitch(Switch.Type type, int value, int fallthroughOffset,
+        int payloadOffset, IRBuilder builder) {
+      throw new Unreachable("Unexpected call to resolveAndBuildSwitch");
+    }
+
+    @Override
+    public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
+        IRBuilder builder) {
+      throw new Unreachable("Unexpected call to resolveAndBuildNewArrayFilledData");
+    }
+
+    @Override
+    public CatchHandlers<Integer> getCurrentCatchHandlers() {
+      return null;
+    }
+
+    @Override
+    public boolean verifyCurrentInstructionCanThrow() {
+      // TODO(sgjesse): Check more here?
+      return true;
+    }
+
+    @Override
+    public boolean verifyLocalInScope(DebugLocalInfo local) {
+      return true;
+    }
+
+    @Override
+    public boolean verifyRegister(int register) {
+      return true;
+    }
+  }
+
+  public class OutlineCode extends Code {
+
+    private Outline outline;
+
+    OutlineCode(Outline outline) {
+      this.outline = outline;
+    }
+
+    @Override
+    public boolean isOutlineCode() {
+      return true;
+    }
+
+    @Override
+    public OutlineCode asOutlineCode() {
+      return this;
+    }
+
+    @Override
+    public IRCode buildIR(DexEncodedMethod encodedMethod, InternalOptions options) {
+      OutlineSourceCode source = new OutlineSourceCode(outline);
+      IRBuilder builder = new IRBuilder(encodedMethod, source, options);
+      return builder.build();
+    }
+
+    @Override
+    public String toString() {
+      return outline.toString();
+    }
+
+    @Override
+    public void registerReachableDefinitions(UseRegistry registry) {
+      throw new Unreachable();
+    }
+
+    @Override
+    protected int computeHashCode() {
+      return outline.hashCode();
+    }
+
+    @Override
+    protected boolean computeEquals(Object other) {
+      return outline.equals(outline);
+    }
+
+    @Override
+    public String toString(ClassNameMapper naming) {
+      return null;
+    }
+  }
+
+
+  public DexProgramClass buildOutlinerClass(DexType type) {
+    if (candidates.size() == 0) {
+      return null;
+    }
+
+    // Build the outlined methods.
+    DexEncodedMethod[] direct = new DexEncodedMethod[candidates.size()];
+    int count = 0;
+
+    // By now the candidates are the actual selected outlines. Name the generated methods in a
+    // consistent order, to provide deterministic output.
+    List<Outline> outlines = new ArrayList<>(candidates.keySet());
+    outlines.sort(Comparator.naturalOrder());
+    for (Outline outline : outlines) {
+      DexAccessFlags methodAccess = new DexAccessFlags(Constants.ACC_PUBLIC, Constants.ACC_STATIC);
+      DexString methodName = dexItemFactory.createString(options.outline.methodPrefix + count);
+      DexMethod method = outline.buildMethod(type, methodName);
+      direct[count] = new DexEncodedMethod(method, methodAccess, DexAnnotationSet.empty(),
+          DexAnnotationSetRefList.empty(), new OutlineCode(outline));
+      generatedOutlines.put(outline, method);
+      count++;
+    }
+    // No need to sort the direct methods as they are generated in sorted order.
+
+    // Build the outliner class.
+    DexType superType = dexItemFactory.createType("Ljava/lang/Object;");
+    DexTypeList interfaces = DexTypeList.empty();
+    DexString sourceFile = dexItemFactory.createString("outline");
+    DexAccessFlags accessFlags = new DexAccessFlags(Constants.ACC_PUBLIC);
+    DexProgramClass clazz = new DexProgramClass(
+        type,
+        DexClass.Origin.Synthetic,
+        accessFlags,
+        superType,
+        interfaces,
+        sourceFile,
+        // TODO: Build dex annotations structure.
+        DexAnnotationSet.empty(),
+        DexEncodedField.EMPTY_ARRAY, // Static fields.
+        DexEncodedField.EMPTY_ARRAY, // Instance fields.
+        direct,
+        DexEncodedMethod.EMPTY_ARRAY // Virtual methods.
+    );
+
+    return clazz;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
new file mode 100644
index 0000000..839a51b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -0,0 +1,260 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.Goto;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.LiveIntervals;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+public class PeepholeOptimizer {
+
+  /**
+   * Perform optimizations of the code with register assignments provided by the register allocator.
+   */
+  public static void optimize(IRCode code, LinearScanRegisterAllocator allocator) {
+    removeIdenticalPredecessorBlocks(code, allocator);
+    removeRedundantInstructions(code, allocator);
+    shareIdenticalBlockSuffix(code, allocator);
+    assert code.isConsistentGraph();
+  }
+
+  /**
+   * Identify common suffixes in predecessor blocks and share them.
+   */
+  private static void shareIdenticalBlockSuffix(IRCode code, RegisterAllocator allocator) {
+    Collection<BasicBlock> blocks = code.blocks;
+    do {
+      Map<BasicBlock, BasicBlock> newBlocks = new IdentityHashMap<>();
+      for (BasicBlock block : blocks) {
+        InstructionEquivalence equivalence = new InstructionEquivalence(allocator);
+        // Group interesting predecessor blocks by their last instruction.
+        Map<Wrapper<Instruction>, List<BasicBlock>> lastInstructionToBlocks = new HashMap<>();
+        for (BasicBlock pred : block.getPredecessors()) {
+          // Only deal with predecessors with one successor. This way we can move throwing
+          // instructions as well since there are no handlers (or the handler is the same as the
+          // normal control-flow block). Alternatively, we could move only non-throwing instructions
+          // and allow both a goto edge and exception edges when the target does not start with a
+          // MoveException instruction. However, that would require us to require rewriting of
+          // catch handlers as well.
+          if (pred.exit().isGoto() &&
+              pred.getSuccessors().size() == 1 &&
+              pred.getInstructions().size() > 1) {
+            List<Instruction> instructions = pred.getInstructions();
+            Instruction lastInstruction = instructions.get(instructions.size() - 2);
+            List<BasicBlock> value = lastInstructionToBlocks.computeIfAbsent(
+                equivalence.wrap(lastInstruction), (k) -> new ArrayList<>());
+            value.add(pred);
+          }
+        }
+        // For each group of predecessors of size 2 or more, find the largest common suffix and
+        // move that to a separate block.
+        for (List<BasicBlock> predsWithSameLastInstruction : lastInstructionToBlocks.values()) {
+          if (predsWithSameLastInstruction.size() < 2) {
+            continue;
+          }
+          BasicBlock firstPred = predsWithSameLastInstruction.get(0);
+          int commonSuffixSize = firstPred.getInstructions().size();
+          for (int i = 1; i < predsWithSameLastInstruction.size(); i++) {
+            BasicBlock pred = predsWithSameLastInstruction.get(i);
+            assert pred.exit().isGoto();
+            commonSuffixSize = Math.min(
+                commonSuffixSize, sharedSuffixSizeExcludingExit(firstPred, pred, allocator));
+          }
+          assert commonSuffixSize >= 1;
+          int blockNumber = code.blocks.size() + newBlocks.size();
+          BasicBlock newBlock = createAndInsertBlockForSuffix(
+              blockNumber, commonSuffixSize, predsWithSameLastInstruction, block);
+          newBlocks.put(predsWithSameLastInstruction.get(0), newBlock);
+        }
+      }
+      ListIterator<BasicBlock> blockIterator = code.listIterator();
+      while (blockIterator.hasNext()) {
+        BasicBlock block = blockIterator.next();
+        if (newBlocks.containsKey(block)) {
+          blockIterator.add(newBlocks.get(block));
+        }
+      }
+      // Go through all the newly introduced blocks to find more common suffixes to share.
+      blocks = newBlocks.values();
+    } while (!blocks.isEmpty());
+  }
+
+  private static BasicBlock createAndInsertBlockForSuffix(
+      int blockNumber, int suffixSize, List<BasicBlock> preds, BasicBlock block) {
+    BasicBlock newBlock = BasicBlock.createGotoBlock(blockNumber);
+    BasicBlock first = preds.get(0);
+    InstructionListIterator from = first.listIterator(first.getInstructions().size() - 1);
+    boolean movedThrowingInstruction = false;
+    for (int i = 0; i < suffixSize; i++) {
+      Instruction instruction = from.previous();
+      movedThrowingInstruction = movedThrowingInstruction || instruction.instructionTypeCanThrow();
+      newBlock.getInstructions().addFirst(instruction);
+    }
+    if (movedThrowingInstruction && first.hasCatchHandlers()) {
+      newBlock.transferCatchHandlers(first);
+    }
+    for (BasicBlock pred : preds) {
+      LinkedList<Instruction> instructions = pred.getInstructions();
+      Instruction exit = instructions.removeLast();
+      for (int i = 0; i < suffixSize; i++) {
+        instructions.removeLast();
+      }
+      instructions.add(exit);
+      newBlock.getPredecessors().add(pred);
+      pred.replaceSuccessor(block, newBlock);
+      block.getPredecessors().remove(pred);
+      if (movedThrowingInstruction) {
+        pred.clearCatchHandlers();
+      }
+    }
+    newBlock.link(block);
+    return newBlock;
+  }
+
+  private static int sharedSuffixSizeExcludingExit(
+      BasicBlock block0, BasicBlock block1, RegisterAllocator allocator) {
+    InstructionListIterator it0 = block0.listIterator(block0.getInstructions().size() - 1);
+    InstructionListIterator it1 = block1.listIterator(block1.getInstructions().size() - 1);
+    int suffixSize = 0;
+    while (it0.hasPrevious() && it1.hasPrevious()) {
+      Instruction i0 = it0.previous();
+      Instruction i1 = it1.previous();
+      if (!i0.identicalAfterRegisterAllocation(i1, allocator)) {
+        return suffixSize;
+      }
+      suffixSize++;
+    }
+    return suffixSize;
+  }
+
+  /**
+   * If two predecessors have the same code and successors. Replace one of them with an
+   * empty block with a goto to the other.
+   */
+  private static void removeIdenticalPredecessorBlocks(IRCode code, RegisterAllocator allocator) {
+    BasicBlockInstructionsEquivalence equivalence =
+        new BasicBlockInstructionsEquivalence(allocator);
+    // Locate one block at a time that has identical predecessors. Rewrite those predecessors and
+    // then start over. Restarting when one blocks predecessors have been rewritten simplifies
+    // the rewriting and reduces the size of the data structures.
+    boolean changed;
+    do {
+      changed = false;
+      for (BasicBlock block : code.blocks) {
+        Map<Wrapper<BasicBlock>, Integer> blockToIndex = new HashMap<>();
+        for (int predIndex = 0; predIndex < block.getPredecessors().size(); predIndex++) {
+          BasicBlock pred = block.getPredecessors().get(predIndex);
+          if (pred.getInstructions().size() == 1) {
+            continue;
+          }
+          Wrapper<BasicBlock> wrapper = equivalence.wrap(pred);
+          if (blockToIndex.containsKey(wrapper)) {
+            changed = true;
+            int otherPredIndex = blockToIndex.get(wrapper);
+            BasicBlock otherPred = block.getPredecessors().get(otherPredIndex);
+            pred.clearCatchHandlers();
+            pred.getInstructions().clear();
+            for (BasicBlock succ : pred.getSuccessors()) {
+              succ.removePredecessor(pred);
+            }
+            pred.getSuccessors().clear();
+            pred.getSuccessors().add(otherPred);
+            assert !otherPred.getPredecessors().contains(pred);
+            otherPred.getPredecessors().add(pred);
+            Goto exit = new Goto();
+            exit.setBlock(pred);
+            pred.getInstructions().add(exit);
+          } else {
+            blockToIndex.put(wrapper, predIndex);
+          }
+        }
+      }
+    } while (changed);
+  }
+
+  /**
+   * Remove redundant instructions from the code.
+   *
+   * <p>Currently removes move instructions with the same src and target register and const
+   * instructions where the constant is known to be in the register already.
+   *
+   * @param code the code from which to remove redundant instruction
+   * @param allocator the register allocator providing registers for values
+   */
+  private static void removeRedundantInstructions(
+      IRCode code, LinearScanRegisterAllocator allocator) {
+    for (BasicBlock block : code.blocks) {
+      // Mapping from register number to const number instructions for this basic block.
+      // Used to remove redundant const instructions that reloads the same constant into
+      // the same register.
+      Map<Integer, ConstNumber> registerToNumber = new HashMap<>();
+      MoveEliminator moveEliminator = new MoveEliminator(allocator);
+      ListIterator<Instruction> iterator = block.getInstructions().listIterator();
+      while (iterator.hasNext()) {
+        Instruction current = iterator.next();
+        if (moveEliminator.shouldBeEliminated(current)) {
+          iterator.remove();
+        } else if (current.outValue() != null && current.outValue().needsRegister()) {
+          Value outValue = current.outValue();
+          int instructionNumber = current.getNumber();
+          if (current.isConstNumber()) {
+            if (constantSpilledAtDefinition(current.asConstNumber(), allocator)) {
+              // Remove constant instructions that are spilled at their definition and are
+              // therefore unused.
+              iterator.remove();
+              continue;
+            }
+            int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+            ConstNumber numberInRegister = registerToNumber.get(outRegister);
+            if (numberInRegister != null && numberInRegister.identicalNonValueParts(current)) {
+              // This instruction is not needed, the same constant is already in this register.
+              iterator.remove();
+            } else {
+              // Insert the current constant in the mapping. Make sure to clobber the second
+              // register if wide.
+              registerToNumber.put(outRegister, current.asConstNumber());
+              if (current.outType() == MoveType.WIDE) {
+                registerToNumber.remove(outRegister + 1);
+              }
+            }
+          } else {
+            // This instruction writes registers with a non-constant value. Remove the registers
+            // from the mapping.
+            int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+            for (int i = 0; i < outValue.requiredRegisters(); i++) {
+              registerToNumber.remove(outRegister + i);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private static boolean constantSpilledAtDefinition(
+      ConstNumber constNumber, LinearScanRegisterAllocator allocator) {
+    if (constNumber.outValue().isFixedRegisterValue()) {
+      return false;
+    }
+    LiveIntervals definitionIntervals =
+        constNumber.outValue().getLiveIntervals().getSplitCovering(constNumber.getNumber());
+    return definitionIntervals.isSpilledAndRematerializable(allocator);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
new file mode 100644
index 0000000..e79abc1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -0,0 +1,1891 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.And;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Or;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Sub;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.Value.DebugInfo;
+import com.android.tools.r8.ir.code.Xor;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Multiset.Entry;
+import com.google.common.collect.Multisets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Linear scan register allocator.
+ *
+ * <p>The implementation is inspired by:
+ *
+ * <ul>
+ *   <li>"Linear Scan Register Allocation in the Context of SSA Form and Register Constraints"
+ *   (ftp://ftp.ssw.uni-linz.ac.at/pub/Papers/Moe02.PDF)</li>
+ *   <li>"Linear Scan Register Allocation on SSA Form"
+ *   (http://www.christianwimmer.at/Publications/Wimmer10a/Wimmer10a.pdf)</li>
+ *   <li>"Linear Scan Register Allocation for the Java HotSpot Client Compiler"
+ *   (http://www.christianwimmer.at/Publications/Wimmer04a/Wimmer04a.pdf)</li>
+ * </ul>
+ */
+public class LinearScanRegisterAllocator implements RegisterAllocator {
+  public static final int NO_REGISTER = Integer.MIN_VALUE;
+
+  private enum ArgumentReuseMode {
+    ALLOW_ARGUMENT_REUSE,
+    DISALLOW_ARGUMENT_REUSE
+  }
+
+  private static class LocalRange implements Comparable<LocalRange> {
+    final DebugLocalInfo local;
+    final int register;
+    final int start;
+    final int end;
+
+    LocalRange(Value value, int register, int start, int end) {
+      assert value.getLocalInfo() != null;
+      this.local = value.getLocalInfo();
+      this.register = register;
+      this.start = start;
+      this.end = end;
+    }
+
+    @Override
+    public int compareTo(LocalRange o) {
+      return (start != o.start)
+          ? Integer.compare(start, o.start)
+          : Integer.compare(end, o.end);
+    }
+
+    @Override
+    public String toString() {
+      return local + " @ r" + register + ": " + new LiveRange(start, end);
+    }
+  }
+
+  // When numbering instructions we number instructions only with even numbers. This allows us to
+  // use odd instruction numbers for the insertion of moves during spilling.
+  public static final int INSTRUCTION_NUMBER_DELTA = 2;
+  // The max register number that will fit in any dex instruction encoding.
+  private static final int MAX_SMALL_REGISTER = Constants.U4BIT_MAX;
+  // We put one sentinel in front of the argument chain and one after the argument chain.
+  private static final int NUMBER_OF_SENTINEL_REGISTERS = 2;
+
+  // The code for which to allocate registers.
+  private final IRCode code;
+  // Number of registers used for arguments.
+  private final int numberOfArgumentRegisters;
+
+  // Mapping from basic blocks to the set of values live at entry to that basic block.
+  private Map<BasicBlock, Set<Value>> liveAtEntrySets = new IdentityHashMap<>();
+  // The sentinel value starting the chain of linked argument values.
+  private Value preArgumentSentinelValue = null;
+
+  // The set of registers that are free for allocation.
+  private TreeSet<Integer> freeRegisters = new TreeSet<>();
+  // The max register number used.
+  private int maxRegisterNumber = 0;
+  // The next available register number not yet included in the set of used registers.
+  private int nextUnusedRegisterNumber = 0;
+
+  // List of all top-level live intervals for all SSA values.
+  private List<LiveIntervals> liveIntervals = new ArrayList<>();
+  // List of active intervals.
+  private List<LiveIntervals> active = new LinkedList<>();
+  // List of intervals where the current instruction falls into one of their live range holes.
+  private List<LiveIntervals> inactive = new LinkedList<>();
+  // List of intervals that no register has been allocated to sorted by first live range.
+  private PriorityQueue<LiveIntervals> unhandled =
+      new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+
+  // The first register used for parallel moves. After register allocation the parallel move
+  // temporary registers are [firstParallelMoveTemporary, maxRegisterNumber].
+  private int firstParallelMoveTemporary = NO_REGISTER;
+  // Mapping from register number to the number of unused register numbers below that register
+  // number. Used for compacting the register numbers if some spill registers are not used
+  // because their values can be rematerialized.
+  private int[] unusedRegisters = null;
+
+  // Whether or not the code has a move exception instruction. Used to pin the move exception
+  // register.
+  private boolean hasDedicatedMoveExceptionRegister = false;
+
+  public LinearScanRegisterAllocator(IRCode code) {
+    this.code = code;
+    int argumentRegisters = 0;
+    for (Instruction instruction : code.blocks.getFirst().getInstructions()) {
+      if (instruction.isArgument()) {
+        argumentRegisters += instruction.outValue().requiredRegisters();
+      }
+    }
+    numberOfArgumentRegisters = argumentRegisters;
+  }
+
+  /**
+   * Perform register allocation for the IRCode.
+   */
+  public void allocateRegisters(boolean debug) {
+    // There are no linked values prior to register allocation.
+    assert noLinkedValues();
+    assert code.isConsistentSSA();
+    computeNeedsRegister();
+    insertArgumentMoves();
+    BasicBlock[] blocks = computeLivenessInformation();
+    // First attempt to allocate register allowing argument reuse. This will fail if spilling
+    // is required or if we end up using more than 15 registers.
+    boolean noSpilling =
+        performAllocationWithoutMoveInsertion(ArgumentReuseMode.ALLOW_ARGUMENT_REUSE);
+    if (!noSpilling || registersUsed() > MAX_SMALL_REGISTER) {
+      // Redo allocation disallowing argument reuse. This always succeeds.
+      clearRegisterAssignments();
+      performAllocation(ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE);
+    } else {
+      // Insert spill and phi moves after allocating with argument reuse. If the moves causes
+      // the method to use more than 15 registers we redo allocation disallowing argument
+      // reuse. This very rarely happens in practice (12 methods on GMSCore v4 hits that case).
+      insertMoves();
+      if (registersUsed() > MAX_SMALL_REGISTER) {
+        // Redo allocation disallowing argument reuse. This always succeeds.
+        clearRegisterAssignments();
+        removeSpillAndPhiMoves();
+        performAllocation(ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE);
+      }
+    }
+
+    clearUserInfo();
+    assert code.isConsistentGraph();
+    if (Log.ENABLED) {
+      Log.debug(this.getClass(), toString());
+    }
+    computeUnusedRegisters();
+    if (debug) {
+      computeDebugInfo(blocks);
+    }
+    clearState();
+  }
+
+  private static Integer nextInRange(int start, int end, List<Integer> points) {
+    while (!points.isEmpty() && points.get(0) < start) {
+      points.remove(0);
+    }
+    if (points.isEmpty()) {
+      return null;
+    }
+    Integer next = points.get(0);
+    assert start <= next;
+    if (next < end) {
+      points.remove(0);
+      return next;
+    }
+    return null;
+  }
+
+  private void computeDebugInfo(BasicBlock[] blocks) {
+    // Collect live-ranges for all SSA values with local information.
+    List<LocalRange> ranges = new ArrayList<>();
+    for (LiveIntervals interval : liveIntervals) {
+      Value value = interval.getValue();
+      if (value.getLocalInfo() == null) {
+        continue;
+      }
+      List<Integer> starts = ListUtils.map(value.getDebugLocalStarts(), Instruction::getNumber);
+      List<Integer> ends = ListUtils.map(value.getDebugLocalEnds(), Instruction::getNumber);
+      List<LiveRange> liveRanges = new ArrayList<>();
+      liveRanges.addAll(interval.getRanges());
+      for (LiveIntervals child : interval.getSplitChildren()) {
+        assert child.getValue() == value;
+        assert child.getSplitChildren() == null || child.getSplitChildren().isEmpty();
+        liveRanges.addAll(child.getRanges());
+      }
+      liveRanges.sort((r1, r2) -> Integer.compare(r1.start, r2.start));
+      starts.sort(Integer::compare);
+      ends.sort(Integer::compare);
+
+      for (LiveRange liveRange : liveRanges) {
+        int start = liveRange.start;
+        int end = liveRange.end;
+        Integer nextEnd;
+        while ((nextEnd = nextInRange(start, end, ends)) != null) {
+          ranges.add(new LocalRange(value, getRegisterForValue(value, start), start, nextEnd));
+          Integer nextStart = nextInRange(nextEnd, end, starts);
+          if (nextStart == null) {
+            start = -1;
+            break;
+          }
+          start = nextStart;
+        }
+        if (start >= 0) {
+          ranges.add(new LocalRange(value, getRegisterForValue(value, start), start, end));
+        }
+      }
+    }
+    if (ranges.isEmpty()) {
+      return;
+    }
+    ranges.sort(LocalRange::compareTo);
+
+    // For each debug position compute the set of live locals.
+    // The previously known locals is used to share locals when unchanged.
+    boolean localsChanged = false;
+    ImmutableMap<Integer, DebugLocalInfo> previousLocals = ImmutableMap.of();
+    Map<Integer, DebugLocalInfo> currentLocals = new HashMap<>();
+
+    LinkedList<LocalRange> openRanges = new LinkedList<>();
+    Iterator<LocalRange> rangeIterator = ranges.iterator();
+    LocalRange nextStartingRange = rangeIterator.next();
+
+    // Open initial (argument) ranges.
+    while (nextStartingRange != null && nextStartingRange.start == 0) {
+      currentLocals.put(nextStartingRange.register, nextStartingRange.local);
+      openRanges.add(nextStartingRange);
+      nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
+      localsChanged = true;
+    }
+
+    for (BasicBlock block : blocks) {
+      Iterator<Instruction> instructionIterator = block.getInstructions().iterator();
+      while (instructionIterator.hasNext()) {
+        Instruction instruction = instructionIterator.next();
+        // Remove any local-read instructions now that live ranges are computed.
+        if (instruction.isDebugLocalRead()) {
+          instructionIterator.remove();
+          continue;
+        }
+        if (!instruction.isDebugPosition()) {
+          continue;
+        }
+        int index = instruction.getNumber();
+        ListIterator<LocalRange> it = openRanges.listIterator(0);
+        while (it.hasNext()) {
+          LocalRange openRange = it.next();
+          if (openRange.end < index) {
+            it.remove();
+            assert currentLocals.get(openRange.register) == openRange.local;
+            currentLocals.put(openRange.register, null);
+            localsChanged = true;
+          }
+        }
+        while (nextStartingRange != null && nextStartingRange.start <= index) {
+          // If the full range is between the two debug positions ignore it.
+          if (nextStartingRange.end >= index) {
+            openRanges.add(nextStartingRange);
+            assert currentLocals.get(nextStartingRange.register) == null;
+            currentLocals.put(nextStartingRange.register, nextStartingRange.local);
+            localsChanged = true;
+          }
+          nextStartingRange = rangeIterator.hasNext() ? rangeIterator.next() : null;
+        }
+        DebugPosition position = instruction.asDebugPosition();
+        if (localsChanged) {
+          ImmutableMap.Builder<Integer, DebugLocalInfo> locals = ImmutableMap.builder();
+          for (Map.Entry<Integer, DebugLocalInfo> entry : currentLocals.entrySet()) {
+            if (entry.getValue() != null) {
+              locals.put(entry);
+            }
+          }
+          localsChanged = false;
+          previousLocals = locals.build();
+        }
+        assert verifyLocalsEqual(previousLocals, currentLocals);
+        position.setLocals(previousLocals);
+      }
+    }
+  }
+
+  private boolean verifyLocalsEqual(
+      ImmutableMap<Integer, DebugLocalInfo> a, Map<Integer, DebugLocalInfo> b) {
+    int size = 0;
+    for (Map.Entry<Integer, DebugLocalInfo> entry : b.entrySet()) {
+      if (entry.getValue() != null) {
+        assert a.get(entry.getKey()) == entry.getValue();
+        ++size;
+      }
+    }
+    return a.size() == size;
+  }
+
+  private void clearState() {
+    liveAtEntrySets = null;
+    liveIntervals = null;
+    active = null;
+    inactive = null;
+    unhandled = null;
+    freeRegisters = null;
+  }
+
+  // Compute a table that for each register numbers contains the number of previous register
+  // numbers that were unused. This table is then used to slide down the actual registers
+  // used to fill the gaps.
+  private void computeUnusedRegisters() {
+    if (registersUsed() == 0) {
+      return;
+    }
+    // Compute the set of registers that is used based on all live intervals.
+    Set<Integer> usedRegisters = new HashSet<>();
+    for (LiveIntervals intervals : liveIntervals) {
+      addRegisterIfUsed(usedRegisters, intervals);
+      for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
+        addRegisterIfUsed(usedRegisters, childIntervals);
+      }
+    }
+    // Additionally, we have used temporary registers for parallel move scheduling, those
+    // are used as well.
+    for (int i = firstParallelMoveTemporary; i < maxRegisterNumber + 1; i++) {
+      usedRegisters.add(realRegisterNumberFromAllocated(i));
+    }
+    // Compute the table based on the set of used registers.
+    int unused = 0;
+    int[] computed = new int[registersUsed()];
+    for (int i = 0; i < registersUsed(); i++) {
+      if (!usedRegisters.contains(i)) {
+        unused++;
+      }
+      computed[i] = unused;
+    }
+    unusedRegisters = computed;
+  }
+
+  private void addRegisterIfUsed(Set<Integer> used, LiveIntervals intervals) {
+    boolean unused = intervals.isSpilledAndRematerializable(this);
+    if (!unused) {
+      used.add(realRegisterNumberFromAllocated(intervals.getRegister()));
+      if (intervals.getType() == MoveType.WIDE) {
+        used.add(realRegisterNumberFromAllocated(intervals.getRegister() + 1));
+      }
+    }
+  }
+
+  @Override
+  public boolean argumentValueUsesHighRegister(Value value, int instructionNumber) {
+    return isHighRegister(
+        getRegisterForValue(value, instructionNumber) + value.requiredRegisters() - 1);
+  }
+
+  @Override
+  public int registersUsed() {
+    int numberOfRegister = maxRegisterNumber + 1 - NUMBER_OF_SENTINEL_REGISTERS;
+    if (unusedRegisters != null) {
+      return numberOfRegister - unusedRegisters[unusedRegisters.length - 1];
+    }
+    return numberOfRegister;
+  }
+
+  @Override
+  public int getRegisterForValue(Value value, int instructionNumber) {
+    if (value.isFixedRegisterValue()) {
+      return realRegisterNumberFromAllocated(value.asFixedRegisterValue().getRegister());
+    }
+    LiveIntervals intervals = value.getLiveIntervals();
+    if (intervals.hasSplits()) {
+      intervals = intervals.getSplitCovering(instructionNumber);
+    }
+    return getRegisterForIntervals(intervals);
+  }
+
+  private BasicBlock[] computeLivenessInformation() {
+    BasicBlock[] blocks = code.numberInstructions();
+    computeLiveAtEntrySets();
+    computeLiveRanges();
+    return blocks;
+  }
+
+  private boolean performAllocationWithoutMoveInsertion(ArgumentReuseMode mode) {
+    pinArgumentRegisters();
+    return performLinearScan(mode);
+  }
+
+  private boolean performAllocation(ArgumentReuseMode mode) {
+    boolean result = performAllocationWithoutMoveInsertion(mode);
+    insertMoves();
+    return result;
+  }
+
+  private void removeSpillAndPhiMoves() {
+    for (BasicBlock block : code.blocks) {
+      InstructionListIterator it = block.listIterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        Value outValue = instruction.outValue();
+        if (outValue != null && outValue.isFixedRegisterValue()) {
+          // Only move and const number instructions are inserted for spill and phi moves. The
+          // const number instructions are for values that can be rematerialized instead of
+          // spilled.
+          assert instruction.isMove() || instruction.isConstNumber();
+          assert !instruction.isDebugInstruction();
+          it.remove();
+        }
+      }
+    }
+  }
+
+  private void clearRegisterAssignments() {
+    freeRegisters.clear();
+    maxRegisterNumber = 0;
+    nextUnusedRegisterNumber = 0;
+    active.clear();
+    inactive.clear();
+    unhandled.clear();
+    for (LiveIntervals intervals : liveIntervals) {
+      intervals.clearRegisterAssignment();
+    }
+  }
+
+  /**
+   * Get the register allocated to a given set of live intervals.
+   */
+  private int getRegisterForIntervals(LiveIntervals intervals) {
+    int intervalsRegister = intervals.getRegister();
+    return realRegisterNumberFromAllocated(intervalsRegister);
+  }
+
+  int realRegisterNumberFromAllocated(int allocated) {
+    assert allocated != NO_REGISTER;
+    assert allocated >= 0;
+    int register;
+    if (allocated < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS) {
+      // For the |numberOfArguments| first registers map to the correct argument register.
+      // Due to the sentinel register in front of the arguments, the register number returned is
+      // the argument number starting from one.
+      register = maxRegisterNumber - (numberOfArgumentRegisters - allocated)
+          - NUMBER_OF_SENTINEL_REGISTERS;
+    } else {
+      // For everything else use the lower numbers.
+      register = allocated - numberOfArgumentRegisters - NUMBER_OF_SENTINEL_REGISTERS;
+    }
+    // Adjust for spill registers that turn out to be unused because the value can be
+    // rematerialized instead of spilled.
+    if (unusedRegisters != null) {
+      return register - unusedRegisters[register];
+    }
+    return register;
+  }
+
+  private boolean isHighRegister(int register) {
+    return register > Constants.U4BIT_MAX;
+  }
+
+  private boolean performLinearScan(ArgumentReuseMode mode) {
+    unhandled.addAll(liveIntervals);
+    LiveIntervals argumentInterval = preArgumentSentinelValue.getLiveIntervals();
+    while (argumentInterval != null) {
+      assert argumentInterval.getRegister() != NO_REGISTER;
+      unhandled.remove(argumentInterval);
+      if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+        // All the argument intervals are active in the beginning and have preallocated registers.
+        active.add(argumentInterval);
+      } else {
+        // Treat the argument interval as spilled which will require a load to a different
+        // register for all usages.
+        if (argumentInterval.getUses().size() > 1) {
+          LiveIntervalsUse use = argumentInterval.firstUseWithConstraint();
+          if (use != null) {
+            LiveIntervals split = argumentInterval.splitBefore(use.getPosition());
+            unhandled.add(split);
+          }
+        }
+      }
+      argumentInterval = argumentInterval.getNextConsecutive();
+    }
+
+    // We have to be careful when it comes to the register allocated for a move exception
+    // instruction. For move exception instructions there is no place to put spill or
+    // restore moves. The move exception instruction has to be the first instruction in a
+    // catch handler.
+    //
+    // When we allow argument reuse we do not allow any splitting, therefore we cannot get into
+    // trouble with move exception registers. When argument reuse is disallowed we block a fixed
+    // register to be used only by move exception instructions.
+    if (mode != ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+      // Force all move exception ranges to start out with the exception in a fixed register. Split
+      // their live ranges which will force another register if used.
+      int moveExceptionRegister = NO_REGISTER;
+      List<LiveIntervals> moveExceptionIntervals = new ArrayList<>();
+      boolean overlappingMoveExceptionIntervals = false;
+      for (BasicBlock block : code.blocks) {
+        for (Instruction instruction : block.getInstructions()) {
+          if (instruction.isMoveException()) {
+            hasDedicatedMoveExceptionRegister = true;
+            Value exceptionValue = instruction.outValue();
+            LiveIntervals intervals = exceptionValue.getLiveIntervals();
+            unhandled.remove(intervals);
+            if (moveExceptionRegister == NO_REGISTER) {
+              moveExceptionRegister = getFreeConsecutiveRegisters(1);
+            }
+            intervals.setRegister(moveExceptionRegister);
+            if (!overlappingMoveExceptionIntervals) {
+              for (LiveIntervals other : moveExceptionIntervals) {
+                overlappingMoveExceptionIntervals |= other.overlaps(intervals);
+              }
+            }
+            moveExceptionIntervals.add(intervals);
+          }
+        }
+      }
+      if (overlappingMoveExceptionIntervals) {
+        for (LiveIntervals intervals : moveExceptionIntervals) {
+          if (intervals.getUses().size() > 1) {
+            LiveIntervalsUse firstUse = intervals.getUses().pollFirst();
+            LiveIntervalsUse secondUse = intervals.getUses().pollFirst();
+            intervals.getUses().add(firstUse);
+            intervals.getUses().add(secondUse);
+            LiveIntervals split = intervals.splitBefore(secondUse.getPosition());
+            unhandled.add(split);
+          }
+        }
+      }
+    }
+
+    // Go through each unhandled live interval and find a register for it.
+    while (!unhandled.isEmpty()) {
+      LiveIntervals unhandledInterval = unhandled.poll();
+      // If this interval value is the src of an argument move. Fix the registers for the
+      // consecutive arguments now and add hints to the move sources. This looks forward
+      // and propagate hints backwards to avoid many moves in connection with ranged invokes.
+      allocateArgumentIntervalsWithSrc(unhandledInterval);
+
+      int start = unhandledInterval.getStart();
+      // Check for active intervals that expired or became inactive.
+      Iterator<LiveIntervals> activeIterator = active.iterator();
+      while (activeIterator.hasNext()) {
+        LiveIntervals activeIntervals = activeIterator.next();
+        if (start >= activeIntervals.getEnd()) {
+          activeIterator.remove();
+          freeRegistersForIntervals(activeIntervals);
+        } else if (!activeIntervals.overlapsPosition(start)) {
+          activeIterator.remove();
+          assert activeIntervals.getRegister() != NO_REGISTER;
+          inactive.add(activeIntervals);
+          freeRegistersForIntervals(activeIntervals);
+        }
+      }
+
+      // Check for inactive intervals that expired or became reactivated.
+      Iterator<LiveIntervals> inactiveIterator = inactive.iterator();
+      while (inactiveIterator.hasNext()) {
+        LiveIntervals inactiveIntervals = inactiveIterator.next();
+        if (start >= inactiveIntervals.getEnd()) {
+          inactiveIterator.remove();
+        } else if (inactiveIntervals.overlapsPosition(start)) {
+          inactiveIterator.remove();
+          assert inactiveIntervals.getRegister() != NO_REGISTER;
+          active.add(inactiveIntervals);
+          takeRegistersForIntervals(inactiveIntervals);
+        }
+      }
+
+      // Perform the actual allocation.
+      if (unhandledInterval.isLinked() && !unhandledInterval.isArgumentInterval()) {
+        allocateLinkedIntervals(unhandledInterval);
+      } else {
+        if (!allocateSingleInterval(unhandledInterval, mode)) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Perform look-ahead and allocate registers for linked argument chains that have the argument
+   * interval as an argument move source.
+   *
+   * <p>The end result of calling this method is that the argument intervals have registers
+   * allocated and have been moved from unhandled to inactive. The move sources have their hints
+   * updated. The rest of the register allocation state is unchanged.
+   */
+  private void allocateArgumentIntervalsWithSrc(LiveIntervals srcInterval) {
+    Value value = srcInterval.getValue();
+    for (Instruction instruction : value.uniqueUsers()) {
+      // If there is a move user that is an argument move, we allocate the consecutive
+      // registers for the argument intervals and propagate the selected registers back as
+      // hints to the sources.
+      if (instruction.isMove() && instruction.asMove().dest().isLinked()) {
+        Move move = instruction.asMove();
+        Value dest = move.dest();
+        LiveIntervals destIntervals = dest.getLiveIntervals();
+        if (destIntervals.getRegister() == NO_REGISTER) {
+          // Save the current register allocation state so we can restore it at the end.
+          TreeSet<Integer> savedFreeRegisters = new TreeSet<>(freeRegisters);
+          int savedUnusedRegisterNumber = nextUnusedRegisterNumber;
+          List<LiveIntervals> savedActive = new LinkedList<>(active);
+          List<LiveIntervals> savedInactive = new LinkedList<>(inactive);
+
+          // Add all the active intervals to the inactive set. When allocating linked intervals we
+          // check all inactive intervals and exclude the registers for overlapping inactive
+          // intervals.
+          for (LiveIntervals active : active) {
+            // TODO(ager): We could allow the use of all the currently active registers for the
+            // ranged invoke (by adding the registers for all the active intervals to freeRegisters
+            // here). That could lead to lower register pressure. However, it would also often mean
+            // that we cannot allocate the right argument register to the current unhandled
+            // interval. Size measurements on GMSCore indicate that blocking the current active
+            // registers works the best for code size.
+            if (active.isArgumentInterval()) {
+              // Allow the ranged invoke to use argument registers if free. This improves register
+              // allocation for brigde methods that forwards all of their arguments after check-cast
+              // checks on their types.
+              freeRegistersForIntervals(active);
+            }
+            inactive.add(active);
+          }
+
+          // Allocate the argument intervals.
+          unhandled.remove(destIntervals);
+          allocateLinkedIntervals(destIntervals);
+          // Restore the register allocation state.
+          freeRegisters = savedFreeRegisters;
+          for (int i = savedUnusedRegisterNumber; i < nextUnusedRegisterNumber; i++) {
+            freeRegisters.add(i);
+          }
+          active = savedActive;
+          inactive = savedInactive;
+          // Move all the argument intervals to the inactive set.
+          LiveIntervals current = destIntervals.getStartOfConsecutive();
+          while (current != null) {
+            assert !inactive.contains(current);
+            assert !active.contains(current);
+            assert !unhandled.contains(current);
+            inactive.add(current);
+            current = current.getNextConsecutive();
+          }
+        }
+      }
+    }
+  }
+
+  private void allocateLinkedIntervals(LiveIntervals unhandledInterval) {
+    // Exclude the registers that overlap the start of one of the live ranges we are
+    // going to assign registers to now.
+    LiveIntervals current = unhandledInterval.getStartOfConsecutive();
+    Set<Integer> excludedRegisters = new HashSet<>();
+    while (current != null) {
+      for (LiveIntervals inactiveIntervals : inactive) {
+        if (inactiveIntervals.overlaps(current)) {
+          excludeRegistersForInterval(inactiveIntervals, excludedRegisters);
+        }
+      }
+      current = current.getNextConsecutive();
+    }
+    // Select registers.
+    current = unhandledInterval.getStartOfConsecutive();
+    int numberOfRegister = current.numberOfConsecutiveRegisters();
+    int firstRegister = getFreeConsecutiveRegisters(numberOfRegister);
+    for (int i = 0; i < numberOfRegister; i++) {
+      assert current != null;
+      current.setRegister(firstRegister + i);
+      if (current.getType() == MoveType.WIDE) {
+        assert i < numberOfRegister;
+        i++;
+      }
+      // Propagate hints to the move sources.
+      Value value = current.getValue();
+      if (!value.isPhi() && value.definition.isMove()) {
+        Move move = value.definition.asMove();
+        LiveIntervals intervals = move.src().getLiveIntervals();
+        intervals.setHint(current);
+      }
+      if (current != unhandledInterval) {
+        // Only the start of unhandledInterval has been reached at this point. All other live
+        // intervals in the chain have been assigned registers but their start has not yet been
+        // reached. Therefore, they belong in the inactive set.
+        unhandled.remove(current);
+        assert current.getRegister() != NO_REGISTER;
+        inactive.add(current);
+        freeRegistersForIntervals(current);
+      }
+      current = current.getNextConsecutive();
+    }
+    assert current == null;
+    assert unhandledInterval.getRegister() != NO_REGISTER;
+    active.add(unhandledInterval);
+    // Include the registers for inactive ranges that we had to exclude for this allocation.
+    freeRegisters.addAll(excludedRegisters);
+  }
+
+  // Update the information about used registers when |register| has been selected for use.
+  private void updateRegisterState(int register, boolean needsRegisterPair) {
+    int maxRegister = register + (needsRegisterPair ? 1 : 0);
+    if (maxRegister >= nextUnusedRegisterNumber) {
+      nextUnusedRegisterNumber = maxRegister + 1;
+    }
+    maxRegisterNumber = Math.max(maxRegisterNumber, maxRegister);
+  }
+
+  // Select a spill register.
+  // TODO(ager): At this point this always takes the next unused register number. We need a
+  // more intelligent selection of spill registers.
+  private int getSpillRegister(LiveIntervals intervals) {
+    int registerNumber = nextUnusedRegisterNumber++;
+    maxRegisterNumber = registerNumber;
+    if (intervals.getType() == MoveType.WIDE) {
+      nextUnusedRegisterNumber++;
+      maxRegisterNumber++;
+    }
+    return registerNumber;
+  }
+
+  private int toInstructionPosition(int position) {
+    return position % 2 == 0 ? position : position + 1;
+  }
+
+  private int toGapPosition(int position) {
+    return position % 2 == 1 ? position : position - 1;
+  }
+
+  // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+  // the first part of the result long before reading the second part of the input longs.
+  //
+  // Therefore, on dalvik, we cannot generate code with overlapping long registers such as:
+  //
+  // add-long v3, v0, v2
+  //
+  // Dalvik would add v0 and v2 and write that to v3. It would then read v1 and v3 and produce
+  // the wrong result.
+  private boolean needsOverlappingLongRegisterWorkaround(LiveIntervals intervals) {
+    if (intervals.requiredRegisters() == 1) {
+      // Not the live range for a wide value.
+      return false;
+    }
+    if (intervals.getValue().isPhi()) {
+      // If this writes a new register pair it will be via a move and not a long operation.
+      return false;
+    }
+    if (intervals.getSplitParent() != intervals) {
+      // This is a split of the parent interval and therefore if this leads to a write of a
+      // register pair it will be via a move and not a long operation.
+      return false;
+    }
+    Instruction definition = intervals.getValue().definition;
+    if (definition.isArithmeticBinop() &&
+        definition.asArithmeticBinop().getNumericType() == NumericType.LONG) {
+      return definition instanceof Add || definition instanceof Sub;
+    }
+    if (definition.isLogicalBinop() &&
+        definition.asLogicalBinop().getNumericType() == NumericType.LONG) {
+      return definition instanceof Or || definition instanceof Xor || definition instanceof And;
+    }
+    return false;
+  }
+
+  private boolean hasOverlappingLongRegisters(LiveIntervals unhandledInterval, int register) {
+    assert needsOverlappingLongRegisterWorkaround(unhandledInterval);
+    Value left = unhandledInterval.getValue().definition.asBinop().leftValue();
+    Value right = unhandledInterval.getValue().definition.asBinop().rightValue();
+    int leftReg =
+        left.getLiveIntervals().getSplitCovering(unhandledInterval.getStart()).getRegister();
+    int rightReg =
+        right.getLiveIntervals().getSplitCovering(unhandledInterval.getStart()).getRegister();
+    assert leftReg != NO_REGISTER && rightReg != NO_REGISTER;
+    // The dalvik bug is actually only for overlap with the second operand, For now we
+    // make sure that there is no overlap with either operand.
+    if ((leftReg + 1) == register|| (rightReg + 1) == register) {
+      return true;
+    }
+    return false;
+  }
+
+  private boolean allocateSingleInterval(LiveIntervals unhandledInterval, ArgumentReuseMode mode) {
+    int registerConstraint = unhandledInterval.getRegisterLimit();
+    assert registerConstraint <= Constants.U16BIT_MAX;
+    if (registerConstraint < Constants.U16BIT_MAX) {
+      // We always have argument sentinels that will not actually occupy registers. Therefore, we
+      // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit.
+      registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
+      if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
+        // We know that none of the argument registers will be reused. Therefore, we allow the
+        // use of number of arguments more registers. (We will never use number of arguments +
+        // number of sentinel registers of them.)
+        registerConstraint += numberOfArgumentRegisters;
+      }
+    }
+
+    // Set all free positions for possible registers to max integer.
+    RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
+
+    if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+      // The sentinel registers cannot be used and we block them.
+      freePositions.set(0, 0);
+      int lastSentinelRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
+      if (lastSentinelRegister <= registerConstraint) {
+        freePositions.set(lastSentinelRegister, 0);
+      }
+    } else {
+      // Argument reuse is not allowed and we block all the argument registers so that
+      // arguments are never free.
+      for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+        if (i <= registerConstraint) {
+          freePositions.set(i, 0);
+        }
+      }
+    }
+
+    // If there is a move exception instruction we block register 0 as the move exception
+    // register. If we cannot find a free valid register for the move exception value we have no
+    // place to put a spill move (because the move exception instruction has to be the
+    // first instruction in the handler block).
+    if (hasDedicatedMoveExceptionRegister) {
+      int moveExceptionRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS;
+      if (moveExceptionRegister <= registerConstraint) {
+        freePositions.set(moveExceptionRegister, 0);
+      }
+    }
+
+    // All the active intervals are not free at this point.
+    for (LiveIntervals intervals : active) {
+      int activeRegister = intervals.getRegister();
+      if (activeRegister <= registerConstraint) {
+        for (int i = 0; i < intervals.requiredRegisters(); i++) {
+          if (activeRegister + i <= registerConstraint) {
+            freePositions.set(activeRegister + i, 0);
+          }
+        }
+      }
+    }
+
+    // The register for inactive intervals that overlap with this interval are free until
+    // the next overlap.
+    for (LiveIntervals intervals : inactive) {
+      int inactiveRegister = intervals.getRegister();
+      if (inactiveRegister <= registerConstraint && unhandledInterval.overlaps(intervals)) {
+        int nextOverlap = unhandledInterval.nextOverlap(intervals);
+        for (int i = 0; i < intervals.requiredRegisters(); i++) {
+          if (inactiveRegister + i <= registerConstraint) {
+            int unhandledStart = toInstructionPosition(unhandledInterval.getStart());
+            if (nextOverlap == unhandledStart) {
+              // Don't use the register for an inactive interval that is only free until the next
+              // instruction. We can get into this situation when unhandledInterval starts at a
+              // gap position.
+              freePositions.set(inactiveRegister + i, 0);
+            } else {
+              freePositions.set(
+                  inactiveRegister + i,
+                  Math.min(freePositions.get(inactiveRegister + i), nextOverlap));
+            }
+          }
+        }
+      }
+    }
+
+    assert unhandledInterval.requiredRegisters() <= 2;
+    boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
+    // Attempt to use register hints.
+    if (useRegisterHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair)) {
+      return true;
+    }
+
+    // Get the register (pair) that is free the longest. That is the register with the largest
+    // free position.
+    int candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
+    if (needsOverlappingLongRegisterWorkaround(unhandledInterval)) {
+      while (hasOverlappingLongRegisters(unhandledInterval, candidate)) {
+        // Make the overlapping register unavailable for allocation and try again.
+        freePositions.set(candidate, 0);
+        candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair);
+      }
+    }
+    int largestFreePosition = freePositions.get(candidate);
+    if (needsRegisterPair) {
+      largestFreePosition = Math.min(largestFreePosition, freePositions.get(candidate + 1));
+    }
+
+    // Determine what to do based on how long the selected candidate is free.
+    if (largestFreePosition == 0) {
+      // Not free. We need to spill.
+      if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+        // No spilling is allowed when we allow argument reuse. Bailout and start over with
+        // argument reuse disallowed.
+        return false;
+      }
+      allocateBlockedRegister(unhandledInterval);
+    } else if (largestFreePosition >= unhandledInterval.getEnd()) {
+      // Free for the entire interval. Allocate the register.
+      assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, candidate);
+    } else {
+      if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
+        // No splitting is allowed when we allow argument reuse. Bailout and start over with
+        // argument reuse disallowed.
+        return false;
+      }
+      // The candidate is free for the beginning of an interval. We split the interval
+      // and use the register for as long as we can.
+      int splitPosition = largestFreePosition;
+      LiveIntervals split = unhandledInterval.splitBefore(splitPosition);
+      assert split != unhandledInterval;
+      assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, candidate);
+      unhandled.add(split);
+    }
+    return true;
+  }
+
+  // Attempt to use the register hint for the unhandled interval in order to avoid generating
+  // moves.
+  private boolean useRegisterHint(LiveIntervals unhandledInterval, int registerConstraint,
+      RegisterPositions freePositions, boolean needsRegisterPair) {
+    // If the unhandled interval has a hint we give it that register if it is available without
+    // spilling. For phis we also use the hint before looking at the operand registers. The
+    // phi could have a hint from an argument moves which it seems more important to honor in
+    // practice.
+    LiveIntervals hint = unhandledInterval.getHint();
+    if (hint != null) {
+      int register = hint.getRegister();
+      if (tryHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair,
+          register)) {
+        return true;
+      }
+    }
+
+    // If there is no hint or it cannot be applied we search for a good register for phis using
+    // the registers assigned to the operand intervals. We determine all the registers used
+    // for operands and try them one by one based on frequency.
+    Value value = unhandledInterval.getValue();
+    if (value.isPhi()) {
+      Phi phi = value.asPhi();
+      Multiset<Integer> map = HashMultiset.create();
+      List<Value> operands = phi.getOperands();
+      for (int i = 0; i < operands.size(); i++) {
+        LiveIntervals intervals = operands.get(i).getLiveIntervals();
+        if (intervals.hasSplits()) {
+          BasicBlock pred = phi.getBlock().getPredecessors().get(i);
+          intervals = intervals.getSplitCovering(pred.exit().getNumber());
+        }
+        int operandRegister = intervals.getRegister();
+        if (operandRegister != NO_REGISTER) {
+          map.add(operandRegister);
+        }
+      }
+      for (Entry<Integer> entry : Multisets.copyHighestCountFirst(map).entrySet()) {
+        int register = entry.getElement();
+        if (tryHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair,
+            register)) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  // Attempt to allocate the hint register to the unhandled intervals.
+  private boolean tryHint(LiveIntervals unhandledInterval, int registerConstraint,
+      RegisterPositions freePositions, boolean needsRegisterPair, int register) {
+    if (register + (needsRegisterPair ? 1 : 0) <= registerConstraint) {
+      int freePosition = freePositions.get(register);
+      if (needsRegisterPair) {
+        freePosition = Math.min(freePosition, freePositions.get(register + 1));
+      }
+      if (freePosition >= unhandledInterval.getEnd()) {
+        // Check for overlapping long registers issue.
+        if (needsOverlappingLongRegisterWorkaround(unhandledInterval) &&
+            hasOverlappingLongRegisters(unhandledInterval, register)) {
+          return false;
+        }
+        assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, register);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void assignRegister(LiveIntervals intervals, int register) {
+    intervals.setRegister(register);
+    updateRegisterHints(intervals);
+  }
+
+  private void updateRegisterHints(LiveIntervals intervals) {
+    Value value = intervals.getValue();
+    // If the value flows into a phi, set the hint for all the operand splits that flow into the
+    // phi and do not have hints yet.
+    for (Phi phi : value.uniquePhiUsers()) {
+      LiveIntervals phiIntervals = phi.getLiveIntervals();
+      if (phiIntervals.getHint() == null) {
+        for (int i = 0; i < phi.getOperands().size(); i++) {
+          Value operand = phi.getOperand(i);
+          LiveIntervals operandIntervals = operand.getLiveIntervals();
+          BasicBlock pred = phi.getBlock().getPredecessors().get(i);
+          operandIntervals = operandIntervals.getSplitCovering(pred.exit().getNumber());
+          if (operandIntervals.getHint() == null) {
+            operandIntervals.setHint(intervals);
+          }
+        }
+      }
+    }
+    // If the value is a phi and we are at the start of the interval, we set the register as
+    // the hint for all of the operand splits flowing into the phi. We set the hint no matter
+    // if there is already a hint. We know the register for the phi and want as many operands
+    // as possible to be allocated the same register to avoid phi moves.
+    if (value.isPhi() && intervals.getSplitParent() == intervals) {
+      Phi phi = value.asPhi();
+      BasicBlock block = phi.getBlock();
+      for (int i = 0; i < phi.getOperands().size(); i++) {
+        Value operand = phi.getOperand(i);
+        BasicBlock pred = block.getPredecessors().get(i);
+        LiveIntervals operandIntervals =
+            operand.getLiveIntervals().getSplitCovering(pred.exit().getNumber());
+        operandIntervals.setHint(intervals);
+      }
+    }
+  }
+
+  private void assignRegisterToUnhandledInterval(
+      LiveIntervals unhandledInterval, boolean needsRegisterPair, int register) {
+    assignRegister(unhandledInterval, register);
+    takeRegistersForIntervals(unhandledInterval);
+    updateRegisterState(register, needsRegisterPair);
+    active.add(unhandledInterval);
+  }
+
+  private int getLargestCandidate(
+      int registerConstraint, RegisterPositions freePositions, boolean needsRegisterPair) {
+    int candidate = 0;
+    int largest = freePositions.get(0);
+    if (needsRegisterPair) {
+      largest = Math.min(largest, freePositions.get(1));
+    }
+    for (int i = 1; i <= registerConstraint; i++) {
+      int freePosition = freePositions.get(i);
+      if (needsRegisterPair) {
+        if (i >= registerConstraint) {
+          break;
+        }
+        freePosition = Math.min(freePosition, freePositions.get(i + 1));
+      }
+      if (freePosition > largest) {
+        candidate = i;
+        largest = freePosition;
+        if (largest == Integer.MAX_VALUE) {
+          break;
+        }
+      }
+    }
+    return candidate;
+  }
+
+  private void allocateBlockedRegister(LiveIntervals unhandledInterval) {
+    int registerConstraint = unhandledInterval.getRegisterLimit();
+    if (registerConstraint < Constants.U16BIT_MAX) {
+      // We always have argument sentinels that will not actually occupy registers. Therefore, we
+      // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit. Additionally, we never
+      // reuse argument registers and therefore allow the use of numberOfArgumentRegisters as well.
+      registerConstraint += numberOfArgumentRegisters;
+      registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
+    }
+
+    // Initialize all candidate registers to Integer.MAX_VALUE.
+    RegisterPositions usePositions = new RegisterPositions(registerConstraint + 1);
+    RegisterPositions blockedPositions = new RegisterPositions(registerConstraint + 1);
+
+    // Compute next use location for all currently active registers.
+    for (LiveIntervals intervals : active) {
+      int activeRegister = intervals.getRegister();
+      if (activeRegister <= registerConstraint) {
+        for (int i = 0; i < intervals.requiredRegisters(); i++) {
+          if (activeRegister + i <= registerConstraint) {
+            int unhandledStart = unhandledInterval.getStart();
+            usePositions.set(activeRegister + i, intervals.firstUseAfter(unhandledStart));
+          }
+        }
+      }
+    }
+
+    // Compute next use location for all inactive registers that overlaps the unhandled interval.
+    for (LiveIntervals intervals : inactive) {
+      int inactiveRegister = intervals.getRegister();
+      if (inactiveRegister <= registerConstraint && intervals.overlaps(unhandledInterval)) {
+        for (int i = 0; i < intervals.requiredRegisters(); i++) {
+          if (inactiveRegister + i <= registerConstraint) {
+            int unhandledStart = unhandledInterval.getStart();
+            usePositions.set(inactiveRegister + i, Math.min(
+                usePositions.get(inactiveRegister + i), intervals.firstUseAfter(unhandledStart)));
+          }
+        }
+      }
+    }
+
+    // Disallow the reuse of argument registers by always treating them as being used
+    // at instruction number 0.
+    for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+      usePositions.set(i, 0);
+    }
+
+    // Disallow reuse of the move exception register if we have reserved one.
+    if (hasDedicatedMoveExceptionRegister) {
+      usePositions.set(numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS, 0);
+    }
+
+    // Treat active linked argument intervals as pinned. They cannot be given another register
+    // at their uses.
+    blockLinkedRegisters(
+        active, unhandledInterval, registerConstraint, usePositions, blockedPositions);
+
+    // Treat inactive linked argument intervals as pinned. They cannot be given another register
+    // at their uses.
+    blockLinkedRegisters(
+        inactive, unhandledInterval, registerConstraint, usePositions, blockedPositions);
+
+    // Get the register (pair) that has the highest use position.
+    assert unhandledInterval.requiredRegisters() <= 2;
+    boolean needsRegisterPair = unhandledInterval.requiredRegisters() == 2;
+    int candidate = getLargestCandidate(registerConstraint, usePositions, needsRegisterPair);
+    int largestUsePosition = usePositions.get(candidate);
+    int blockedPosition = blockedPositions.get(candidate);
+    if (needsRegisterPair) {
+      blockedPosition = Math.min(blockedPosition, blockedPositions.get(candidate + 1));
+      largestUsePosition = Math.min(largestUsePosition, usePositions.get(candidate + 1));
+    }
+
+    // TODO(ager): When selecting a spill candidate also take rematerialization into account.
+    // Prefer spilling a constant that can be rematerialized instead of spilling something that
+    // cannot be rematerialized.
+
+    if (largestUsePosition < unhandledInterval.getFirstUse()) {
+      // All active and inactive intervals are used before current. Therefore, it is best to spill
+      // current itself.
+      int splitPosition = unhandledInterval.getFirstUse();
+      LiveIntervals split = unhandledInterval.splitBefore(splitPosition);
+      assert split != unhandledInterval;
+      int registerNumber = getSpillRegister(unhandledInterval);
+      assignRegisterToUnhandledInterval(unhandledInterval, needsRegisterPair, registerNumber);
+      unhandledInterval.setSpilled(true);
+      unhandled.add(split);
+    } else if (blockedPosition > unhandledInterval.getEnd()) {
+      // Spilling can make a register available for the entire interval.
+      // It would have been nice to use assignRegisterToUnhandledInterval here, but unfortunately
+      // the order of the operations are extremely important here. updateRegisterState has to
+      // happen before spillOverlappingActiveIntervals and takeRegistersForIntervals has to happen
+      // after.
+      assignRegisterAndSpill(unhandledInterval, needsRegisterPair, candidate);
+    } else {
+      // Spilling only makes a register available for the first part of current.
+      LiveIntervals splitChild = unhandledInterval.splitBefore(blockedPosition);
+      unhandled.add(splitChild);
+      assignRegisterAndSpill(unhandledInterval, needsRegisterPair, candidate);
+    }
+  }
+
+  private void assignRegisterAndSpill(
+      LiveIntervals unhandledInterval, boolean needsRegisterPair, int candidate) {
+    assignRegister(unhandledInterval, candidate);
+    updateRegisterState(candidate, needsRegisterPair);
+    // Split and spill intersecting active intervals for this register.
+    spillOverlappingActiveIntervals(unhandledInterval, needsRegisterPair, candidate);
+    takeRegistersForIntervals(unhandledInterval);
+    active.add(unhandledInterval);
+    // Split all overlapping inactive intervals for this register. They need to have a new
+    // register assigned at the next use.
+    splitOverlappingInactiveIntervals(unhandledInterval, needsRegisterPair, candidate);
+  }
+
+  private void splitOverlappingInactiveIntervals(LiveIntervals unhandledInterval,
+      boolean needsRegisterPair, int candidate) {
+    Iterator<LiveIntervals> inactiveIterator = inactive.iterator();
+    while (inactiveIterator.hasNext()) {
+      LiveIntervals intervals = inactiveIterator.next();
+      if ((intervals.usesRegister(candidate) ||
+          (needsRegisterPair && intervals.usesRegister(candidate + 1))) &&
+          intervals.overlaps(unhandledInterval)) {
+        if (intervals.getStart() > unhandledInterval.getStart()) {
+          // The inactive live intervals hasn't started yet. Clear the temporary register
+          // assignment and move back to unhandled for register reassignment.
+          intervals.clearRegisterAssignment();
+          inactiveIterator.remove();
+          unhandled.add(intervals);
+        } else {
+          // The inactive live intervals is in a live range hole. Split the interval and
+          // put the ranges after the hole into the unhandled set for register reassignment.
+          LiveIntervals split = intervals.splitBefore(unhandledInterval.getStart());
+          unhandled.add(split);
+        }
+      }
+    }
+  }
+
+  private void spillOverlappingActiveIntervals(LiveIntervals unhandledInterval,
+      boolean needsRegisterPair, int candidate) {
+    List<LiveIntervals> newActive = new ArrayList<>();
+    Iterator<LiveIntervals> activeIterator = active.iterator();
+    while (activeIterator.hasNext()) {
+      LiveIntervals intervals = activeIterator.next();
+      if (intervals.usesRegister(candidate) ||
+          (needsRegisterPair && intervals.usesRegister(candidate + 1))) {
+        activeIterator.remove();
+        freeRegistersForIntervals(intervals);
+        LiveIntervals splitChild = intervals.splitBefore(unhandledInterval.getStart());
+        int registerNumber = getSpillRegister(intervals);
+        assignRegister(splitChild, registerNumber);
+        splitChild.setSpilled(true);
+        takeRegistersForIntervals(splitChild);
+        assert splitChild.getRegister() != NO_REGISTER;
+        assert intervals.getRegister() != NO_REGISTER;
+        newActive.add(splitChild);
+        // If the constant is split before its first actual use, mark the constant as being
+        // spilled. That will allows us to remove it afterwards if it is rematerializable.
+        if (intervals.getValue().isConstant()
+            && intervals.getStart() == intervals.getValue().definition.getNumber()
+            && intervals.getUses().size() == 1) {
+          intervals.setSpilled(true);
+        }
+        if (splitChild.getUses().size() > 0) {
+          if (splitChild.isLinked() && !splitChild.isArgumentInterval()) {
+            // Spilling a value with a pinned register. We need to move back at the next use.
+            LiveIntervals splitOfSplit = splitChild.splitBefore(splitChild.getFirstUse());
+            splitOfSplit.setRegister(intervals.getRegister());
+            inactive.add(splitOfSplit);
+          } else if (intervals.getValue().isConstant()) {
+            splitRangesForSpilledConstant(splitChild, registerNumber);
+          } else {
+            splitRangesForSpilledInterval(splitChild, registerNumber);
+
+          }
+        }
+      }
+    }
+    active.addAll(newActive);
+  }
+
+  private void splitRangesForSpilledInterval(LiveIntervals splitChild, int registerNumber) {
+    // Spilling a non-pinned, non-rematerializable value. We use the value in the spill
+    // register for as long as possible to avoid further moves.
+    assert splitChild.isSpilled();
+    assert !splitChild.getValue().isConstant();
+    assert !splitChild.isLinked() || splitChild.isArgumentInterval();
+    LiveIntervalsUse firstUseWithLowerLimit = null;
+    boolean hasUsesBeforeFirstUseWithLowerLimit = false;
+    for (LiveIntervalsUse use : splitChild.getUses()) {
+      if (registerNumber > use.getLimit()) {
+        firstUseWithLowerLimit = use;
+        break;
+      } else {
+        hasUsesBeforeFirstUseWithLowerLimit = true;
+      }
+    }
+    if (hasUsesBeforeFirstUseWithLowerLimit) {
+      splitChild.setSpilled(false);
+    }
+    if (firstUseWithLowerLimit != null) {
+      LiveIntervals splitOfSplit = splitChild.splitBefore(firstUseWithLowerLimit.getPosition());
+      unhandled.add(splitOfSplit);
+    }
+  }
+
+  private void splitRangesForSpilledConstant(LiveIntervals spilled, int spillRegister) {
+    // When spilling a constant we should not keep it alive in the spill register, instead
+    // we should use rematerialization. We aggressively spill the constant in all gaps
+    // between uses that span more than a certain number of instructions. If we needed to
+    // spill we are running low on registers and this constant should get out of the way
+    // as much as possible.
+    assert spilled.isSpilled();
+    assert spilled.getValue().isConstant();
+    assert !spilled.isLinked() || spilled.isArgumentInterval();
+    int maxGapSize = 50 * INSTRUCTION_NUMBER_DELTA;
+    if (!spilled.getUses().isEmpty()) {
+      // Split at first use after the spill position and add to unhandled to get a register
+      // assigned for rematerialization.
+      LiveIntervals split = spilled.splitBefore(spilled.getFirstUse());
+      unhandled.add(split);
+      // Now repeatedly split for each use that is more than maxGapSize away from the previous use.
+      boolean changed = true;
+      while (changed) {
+        changed = false;
+        int previousUse = split.getStart();
+        for (LiveIntervalsUse use : split.getUses()) {
+          if (use.getPosition() - previousUse > maxGapSize) {
+            // Found a use that is more than gap size away from the previous use. Split after
+            // the previous use.
+            split = split.splitBefore(previousUse + INSTRUCTION_NUMBER_DELTA);
+            // If the next use is not at the start of the new split, we split again at the next use
+            // and spill the gap.
+            if (toGapPosition(use.getPosition()) > split.getStart()) {
+              assignRegister(split, spillRegister);
+              split.setSpilled(true);
+              inactive.add(split);
+              split = split.splitBefore(use.getPosition());
+            }
+            // |split| now starts at the next use - add it to unhandled to get a register
+            // assigned for rematerialization.
+            unhandled.add(split);
+            // Break out of the loop to start iterating the new split uses.
+            changed = true;
+            break;
+          }
+          previousUse = use.getPosition();
+        }
+      }
+    }
+  }
+
+  private void blockLinkedRegisters(
+      List<LiveIntervals> intervalsList, LiveIntervals interval, int registerConstraint,
+      RegisterPositions usePositions, RegisterPositions blockedPositions) {
+    for (LiveIntervals other : intervalsList) {
+      if (other.isLinked()) {
+        int register = other.getRegister();
+        if (register <= registerConstraint && other.overlaps(interval)) {
+          for (int i = 0; i < other.requiredRegisters(); i++) {
+            if (register + i <= registerConstraint) {
+              for (LiveIntervalsUse use : other.getUses()) {
+                if (use.getPosition() > interval.getStart()) {
+                  blockedPositions.set(
+                      register + i,
+                      Math.min(blockedPositions.get(register + i), use.getPosition()));
+                  usePositions.set(
+                      register + i,
+                      Math.min(usePositions.get(register + i), use.getPosition()));
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private void insertMoves() {
+    SpillMoveSet spillMoves =
+        new SpillMoveSet(this, code, numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS);
+    for (LiveIntervals intervals : liveIntervals) {
+      if (intervals.hasSplits()) {
+        LiveIntervals current = intervals;
+        PriorityQueue<LiveIntervals> sortedChildren =
+            new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+        sortedChildren.addAll(current.getSplitChildren());
+        for (LiveIntervals split = sortedChildren.poll();
+            split != null;
+            split = sortedChildren.poll()) {
+          int position = split.getStart();
+          spillMoves.addSpillOrRestoreMove(toGapPosition(position), split, current);
+          current = split;
+        }
+      }
+    }
+
+    resolveControlFlow(spillMoves);
+    firstParallelMoveTemporary = maxRegisterNumber + 1;
+    maxRegisterNumber += spillMoves.scheduleAndInsertMoves(maxRegisterNumber + 1);
+  }
+
+  // Resolve control flow by inserting phi moves and by inserting moves when the live intervals
+  // change for a value across block boundaries.
+  private void resolveControlFlow(SpillMoveSet spillMoves) {
+    // For a control-flow graph like the following where a value v is split at an instruction in
+    // block C a spill move is inserted in block C to transfer the value from register r0 to
+    // register r1. However, that move is not executed when taking the control-flow edge from
+    // B to D and therefore resolution will insert a move from r0 to r1 on that edge.
+    //
+    //             r0            r1
+    //   v: |----------------|--------|
+    //
+    //       A ----> B ----> C ----> D
+    //               |               ^
+    //               +---------------+
+    for (BasicBlock block : code.blocks) {
+      for (BasicBlock successor : block.getSuccessors()) {
+        // If we are processing an exception edge, we need to use the throwing instruction
+        // as the instruction we are coming from.
+        int fromInstruction = block.exit().getNumber();
+        boolean isCatch = block.isCatchSuccessor(successor);
+        if (isCatch) {
+          for (Instruction instruction : block.getInstructions()) {
+            if (instruction.instructionTypeCanThrow()) {
+              fromInstruction = instruction.getNumber();
+              break;
+            }
+          }
+        }
+        int toInstruction = successor.entry().getNumber();
+
+        // Insert spill/restore moves when a value changes across a block boundary.
+        Set<Value> liveAtEntry = liveAtEntrySets.get(successor);
+        for (Value value : liveAtEntry) {
+          LiveIntervals parentInterval = value.getLiveIntervals();
+          LiveIntervals fromIntervals = parentInterval.getSplitCovering(fromInstruction);
+          LiveIntervals toIntervals = parentInterval.getSplitCovering(toInstruction);
+          if (fromIntervals != toIntervals) {
+            if (block.exit().isGoto() && !isCatch) {
+              spillMoves.addOutResolutionMove(fromInstruction - 1, toIntervals, fromIntervals);
+            } else if (successor.entry().isMoveException()) {
+              spillMoves.addInResolutionMove(toInstruction + 1, toIntervals, fromIntervals);
+            } else {
+              spillMoves.addInResolutionMove(toInstruction - 1, toIntervals, fromIntervals);
+            }
+          }
+        }
+
+        // Insert phi moves.
+        int predIndex = successor.getPredecessors().indexOf(block);
+        for (Phi phi : successor.getPhis()) {
+          LiveIntervals toIntervals = phi.getLiveIntervals().getSplitCovering(toInstruction);
+          Value operand = phi.getOperand(predIndex);
+          LiveIntervals fromIntervals = operand.getLiveIntervals();
+          if (operand.isPhi() && operand != phi && successor.getPhis().contains(operand)) {
+            // If the input to this phi is another phi in this block we want the value after
+            // merging which is the value for that phi at the from instruction.
+            fromIntervals = fromIntervals.getSplitCovering(fromInstruction);
+          } else {
+            fromIntervals = fromIntervals.getSplitCovering(fromInstruction);
+          }
+          if (fromIntervals != toIntervals && !toIntervals.isArgumentInterval()) {
+            assert block.getSuccessors().size() == 1;
+            spillMoves.addPhiMove(fromInstruction - 1, toIntervals, fromIntervals);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Compute the set of live values at the entry to each block using a backwards data-flow
+   * analysis.
+   */
+  private void computeLiveAtEntrySets() {
+    Queue<BasicBlock> worklist = new LinkedList<>();
+    // Since this is a backwards data-flow analysis we process the blocks in reverse
+    // topological order to reduce the number of iterations.
+    BasicBlock[] sorted = code.topologicallySortedBlocks();
+    for (int i = sorted.length - 1; i >= 0; i--) {
+      worklist.add(sorted[i]);
+    }
+    while (!worklist.isEmpty()) {
+      BasicBlock block = worklist.poll();
+      Set<Value> live = new HashSet<>();
+      for (BasicBlock succ : block.getSuccessors()) {
+        Set<Value> succLiveAtEntry = liveAtEntrySets.get(succ);
+        if (succLiveAtEntry != null) {
+          live.addAll(succLiveAtEntry);
+        }
+        int predIndex = succ.getPredecessors().indexOf(block);
+        for (Phi phi : succ.getPhis()) {
+          live.add(phi.getOperand(predIndex));
+        }
+      }
+      ListIterator<Instruction> iterator =
+          block.getInstructions().listIterator(block.getInstructions().size());
+      while (iterator.hasPrevious()) {
+        Instruction instruction = iterator.previous();
+        if (instruction.outValue() != null) {
+          live.remove(instruction.outValue());
+        }
+        for (Value use : instruction.inValues()) {
+          if (use.needsRegister()) {
+            live.add(use);
+          }
+        }
+        Value use = instruction.getPreviousLocalValue();
+        if (use != null) {
+          assert use.needsRegister();
+          live.add(use);
+        }
+      }
+      for (Phi phi : block.getPhis()) {
+        live.remove(phi);
+      }
+      Set<Value> previousLiveAtEntry = liveAtEntrySets.put(block, live);
+      // If the live at entry set changed for this block at the predecessors to the worklist if
+      // they are not already there.
+      if (previousLiveAtEntry == null || !previousLiveAtEntry.equals(live)) {
+        for (BasicBlock pred : block.getPredecessors()) {
+          if (!worklist.contains(pred)) {
+            worklist.add(pred);
+          }
+        }
+      }
+    }
+    assert liveAtEntrySets.get(sorted[0]).size() == 0;
+  }
+
+  private void addLiveRange(Value v, BasicBlock b, int end) {
+    int firstInstructionInBlock = b.entry().getNumber();
+    int instructionsSize = b.getInstructions().size() * INSTRUCTION_NUMBER_DELTA;
+    int lastInstructionInBlock =
+        firstInstructionInBlock + instructionsSize - INSTRUCTION_NUMBER_DELTA;
+    int instructionNumber;
+    if (v.isPhi()) {
+      instructionNumber = firstInstructionInBlock;
+    } else {
+      Instruction instruction = v.definition;
+      instructionNumber = instruction.getNumber();
+    }
+    if (v.getLiveIntervals() == null) {
+      Value current = v.getStartOfConsecutive();
+      LiveIntervals intervals = new LiveIntervals(current);
+      while (true) {
+        liveIntervals.add(intervals);
+        Value next = current.getNextConsecutive();
+        if (next == null) {
+          break;
+        }
+        LiveIntervals nextIntervals = new LiveIntervals(next);
+        intervals.link(nextIntervals);
+        current = next;
+        intervals = nextIntervals;
+      }
+    }
+    LiveIntervals intervals = v.getLiveIntervals();
+    if (firstInstructionInBlock <= instructionNumber &&
+        instructionNumber <= lastInstructionInBlock) {
+      if (v.isPhi()) {
+        // Phis need to interfere with spill restore moves inserted before the instruction because
+        // the phi value is defined on the inflowing edge.
+        instructionNumber--;
+      }
+      intervals.addRange(new LiveRange(instructionNumber, end));
+      if (!v.isPhi()) {
+        int constraint = v.definition.maxOutValueRegister();
+        intervals.addUse(new LiveIntervalsUse(instructionNumber, constraint));
+      }
+    } else {
+      intervals.addRange(new LiveRange(firstInstructionInBlock - 1, end));
+    }
+  }
+
+  /**
+   * Compute live ranges based on liveAtEntry sets for all basic blocks.
+   */
+  private void computeLiveRanges() {
+    for (BasicBlock block : code.topologicallySortedBlocks()) {
+      Set<Value> live = new HashSet<>();
+      List<BasicBlock> successors = block.getSuccessors();
+      Set<Value> phiOperands = new HashSet<>();
+      for (BasicBlock successor : successors) {
+        live.addAll(liveAtEntrySets.get(successor));
+        for (Phi phi : successor.getPhis()) {
+          live.remove(phi);
+          phiOperands.add(phi.getOperand(successor.getPredecessors().indexOf(block)));
+        }
+      }
+      live.addAll(phiOperands);
+      List<Instruction> instructions = block.getInstructions();
+      for (Value value : live) {
+        int end = block.entry().getNumber() + instructions.size() * INSTRUCTION_NUMBER_DELTA;
+        // Make sure that phi operands do not overlap the phi live range. The phi operand is
+        // not live until the next instruction, but only until the gap before the next instruction
+        // where the phi value takes over.
+        if (phiOperands.contains(value)) {
+          end--;
+        }
+        addLiveRange(value, block, end);
+      }
+      ListIterator<Instruction> iterator =
+          block.getInstructions().listIterator(block.getInstructions().size());
+      while (iterator.hasPrevious()) {
+        Instruction instruction = iterator.previous();
+        Value definition = instruction.outValue();
+        if (definition != null) {
+          // For instructions that define values which have no use create a live range covering
+          // the instruction. This will typically be instructions that can have side effects even
+          // if their output is not used.
+          if (definition.numberOfAllUsers() == 0) {
+            addLiveRange(definition, block, instruction.getNumber() + INSTRUCTION_NUMBER_DELTA);
+          }
+          live.remove(definition);
+        }
+        for (Value use : instruction.inValues()) {
+          if (!live.contains(use) && use.needsRegister()) {
+            live.add(use);
+            addLiveRange(use, block, instruction.getNumber());
+          }
+          if (use.needsRegister()) {
+            LiveIntervals useIntervals = use.getLiveIntervals();
+            useIntervals.addUse(
+                new LiveIntervalsUse(instruction.getNumber(), instruction.maxInValueRegister()));
+          }
+        }
+        Value use = instruction.getPreviousLocalValue();
+        if (use != null) {
+          assert use.needsRegister();
+          if (!live.contains(use)) {
+            live.add(use);
+            addLiveRange(use, block, instruction.getNumber());
+          }
+          LiveIntervals useIntervals = use.getLiveIntervals();
+          useIntervals.addUse(new LiveIntervalsUse(instruction.getNumber(), Constants.U16BIT_MAX));
+        }
+      }
+    }
+  }
+
+  private void clearUserInfo() {
+    code.blocks.forEach(BasicBlock::clearUserInfo);
+  }
+
+  private Value createValue(MoveType type, DebugInfo debugInfo) {
+    Value value = new Value(code.valueNumberGenerator.next(), NO_REGISTER, type, debugInfo);
+    value.setNeedsRegister(true);
+    return value;
+  }
+
+  private void replaceArgument(Invoke invoke, int index, Value newArgument) {
+    Value argument = invoke.arguments().get(index);
+    invoke.arguments().set(index, newArgument);
+    argument.removeUser(invoke);
+    newArgument.addUser(invoke);
+  }
+
+  private void generateArgumentMoves(Invoke invoke, InstructionListIterator insertAt) {
+    // If the invoke instruction require more than 5 registers we link the inputs because they
+    // need to be in consecutive registers.
+    if (invoke.requiredArgumentRegisters() > 5) {
+      replaceFullArgumentList(invoke, insertAt);
+    }
+  }
+
+  private Value createSentinelRegisterValue() {
+    return createValue(MoveType.OBJECT, null);
+  }
+
+  private LiveIntervals createSentinelLiveInterval(Value sentinelValue) {
+    LiveIntervals sentinelInterval = new LiveIntervals(sentinelValue);
+    sentinelInterval.addRange(LiveRange.INFINITE);
+    liveIntervals.add(sentinelInterval);
+    return sentinelInterval;
+  }
+
+  private void linkArgumentValues() {
+    Value current = preArgumentSentinelValue = createSentinelRegisterValue();
+    for (Value argument : code.collectArguments()) {
+      current.linkTo(argument);
+      current = argument;
+    }
+    Value endSentinel = createSentinelRegisterValue();
+    current.linkTo(endSentinel);
+  }
+
+  private void setupArgumentLiveIntervals() {
+    Value current = preArgumentSentinelValue;
+    LiveIntervals currentIntervals = createSentinelLiveInterval(current);
+    current = current.getNextConsecutive();
+    int index = 0;
+    while (current.getNextConsecutive() != null) {
+      // Add a live range to this value from the beginning of the block up to the argument
+      // instruction to avoid dead arguments without a range. This may create an actually empty
+      // range like [0,0[ but that works, too.
+      LiveIntervals argumentInterval = new LiveIntervals(current);
+      argumentInterval.addRange(new LiveRange(0, index * INSTRUCTION_NUMBER_DELTA));
+      index++;
+      liveIntervals.add(argumentInterval);
+      currentIntervals.link(argumentInterval);
+      currentIntervals = argumentInterval;
+      current = current.getNextConsecutive();
+    }
+    currentIntervals.link(createSentinelLiveInterval(current));
+  }
+
+  private void insertArgumentMoves() {
+    // Record the constraint that incoming arguments are in consecutive registers.
+    // We also insert sentinel registers before and after the arguments at this
+    // point.
+    linkArgumentValues();
+    setupArgumentLiveIntervals();
+    for (BasicBlock block : code.blocks) {
+      InstructionListIterator it = block.listIterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        if (instruction.isInvoke()) {
+          // Rewind so moves are inserted before the invoke.
+          it.previous();
+          // Generate the argument moves.
+          generateArgumentMoves(instruction.asInvoke(), it);
+          // Move past the move again.
+          it.next();
+        }
+      }
+    }
+  }
+
+  private void replaceFullArgumentList(Invoke invoke, InstructionListIterator insertAt) {
+    List<Value> arguments = invoke.arguments();
+    Value previous = null;
+    for (int i = 0; i < arguments.size(); i++) {
+      generateArgumentMove(invoke, i, insertAt);
+      if (previous != null) {
+        previous.linkTo(arguments.get(i));
+      }
+      previous = arguments.get(i);
+    }
+  }
+
+  private Move generateArgumentMove(Invoke invoke, int i) {
+    List<Value> arguments = invoke.arguments();
+    Value argument = arguments.get(i);
+    Value newArgument = createValue(argument.outType(), argument.getDebugInfo());
+    Move move = new Move(newArgument, argument);
+    move.setBlock(invoke.getBlock());
+    replaceArgument(invoke, i, newArgument);
+    return move;
+  }
+
+  private void generateArgumentMove(Invoke invoke, int i, InstructionListIterator insertAt) {
+    Move move = generateArgumentMove(invoke, i);
+    insertAt.add(move);
+  }
+
+  private void computeNeedsRegister() {
+    for (BasicBlock block : code.topologicallySortedBlocks()) {
+      for (Instruction instruction : block.getInstructions()) {
+        if (instruction.outValue() != null) {
+          instruction.outValue().computeNeedsRegister();
+        }
+      }
+    }
+  }
+
+  private void pinArgumentRegisters() {
+    // Special handling for arguments. Pin their register.
+    int register = 0;
+    Value current = preArgumentSentinelValue;
+    while (current != null) {
+      LiveIntervals argumentLiveInterval = current.getLiveIntervals();
+      // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
+      // the max register number.
+      register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
+      argumentLiveInterval.setRegister(register);
+      current = current.getNextConsecutive();
+    }
+    assert register == numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
+  }
+
+  private int getFreeConsecutiveRegisters(int numberOfRegister) {
+    List<Integer> unused = new ArrayList<>();
+    int first = getNextFreeRegister();
+    int current = first;
+    while ((current - first + 1) != numberOfRegister) {
+      for (int i = 0; i < numberOfRegister - 1; i++) {
+        int next = getNextFreeRegister();
+        if (next != current + 1) {
+          for (int j = first; j <= current; j++) {
+            unused.add(j);
+          }
+          first = next;
+          current = first;
+          break;
+        }
+        current++;
+      }
+    }
+    freeRegisters.addAll(unused);
+    maxRegisterNumber = Math.max(maxRegisterNumber, first + numberOfRegister - 1);
+    return first;
+  }
+
+  private int getNextFreeRegister() {
+    if (freeRegisters.size() > 0) {
+      return freeRegisters.pollFirst();
+    }
+    return nextUnusedRegisterNumber++;
+  }
+
+  private void excludeRegistersForInterval(LiveIntervals intervals, Set<Integer> excluded) {
+    int register = intervals.getRegister();
+    for (int i = 0; i < intervals.requiredRegisters(); i++) {
+      if (freeRegisters.remove(register + i)) {
+        excluded.add(register + i);
+      }
+    }
+  }
+
+  private void freeRegistersForIntervals(LiveIntervals intervals) {
+    int register = intervals.getRegister();
+    freeRegisters.add(register);
+    if (intervals.getType() == MoveType.WIDE) {
+      freeRegisters.add(register + 1);
+    }
+  }
+
+  private void takeRegistersForIntervals(LiveIntervals intervals) {
+    int register = intervals.getRegister();
+    freeRegisters.remove(register);
+    if (intervals.getType() == MoveType.WIDE) {
+      freeRegisters.remove(register + 1);
+    }
+  }
+
+  private boolean noLinkedValues() {
+    for (BasicBlock block : code.blocks) {
+      for (Phi phi : block.getPhis()) {
+        assert phi.getNextConsecutive() == null;
+      }
+      for (Instruction instruction : block.getInstructions()) {
+        for (Value value : instruction.inValues()) {
+          assert value.getNextConsecutive() == null;
+        }
+        assert instruction.outValue() == null ||
+            instruction.outValue().getNextConsecutive() == null;
+      }
+    }
+    return true;
+  }
+
+  public void print(CfgPrinter printer, String title) {
+    printer.begin("intervals");
+    printer.print("name \"").append(title).append("\"").ln();
+    PriorityQueue<LiveIntervals> sortedIntervals =
+        new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+    sortedIntervals.addAll(liveIntervals);
+    for (LiveIntervals interval = sortedIntervals.poll();
+        interval != null;
+        interval = sortedIntervals.poll()) {
+      Value value = interval.getValue();
+      if (interval.getRanges().get(0).isInfinite()) {
+        // Skip argument sentinels.
+        continue;
+      }
+      interval.print(printer, value.getNumber(), value.getNumber());
+    }
+    printer.end("intervals");
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder("Live ranges:\n");
+    for (LiveIntervals intervals : liveIntervals) {
+      Value value = intervals.getValue();
+      builder.append(value);
+      builder.append(" ");
+      builder.append(intervals);
+    }
+    builder.append("\nLive range ascii art: \n");
+    for (LiveIntervals intervals : liveIntervals) {
+      Value value = intervals.getValue();
+      if (intervals.getRegister() == NO_REGISTER) {
+        StringUtils.appendRightPadded(builder, value + " (no reg): ", 20);
+      } else {
+        StringUtils.appendRightPadded(builder, value + " r" + intervals.getRegister() + ": ", 20);
+      }
+      builder.append("|");
+      builder.append(intervals.toAscciArtString());
+      builder.append("\n");
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
new file mode 100644
index 0000000..f1d3b9e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -0,0 +1,467 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import static com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator.NO_REGISTER;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.CfgPrinter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+public class LiveIntervals {
+
+  private final Value value;
+  private LiveIntervals nextConsecutive;
+  private LiveIntervals previousConsecutive;
+  private LiveIntervals splitParent;
+  private List<LiveIntervals> splitChildren = new ArrayList<>();
+  private List<LiveRange> ranges = new ArrayList<>();
+  private TreeSet<LiveIntervalsUse> uses = new TreeSet<>();
+  private int numberOfConsecutiveRegisters = -1;
+  private int register = NO_REGISTER;
+  private LiveIntervals hint;
+  private boolean spilled = false;
+
+  // Only registers up to and including the registerLimit are allowed for this interval.
+  private int registerLimit = Constants.U16BIT_MAX;
+
+  // Max register used for any of the non-spilled splits for these live intervals or for any of the
+  // live intervals that this live interval is connected to by phi moves. This is used to
+  // conservatively determine if it is safe to use rematerialization for this value.
+  private int maxNonSpilledRegister = NO_REGISTER;
+
+  LiveIntervals(Value value) {
+    this.value = value;
+    splitParent = this;
+    value.setLiveIntervals(this);
+  }
+
+  LiveIntervals(LiveIntervals splitParent) {
+    this.splitParent = splitParent;
+    value = splitParent.value;
+  }
+
+  private int toInstructionPosition(int position) {
+    return position % 2 == 0 ? position : position + 1;
+  }
+
+  private int toGapPosition(int position) {
+    return position % 2 == 1 ? position : position - 1;
+  }
+
+  public MoveType getType() {
+    return value.outType();
+  }
+
+  public Value getValue() {
+    return value;
+  }
+
+  public int requiredRegisters() {
+    return getType() == MoveType.WIDE ? 2 : 1;
+  }
+
+  public void setHint(LiveIntervals intervals) {
+    hint = intervals;
+  }
+
+  public LiveIntervals getHint() {
+    return hint;
+  }
+
+  public void setSpilled(boolean value) {
+    spilled = value;
+  }
+
+  public boolean isSpilled() {
+    return spilled;
+  }
+
+  public boolean isRematerializable(LinearScanRegisterAllocator registerAllocator) {
+    if (value.isPhi() || !value.definition.isConstNumber()) {
+      return false;
+    }
+    // If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
+    // rematerialize it using a ConstNumber instruction and we use spill moves instead of
+    // rematerialization.
+    int max = registerAllocator.realRegisterNumberFromAllocated(getMaxNonSpilledRegister());
+    return max < Constants.U8BIT_MAX;
+  }
+
+  public boolean isSpilledAndRematerializable(LinearScanRegisterAllocator allocator) {
+    return isSpilled() && isRematerializable(allocator);
+  }
+
+  public void link(LiveIntervals next) {
+    assert numberOfConsecutiveRegisters == -1;
+    nextConsecutive = next;
+    next.previousConsecutive = this;
+  }
+
+  public boolean isLinked() {
+    return splitParent.previousConsecutive != null || splitParent.nextConsecutive != null;
+  }
+
+  public boolean isArgumentInterval() {
+    // TODO(ager): This is pretty indirect. We might want to have a more direct indication.
+    LiveIntervals current = splitParent;
+    while (current.previousConsecutive != null) {
+      current = current.previousConsecutive;
+    }
+    return current.ranges.get(0).isInfinite();
+  }
+
+
+  public LiveIntervals getStartOfConsecutive() {
+    LiveIntervals current = this;
+    while (current.previousConsecutive != null) {
+      current = current.previousConsecutive;
+    }
+    return current;
+  }
+
+  public LiveIntervals getNextConsecutive() {
+    return nextConsecutive;
+  }
+
+  public LiveIntervals getPreviousConsecutive() {
+    return previousConsecutive;
+  }
+
+  public int numberOfConsecutiveRegisters() {
+    LiveIntervals start = getStartOfConsecutive();
+    if (start.numberOfConsecutiveRegisters != -1) {
+      assert start.numberOfConsecutiveRegisters == computeNumberOfConsecutiveRegisters();
+      return start.numberOfConsecutiveRegisters;
+    }
+    return computeNumberOfConsecutiveRegisters();
+  }
+
+  private int computeNumberOfConsecutiveRegisters() {
+    LiveIntervals start = getStartOfConsecutive();
+    int result = 0;
+    for (LiveIntervals current = start;
+        current != null;
+        current = current.nextConsecutive) {
+      result += current.requiredRegisters();
+    }
+    start.numberOfConsecutiveRegisters = result;
+    return result;
+  }
+
+  public boolean hasSplits() {
+    return splitChildren.size() != 0;
+  }
+
+  public List<LiveIntervals> getSplitChildren() {
+    return splitChildren;
+  }
+
+  public LiveIntervals getSplitParent() {
+    return splitParent;
+  }
+
+  /**
+   * Add a live range to the intervals.
+   *
+   * @param range the range to add
+   */
+  public void addRange(LiveRange range) {
+    boolean added = tryAddRange(range);
+    assert added;
+  }
+
+  private boolean tryAddRange(LiveRange range) {
+    if (ranges.size() > 0) {
+      LiveRange lastRange = ranges.get(ranges.size() - 1);
+      if (lastRange.isInfinite()) {
+        return false;
+      }
+      int rangeStartInstructionPosition = toInstructionPosition(range.start);
+      int lastRangeEndInstructionPosition = toInstructionPosition(lastRange.end);
+      if (lastRangeEndInstructionPosition > rangeStartInstructionPosition) {
+        return false;
+      }
+      if (lastRangeEndInstructionPosition == rangeStartInstructionPosition) {
+        lastRange.end = range.end;
+        return true;
+      }
+    }
+    ranges.add(range);
+    return true;
+  }
+
+  /**
+   * Record a use for this interval.
+   */
+  public void addUse(LiveIntervalsUse use) {
+    uses.add(use);
+    updateRegisterConstraint(use.getLimit());
+  }
+
+  public void updateRegisterConstraint(int constraint) {
+    registerLimit = Math.min(registerLimit, constraint);
+  }
+
+  public TreeSet<LiveIntervalsUse> getUses() {
+    return uses;
+  }
+
+  public List<LiveRange> getRanges() {
+    return ranges;
+  }
+
+  public int getStart() {
+    assert !ranges.isEmpty();
+    return ranges.get(0).start;
+  }
+
+  public int getEnd() {
+    assert !ranges.isEmpty();
+    return ranges.get(ranges.size() - 1).end;
+  }
+
+  public int getRegister() {
+    return register;
+  }
+
+  public int getRegisterLimit() {
+    return registerLimit;
+  }
+
+  public void setRegister(int n) {
+    assert register == NO_REGISTER || register == n;
+    register = n;
+  }
+
+  private int computeMaxNonSpilledRegister() {
+    assert splitParent == this;
+    assert maxNonSpilledRegister == NO_REGISTER;
+    if (!isSpilled()) {
+      maxNonSpilledRegister = getRegister();
+    }
+    for (LiveIntervals child : splitChildren) {
+      if (!child.isSpilled()) {
+        maxNonSpilledRegister = Math.max(maxNonSpilledRegister, child.getRegister());
+      }
+    }
+    return maxNonSpilledRegister;
+  }
+
+  public void setMaxNonSpilledRegister(int i) {
+    assert i >= splitParent.maxNonSpilledRegister;
+    splitParent.maxNonSpilledRegister = i;
+  }
+
+  public int getMaxNonSpilledRegister() {
+    if (splitParent.maxNonSpilledRegister != NO_REGISTER) {
+      return splitParent.maxNonSpilledRegister;
+    }
+    return splitParent.computeMaxNonSpilledRegister();
+  }
+
+  public boolean usesRegister(int n) {
+    if (register == n || (getType() == MoveType.WIDE && register + 1 == n)) {
+      return true;
+    }
+    return false;
+  }
+
+  public void clearRegisterAssignment() {
+    register = NO_REGISTER;
+    hint = null;
+  }
+
+  public boolean overlapsPosition(int position) {
+    for (LiveRange range : ranges) {
+      if (range.start > position) {
+        // Ranges are sorted. When a range starts after position there is no overlap.
+        return false;
+      }
+      if (position < range.end) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean overlaps(LiveIntervals other) {
+    return nextOverlap(other) != -1;
+  }
+
+  public int nextOverlap(LiveIntervals other) {
+    Iterator<LiveRange> it = other.ranges.iterator();
+    LiveRange otherRange = it.next();
+    for (LiveRange range : ranges) {
+      while (otherRange.end <= range.start) {
+        if (!it.hasNext()) {
+          return -1;
+        }
+        otherRange = it.next();
+      }
+      if (otherRange.start < range.end) {
+        return otherRange.start;
+      }
+    }
+    return -1;
+  }
+
+  public int firstUseAfter(int unhandledStart) {
+    for (LiveIntervalsUse use : uses) {
+      if (use.getPosition() >= unhandledStart) {
+        return use.getPosition();
+      }
+    }
+    return Integer.MAX_VALUE;
+  }
+
+  public int getFirstUse() {
+    return uses.first().getPosition();
+  }
+
+  public LiveIntervalsUse firstUseWithConstraint() {
+    for (LiveIntervalsUse use : uses) {
+      if (use.getLimit() < Constants.U16BIT_MAX) {
+        return use;
+      }
+    }
+    return null;
+  }
+
+  public LiveIntervals splitBefore(int start) {
+    if (toInstructionPosition(start) == toInstructionPosition(getStart())) {
+      assert uses.size() == 0 || getFirstUse() != start;
+      register = NO_REGISTER;
+      return this;
+    }
+    start = toGapPosition(start);
+    LiveIntervals splitChild = new LiveIntervals(splitParent);
+    splitParent.splitChildren.add(splitChild);
+    List<LiveRange> beforeSplit = new ArrayList<>();
+    List<LiveRange> afterSplit = new ArrayList<>();
+    if (start == getEnd()) {
+      beforeSplit = ranges;
+      afterSplit.add(new LiveRange(start, start));
+    } else {
+      int rangeToSplitIndex = 0;
+      for (; rangeToSplitIndex < ranges.size(); rangeToSplitIndex++) {
+        LiveRange range = ranges.get(rangeToSplitIndex);
+        if (range.start <= start && range.end > start) {
+          break;
+        }
+        if (range.start > start) {
+          break;
+        }
+      }
+      LiveRange rangeToSplit = ranges.get(rangeToSplitIndex);
+      beforeSplit.addAll(ranges.subList(0, rangeToSplitIndex));
+      if (rangeToSplit.start < start) {
+        beforeSplit.add(new LiveRange(rangeToSplit.start, start));
+        afterSplit.add(new LiveRange(start, rangeToSplit.end));
+      } else {
+        afterSplit.add(rangeToSplit);
+      }
+      afterSplit.addAll(ranges.subList(rangeToSplitIndex + 1, ranges.size()));
+    }
+    splitChild.ranges = afterSplit;
+    ranges = beforeSplit;
+    while (!uses.isEmpty() && uses.last().getPosition() >= start) {
+      splitChild.addUse(uses.pollLast());
+    }
+    // Recompute limit after having removed uses from this interval.
+    recomputeLimit();
+    assert !ranges.isEmpty();
+    assert !splitChild.ranges.isEmpty();
+    return splitChild;
+  }
+
+  private void recomputeLimit() {
+    registerLimit = Constants.U16BIT_MAX;
+    for (LiveIntervalsUse use : uses) {
+      updateRegisterConstraint(use.getLimit());
+    }
+  }
+
+  public LiveIntervals getSplitCovering(int instructionNumber) {
+    // Check if this interval itself is covering the instruction.
+    if (getStart() <= instructionNumber && getEnd() > instructionNumber) {
+      return this;
+    }
+    // If the instruction number is not in this intervals range, we go through all split children.
+    // If we do not find a child that contains the instruction number we return the interval
+    // whose end is the instruction number. This is needed when transitioning values across
+    // control-flow boundaries.
+    LiveIntervals matchingEnd = getEnd() == instructionNumber ? this : null;
+    for (LiveIntervals splitChild : splitChildren) {
+      if (splitChild.getStart() <= instructionNumber && splitChild.getEnd() > instructionNumber) {
+        return splitChild;
+      }
+      if (splitChild.getEnd() == instructionNumber) {
+        matchingEnd = splitChild;
+      }
+    }
+    if (matchingEnd != null) {
+      return matchingEnd;
+    }
+    assert false : "Couldn't find split covering instruction position.";
+    return null;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("(cons ");
+    // Use the field here to avoid toString to have side effects.
+    builder.append(numberOfConsecutiveRegisters);
+    builder.append("): ");
+    for (LiveRange range : getRanges()) {
+      builder.append(range);
+      builder.append(" ");
+    }
+    builder.append("\n");
+    return builder.toString();
+  }
+
+  public String toAscciArtString() {
+    StringBuilder builder = new StringBuilder();
+    int current = 0;
+    for (LiveRange range : getRanges()) {
+      if (range.isInfinite()) {
+        builder.append("--- infinite ---...");
+        break;
+      }
+      for (; current < range.start; current++) {
+        builder.append(" ");
+      }
+      for (; current < range.end; current++) {
+        builder.append("-");
+      }
+    }
+    return builder.toString();
+  }
+
+  public void print(CfgPrinter printer, int number, int parentNumber) {
+    printer.append(number * 10000 + register) // range number
+        .sp().append("object") // range type
+        .sp().append(parentNumber * 10000 + getSplitParent().getRegister()) // split parent
+        .sp().append(-1); // hint
+    for (LiveRange range : getRanges()) {
+      printer.sp().append(range.toString());
+    }
+    for (LiveIntervalsUse use : getUses()) {
+      printer.sp().append(use.getPosition()).sp().append("M");
+    }
+    printer.append(" \"\"").ln();
+    int delta = 0;
+    for (LiveIntervals splitChild : splitChildren) {
+      delta += 10000;
+      splitChild.print(printer, number + delta, number);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
new file mode 100644
index 0000000..fd37af8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervalsUse.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+public class LiveIntervalsUse implements Comparable<LiveIntervalsUse> {
+  private int position;
+  private int limit;
+
+  public LiveIntervalsUse(int position, int limit) {
+    this.position = position;
+    this.limit = limit;
+  }
+
+  public int getPosition() {
+    return position;
+  }
+
+  public int getLimit() {
+    return limit;
+  }
+
+  @Override
+  public int hashCode() {
+    return position + limit * 7;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (!(other instanceof LiveIntervalsUse)) {
+      return false;
+    }
+    LiveIntervalsUse o = (LiveIntervalsUse) other;
+    return o.position == position && o.limit == limit;
+  }
+
+  @Override
+  public int compareTo(LiveIntervalsUse o) {
+    if (o.position != position) {
+      return position - o.position;
+    }
+    return limit - o.limit;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveRange.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveRange.java
new file mode 100644
index 0000000..0d7bb80
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveRange.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+class LiveRange {
+
+  public final static LiveRange INFINITE = new LiveRange(0, Integer.MAX_VALUE);
+
+  public int start;  // inclusive
+  public int end;  // exclusive
+
+  public LiveRange(int start, int end) {
+    this.start = start;
+    this.end = end;
+  }
+
+  @Override
+  public String toString() {
+    return "[" + start + ", " + end + "[";
+  }
+
+  public boolean isInfinite() {
+    return this == INFINITE;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
new file mode 100644
index 0000000..9dba6d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterAllocator.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.ir.code.Value;
+
+public interface RegisterAllocator {
+  void allocateRegisters(boolean debug);
+  int registersUsed();
+  int getRegisterForValue(Value value, int instructionNumber);
+  boolean argumentValueUsesHighRegister(Value value, int instructionNumber);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
new file mode 100644
index 0000000..9cb873e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.MoveType;
+import java.util.Map;
+import java.util.Set;
+
+// Register moves used by the spilling register allocator. These are used both for spill and
+// for phi moves and they are moves between actual registers represented by their register number.
+public class RegisterMove {
+  MoveType type;
+  int dst;
+  int src;
+  Instruction definition;
+
+  public RegisterMove(int dst, int src, MoveType type) {
+    this.dst = dst;
+    this.src = src;
+    this.type = type;
+  }
+
+  public RegisterMove(int dst, MoveType type, Instruction definition) {
+    this.dst = dst;
+    this.src = LinearScanRegisterAllocator.NO_REGISTER;
+    this.type = type;
+    assert definition.isConstInstruction();
+    this.definition = definition;
+  }
+
+
+  private boolean writes(int register) {
+    if (type == MoveType.WIDE && (dst + 1) == register) {
+      return true;
+    }
+    return dst == register;
+  }
+
+  public boolean isBlocked(Set<RegisterMove> moveSet, Map<Integer, Integer> valueMap) {
+    for (RegisterMove move : moveSet) {
+      if (move.src == LinearScanRegisterAllocator.NO_REGISTER) {
+        continue;
+      }
+      if (move != this) {
+        if (writes(valueMap.get(move.src))) {
+          return true;
+        }
+        if (move.type == MoveType.WIDE) {
+          if (writes(valueMap.get(move.src) + 1)) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return src + dst * 3 + type.hashCode() * 5 + (definition == null ? 0 : definition.hashCode());
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (!(other instanceof RegisterMove)) {
+      return false;
+    }
+    RegisterMove o = (RegisterMove) other;
+    return o.src == src && o.dst == dst && o.type == type && o.definition == definition;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
new file mode 100644
index 0000000..cc26729
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.FixedRegisterValue;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.Value;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class RegisterMoveScheduler {
+  // The set of moves to schedule.
+  private Set<RegisterMove> moveSet = new LinkedHashSet<>();
+  // Mapping to keep track of which values currently corresponds to each other.
+  // This is initially an identity map but changes as we insert moves.
+  private Map<Integer, Integer> valueMap = new HashMap<>();
+  // Number of temp registers used to schedule the moves.
+  private int usedTempRegisters = 0;
+  // Location at which to insert the scheduled moves.
+  private final InstructionListIterator insertAt;
+  // The first available temporary register.
+  private final int tempRegister;
+
+  public RegisterMoveScheduler(InstructionListIterator insertAt, int tempRegister) {
+    this.insertAt = insertAt;
+    this.tempRegister = tempRegister;
+  }
+
+  public void addMove(RegisterMove move) {
+    moveSet.add(move);
+    if (move.src != LinearScanRegisterAllocator.NO_REGISTER) {
+      valueMap.put(move.src, move.src);
+    }
+    valueMap.put(move.dst, move.dst);
+  }
+
+  public void schedule() {
+    // Worklist of moves that are ready to be inserted.
+    Deque<RegisterMove> worklist = new LinkedList<>();
+
+    // Initialize worklist with the moves that do not interfere with other moves.
+    Iterator<RegisterMove> iterator = moveSet.iterator();
+    while (iterator.hasNext()) {
+      RegisterMove move = iterator.next();
+      if (!move.isBlocked(moveSet, valueMap)) {
+        worklist.addLast(move);
+        iterator.remove();
+      }
+    }
+
+    // Process the worklist generating moves. If the worklist becomes empty while the move set
+    // still contains elements we need to use a temporary to break cycles.
+    while (!worklist.isEmpty() || !moveSet.isEmpty()) {
+      while (!worklist.isEmpty()) {
+        RegisterMove move = worklist.removeFirst();
+        assert !move.isBlocked(moveSet, valueMap);
+        // Insert the move.
+        Integer generatedDest = createMove(move);
+        // Update the value map with the information that dest can be used instead of
+        // src starting now.
+        if (move.src != LinearScanRegisterAllocator.NO_REGISTER) {
+          valueMap.put(move.src, generatedDest);
+        }
+        // Iterate and find the moves that were blocked because they need to write to
+        // one of the move src. That is now valid because the move src is preserved in dest.
+        iterator = moveSet.iterator();
+        while (iterator.hasNext()) {
+          RegisterMove other = iterator.next();
+          if (!other.isBlocked(moveSet, valueMap)) {
+            worklist.addLast(other);
+            iterator.remove();
+          }
+        }
+      }
+      if (!moveSet.isEmpty()) {
+        // The remaining moves are conflicting. Chose a move and unblock it by generating moves to
+        // temporary registers for its destination value(s).
+        RegisterMove move = pickMoveToUnblock();
+        createMoveDestToTemp(move);
+        worklist.addLast(move);
+      }
+    }
+  }
+
+  public int getUsedTempRegisters() {
+    return usedTempRegisters;
+  }
+
+  private List<RegisterMove> findMovesWithSrc(int src, MoveType type) {
+    List<RegisterMove> result = new ArrayList<>();
+    assert src != LinearScanRegisterAllocator.NO_REGISTER;
+    for (RegisterMove move : moveSet) {
+      if (move.src == LinearScanRegisterAllocator.NO_REGISTER) {
+        continue;
+      }
+      int moveSrc = valueMap.get(move.src);
+      if (moveSrc == src) {
+        result.add(move);
+      } else if (move.type == MoveType.WIDE && (moveSrc + 1) == src) {
+        result.add(move);
+      } else if (type == MoveType.WIDE && (moveSrc - 1) == src) {
+        result.add(move);
+      }
+    }
+    return result;
+  }
+
+  private Integer createMove(RegisterMove move) {
+    Instruction instruction;
+    Value to = new FixedRegisterValue(move.type, move.dst);
+    if (move.definition != null) {
+      ConstNumber number = move.definition.asConstNumber();
+      instruction = new ConstNumber(number.type, to, number.getRawValue());
+    } else {
+      Value from = new FixedRegisterValue(move.type, valueMap.get(move.src));
+      instruction = new Move(to, from);
+    }
+    insertAt.add(instruction);
+    return move.dst;
+
+  }
+
+  private void createMoveDestToTemp(RegisterMove move) {
+    // In order to unblock this move we might have to move more than one value to temporary
+    // registers if we are unlucky with the overlap for values that use two registers.
+    List<RegisterMove> movesWithSrc = findMovesWithSrc(move.dst, move.type);
+    assert movesWithSrc.size() > 0;
+    for (RegisterMove moveWithSrc : movesWithSrc) {
+      // TODO(ager): For now we always use a new temporary register whenever we have to unblock
+      // a move. The move scheduler can have multiple unblocking temps live at the same time
+      // and therefore we cannot have just one tempRegister (pair). However, we could check here
+      // if the previously used tempRegisters is still needed by any of the moves in the move set
+      // (taking the value map into account). If not, we can reuse the temp register instead
+      // of generating a new one.
+      Value to = new FixedRegisterValue(moveWithSrc.type, tempRegister + usedTempRegisters);
+      Value from = new FixedRegisterValue(moveWithSrc.type, valueMap.get(moveWithSrc.src));
+      insertAt.add(new Move(to, from));
+      valueMap.put(moveWithSrc.src, tempRegister + usedTempRegisters);
+      usedTempRegisters += moveWithSrc.type == MoveType.WIDE ? 2 : 1;
+    }
+  }
+
+  private RegisterMove pickMoveToUnblock() {
+    Iterator<RegisterMove> iterator = moveSet.iterator();
+    RegisterMove move = null;
+    // Pick a non-wide move to unblock if possible.
+    while (iterator.hasNext()) {
+      move = iterator.next();
+      if (move.type != MoveType.WIDE) {
+        break;
+      }
+    }
+    iterator.remove();
+    return move;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
new file mode 100644
index 0000000..87eedca
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import java.util.Arrays;
+
+/**
+ * Simple mapping from a register to an int value.
+ * <p>
+ * The backing for the mapping grows as needed up to a given limit. If no mapping exists for
+ * a register number the value is assumed to be Integer.MAX_VALUE.
+ */
+
+public class RegisterPositions {
+  private static final int INITIAL_SIZE = 16;
+  private int limit;
+  private int[] backing;
+
+  public RegisterPositions(int limit) {
+    this.limit = limit;
+    backing = new int[INITIAL_SIZE];
+    for (int i = 0; i < INITIAL_SIZE; i++) {
+      backing[i] = Integer.MAX_VALUE;
+    }
+  }
+
+  public void set(int index, int value) {
+    if (index >= backing.length) {
+      grow(index + 1);
+    }
+    backing[index] = value;
+  }
+
+  public int get(int index) {
+    if (index < backing.length) {
+      return backing[index];
+    }
+    assert index < limit;
+    return Integer.MAX_VALUE;
+  }
+
+  public void grow(int minSize) {
+    int size = backing.length;
+    while (size < minSize) {
+      size *= 2;
+    }
+    size = Math.min(size, limit);
+    int oldSize = backing.length;
+    backing = Arrays.copyOf(backing, size);
+    for (int i = oldSize; i < size; i++) {
+      backing[i] = Integer.MAX_VALUE;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMove.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMove.java
new file mode 100644
index 0000000..c13d23e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMove.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.ir.code.MoveType;
+
+/**
+ * A SpillMove represents either a phi move that transfers an SSA value to the SSA phi value or
+ * a spill or restore move that transfers the same SSA value between different registers because
+ * of spilling.
+ */
+class SpillMove {
+  MoveType type;
+  LiveIntervals from;
+  LiveIntervals to;
+
+  public SpillMove(MoveType type, LiveIntervals to, LiveIntervals from) {
+    this.type = type;
+    this.to = to;
+    this.from = from;
+    assert to.getRegister() != LinearScanRegisterAllocator.NO_REGISTER;
+    assert from.getRegister() != LinearScanRegisterAllocator.NO_REGISTER;
+  }
+
+  @Override
+  public int hashCode() {
+    return type.hashCode() + 3 * from.getRegister() + 5 * to.getRegister();
+  }
+
+  public void updateMaxNonSpilled() {
+    int maxFrom = from.getMaxNonSpilledRegister();
+    int maxTo = to.getMaxNonSpilledRegister();
+    if (maxFrom > maxTo) {
+      to.setMaxNonSpilledRegister(maxFrom);
+    } else {
+      from.setMaxNonSpilledRegister(maxTo);
+    }
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == this) {
+      return true;
+    }
+    if (!(other instanceof SpillMove)) {
+      return false;
+    }
+    SpillMove o = (SpillMove) other;
+    return type == o.type
+        && from.getRegister() == o.from.getRegister()
+        && to.getRegister() == o.to.getRegister()
+        && from.getSplitParent() == o.from.getSplitParent()
+        && to.getSplitParent() == o.to.getSplitParent();
+  }
+
+  @Override
+  public String toString() {
+    return to.getRegister() + " <- " + from.getRegister() + " (" + type + ")";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
new file mode 100644
index 0000000..643a3c1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -0,0 +1,291 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.regalloc;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.MoveType;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A set of spill moves and functionality to schedule and insert them in the code.
+ */
+class SpillMoveSet {
+  // Spill and restore moves on entry.
+  private final Map<Integer, Set<SpillMove>> instructionToInMoves = new HashMap<>();
+  // Spill and restore moves on exit.
+  private final Map<Integer, Set<SpillMove>> instructionToOutMoves = new HashMap<>();
+  // Phi moves.
+  private final Map<Integer, Set<SpillMove>> instructionToPhiMoves = new HashMap<>();
+  // The code into which to insert the moves.
+  private final IRCode code;
+  // The register allocator generating moves.
+  private LinearScanRegisterAllocator allocator;
+  // All registers below this number are arguments.
+  // TODO(ager): Get rid of this field, we should deal with arguments and other values that can
+  // be rematerialized differently.
+  private final int argumentRegisterLimit;
+  // Mapping from instruction numbers to the block that start with that instruction if any.
+  private final Map<Integer, BasicBlock> blockStartMap = new HashMap<>();
+  // The number of temporary registers used for parallel moves when scheduling the moves.
+  private int usedTempRegisters = 0;
+
+  public SpillMoveSet(
+      LinearScanRegisterAllocator allocator, IRCode code, int argumentRegisterLimit) {
+    this.allocator = allocator;
+    this.code = code;
+    this.argumentRegisterLimit = argumentRegisterLimit;
+    for (BasicBlock block : code.blocks) {
+      blockStartMap.put(block.entry().getNumber(), block);
+    }
+  }
+
+  /**
+   * Add a spill or restore move.
+   *
+   * <p>This is used between all interval splits. The move is only inserted if it is restoring
+   * from a spill slot at a position that is not at the start of a block. All block start
+   * moves are handled by resolution.
+   *
+   * @param i instruction number (gap number) for which to insert the move
+   * @param to interval representing the destination for the move
+   * @param from interval representating the source for the move
+   */
+  public void addSpillOrRestoreMove(int i, LiveIntervals to, LiveIntervals from) {
+    assert i % 2 == 1;
+    assert to.getSplitParent() == from.getSplitParent();
+    BasicBlock atEntryToBlock = blockStartMap.get(i + 1);
+    if (atEntryToBlock == null) {
+      addInMove(i, to, from);
+    }
+  }
+
+  /**
+   * Add a resolution move. This deals with moves in order to transfer an SSA value to another
+   * register across basic block boundaries.
+   *
+   * @param i instruction number (gap number) for which to insert the move
+   * @param to interval representing the destination for the move
+   * @param from interval representing the source for the move
+   */
+  public void addInResolutionMove(int i, LiveIntervals to, LiveIntervals from) {
+    assert to.getSplitParent() == from.getSplitParent();
+    addInMove(i, to, from);
+  }
+
+  public void addOutResolutionMove(int i, LiveIntervals to, LiveIntervals from) {
+    assert to.getSplitParent() == from.getSplitParent();
+    addOutMove(i, to, from);
+  }
+
+  /**
+   * Add a phi move to transfer an incoming SSA value to the SSA value in the destination block.
+   *
+   * @param i instruction number (gap number) for which to insert the move
+   * @param to interval representing the destination for the move
+   * @param from interval representing the source for the move
+   */
+  public void addPhiMove(int i, LiveIntervals to, LiveIntervals from) {
+    assert i % 2 == 1;
+    SpillMove move = new SpillMove(moveTypeForIntervals(to, from), to, from);
+    move.updateMaxNonSpilled();
+    instructionToPhiMoves.computeIfAbsent(i, (k) -> new LinkedHashSet<>()).add(move);
+  }
+
+  private void addInMove(int i, LiveIntervals to, LiveIntervals from) {
+    assert i % 2 == 1;
+    instructionToInMoves.computeIfAbsent(i, (k) -> new LinkedHashSet<>()).add(
+        new SpillMove(moveTypeForIntervals(to, from), to, from));
+  }
+
+  private void addOutMove(int i, LiveIntervals to, LiveIntervals from) {
+    assert i % 2 == 1;
+    instructionToOutMoves.computeIfAbsent(i, (k) -> new LinkedHashSet<>()).add(
+        new SpillMove(moveTypeForIntervals(to, from), to, from));
+  }
+
+  /**
+   * Schedule the moves added to this SpillMoveSet and insert them into the code.
+   *
+   * <p>Scheduling requires parallel move semantics for some of the moves. That can require
+   * the use of temporary registers to break cycles.
+   *
+   * @param tempRegister the first temporary register to use
+   * @return the number of temporary registers used
+   */
+  public int scheduleAndInsertMoves(int tempRegister) {
+    for (BasicBlock block : code.blocks) {
+      InstructionListIterator it = block.listIterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        int number = instruction.getNumber();
+        if (needsMovesBeforeInstruction(number)) {
+          // Move back so moves are inserted before the instruction.
+          it.previous();
+          scheduleMovesBeforeInstruction(tempRegister, number, it);
+          // Move past the instruction again.
+          it.next();
+        }
+      }
+    }
+    return usedTempRegisters;
+  }
+
+  private boolean isArgumentRegister(int register) {
+    return register < argumentRegisterLimit;
+  }
+
+  private MoveType moveTypeForIntervals(LiveIntervals to, LiveIntervals from) {
+    MoveType toType = to.getType();
+    MoveType fromType = from.getType();
+    if (toType == MoveType.OBJECT || fromType == MoveType.OBJECT) {
+      assert fromType == MoveType.OBJECT || fromType == MoveType.SINGLE;
+      assert toType == MoveType.OBJECT || toType == MoveType.SINGLE;
+      return MoveType.OBJECT;
+    }
+    assert toType == fromType;
+    return toType;
+  }
+
+  private boolean needsMovesBeforeInstruction(int i) {
+    return instructionToOutMoves.containsKey(i - 1)
+        || instructionToInMoves.containsKey(i - 1)
+        || instructionToPhiMoves.containsKey(i - 1);
+  }
+
+  private SpillMove getMoveWithSource(LiveIntervals src, Collection<SpillMove> moves) {
+    for (SpillMove move : moves) {
+      if (move.from == src) {
+        return move;
+      }
+    }
+    return null;
+  }
+
+  private SpillMove getMoveWritingSourceRegister(SpillMove inMove, Collection<SpillMove> moves) {
+    int srcRegister = inMove.from.getRegister();
+    int srcRegisters = inMove.type == MoveType.WIDE ? 2 : 1;
+    for (SpillMove move : moves) {
+      int dstRegister = move.to.getRegister();
+      int dstRegisters = move.type == MoveType.WIDE ? 2 : 1;
+      for (int s = 0; s < srcRegisters; s++) {
+        for (int d = 0; d < dstRegisters; d++) {
+          if ((dstRegister + d) == (srcRegister + s)) {
+            return move;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  // Shortcut move chains where we have a move in the in move set that moves to a
+  // location that is then moved in the out move set to its final destination.
+  //
+  // r1 <- r0 (in move set)
+  //
+  // r2 <- r1 (out move set)
+  //
+  // is replaced with
+  //
+  // r2 <- r0 (out move set)
+  //
+  // Care must be taken when there are other moves in the in move set that can interfere
+  // with the value. For example:
+  //
+  // r1 <- r0 (in move set)
+  // r0 <- r1 (in move set)
+  //
+  // r2 <- r1 (out move set)
+  //
+  // Additionally, if a phi move uses the destination of the in move it needs to stay.
+  //
+  // If such interference exists we don't rewrite the moves and parallel moves are generated
+  // to swap r1 and r0 on entry via a temporary register.
+  private void pruneParallelMoveSets(
+      Set<SpillMove> inMoves, Set<SpillMove> outMoves, Set<SpillMove> phiMoves) {
+    Iterator<SpillMove> it = inMoves.iterator();
+    while (it.hasNext()) {
+      SpillMove inMove = it.next();
+      SpillMove outMove = getMoveWithSource(inMove.to, outMoves);
+      SpillMove blockingInMove = getMoveWritingSourceRegister(inMove, inMoves);
+      SpillMove blockingPhiMove = getMoveWithSource(inMove.to, phiMoves);
+      if (outMove != null && blockingInMove == null && blockingPhiMove == null) {
+        it.remove();
+        outMove.from = inMove.from;
+      }
+    }
+  }
+
+  private void scheduleMovesBeforeInstruction(
+      int tempRegister, int instruction, InstructionListIterator insertAt) {
+    // Spill and restore moves for the incoming edge.
+    Set<SpillMove> inMoves =
+        instructionToInMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
+    removeArgumentRestores(inMoves);
+
+    // Spill and restore moves for the outgoing edge.
+    Set<SpillMove> outMoves =
+        instructionToOutMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
+    removeArgumentRestores(outMoves);
+
+    // Get the phi moves for this instruction and schedule them with the out going spill moves.
+    Set<SpillMove> phiMoves =
+        instructionToPhiMoves.computeIfAbsent(instruction - 1, (k) -> new LinkedHashSet<>());
+
+    // Remove/rewrite moves that we can guarantee will not be needed.
+    pruneParallelMoveSets(inMoves, outMoves, phiMoves);
+
+    // Schedule out and phi moves together.
+    outMoves.addAll(phiMoves);
+
+    // Perform parallel move scheduling independently for the in and out moves.
+    scheduleMoves(tempRegister, inMoves, insertAt);
+    scheduleMoves(tempRegister, outMoves, insertAt);
+  }
+
+  // Remove restore moves that restore arguments. Since argument register reuse is
+  // disallowed at this point we know that argument registers do not change value and
+  // therefore we don't have to perform spill moves. Performing spill moves will also
+  // make art reject the code because it loses type information for the argument.
+  private void removeArgumentRestores(Set<SpillMove> moves) {
+    Iterator<SpillMove> moveIterator = moves.iterator();
+    while (moveIterator.hasNext()) {
+      SpillMove move = moveIterator.next();
+      if (isArgumentRegister(move.to.getRegister())) {
+        moveIterator.remove();
+      }
+    }
+  }
+
+  private void scheduleMoves(
+      int tempRegister, Collection<SpillMove> moves, InstructionListIterator insertAt) {
+    RegisterMoveScheduler scheduler =
+        new RegisterMoveScheduler(insertAt, tempRegister);
+    for (SpillMove move : moves) {
+      // Do not generate moves to spill a value that can be rematerialized.
+      if (move.to.isSpilledAndRematerializable(allocator)) {
+        continue;
+      }
+      // Use rematerialization when possible and otherwise generate moves.
+      if (move.from.isSpilledAndRematerializable(allocator)) {
+        scheduler.addMove(
+            new RegisterMove(move.to.getRegister(), move.type, move.from.getValue().definition));
+      } else if (move.to.getRegister() != move.from.getRegister()) {
+        scheduler.addMove(
+            new RegisterMove(move.to.getRegister(), move.from.getRegister(), move.type));
+      }
+    }
+    scheduler.schedule();
+    usedTempRegisters = Math.max(usedTempRegisters, scheduler.getUsedTempRegisters());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
new file mode 100644
index 0000000..b8cbaa9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/jar/JarRegisterEffectsVisitor.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jar;
+
+import static org.objectweb.asm.Opcodes.ASM5;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.graph.UseRegistry;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class JarRegisterEffectsVisitor extends MethodVisitor {
+  private final DexType clazz;
+  private final UseRegistry registry;
+  private final JarApplicationReader application;
+
+  public JarRegisterEffectsVisitor(DexType clazz, UseRegistry registry,
+      JarApplicationReader application) {
+    super(ASM5);
+    this.clazz = clazz;
+    this.registry = registry;
+    this.application = application;
+  }
+
+  @Override
+  public void visitTypeInsn(int opcode, String name) {
+    DexType type = application.getTypeFromName(name);
+    if (opcode == org.objectweb.asm.Opcodes.NEW) {
+      registry.registerNewInstance(type);
+    } else {
+      registry.registerTypeReference(type);
+    }
+  }
+
+  @Override
+  public void visitMultiANewArrayInsn(String desc, int dims) {
+    registry.registerTypeReference(application.getTypeFromDescriptor(desc));
+  }
+
+  @Override
+  public void visitLdcInsn(Object cst) {
+    if (cst instanceof Type) {
+      registry.registerTypeReference(application.getType((Type) cst));
+    }
+  }
+
+  @Override
+  public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+    DexType ownerType = application.getTypeFromName(owner);
+    DexMethod method = application.getMethod(ownerType, name, desc);
+    switch (opcode) {
+      case Opcodes.INVOKEVIRTUAL:
+        registry.registerInvokeVirtual(method);
+        break;
+      case Opcodes.INVOKESTATIC:
+        registry.registerInvokeStatic(method);
+        break;
+      case Opcodes.INVOKEINTERFACE:
+        registry.registerInvokeInterface(method);
+        break;
+      case Opcodes.INVOKESPECIAL:
+        if (name.equals(Constants.INSTANCE_INITIALIZER_NAME) || ownerType == clazz) {
+          registry.registerInvokeDirect(method);
+        } else {
+          registry.registerInvokeSuper(method);
+        }
+        break;
+      default:
+        throw new Unreachable("Unexpected opcode " + opcode);
+    }
+  }
+
+  @Override
+  public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+    DexField field = application.getField(owner, name, desc);
+    switch (opcode) {
+      case Opcodes.GETFIELD:
+        registry.registerInstanceFieldRead(field);
+        break;
+      case Opcodes.PUTFIELD:
+        registry.registerInstanceFieldWrite(field);
+        break;
+      case Opcodes.GETSTATIC:
+        registry.registerStaticFieldRead(field);
+        break;
+      case Opcodes.PUTSTATIC:
+        registry.registerStaticFieldWrite(field);
+        break;
+      default:
+        throw new Unreachable("Unexpected opcode " + opcode);
+    }
+  }
+
+  @Override
+  public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+    registerMethodHandleType(bsm);
+
+    // Register bootstrap method arguments, only Type and MethodHandle need to be register.
+    for (Object arg : bsmArgs) {
+      if (arg instanceof Type && ((Type) arg).getSort() == Type.OBJECT) {
+        registry.registerTypeReference(application.getType((Type) arg));
+      } else if (arg instanceof Handle) {
+        registerMethodHandleType((Handle) arg);
+      }
+    }
+  }
+
+  private void registerMethodHandleType(Handle handle) {
+    switch (handle.getTag()) {
+      case Opcodes.H_GETFIELD:
+        visitFieldInsn(Opcodes.GETFIELD, handle.getOwner(), handle.getName(), handle.getDesc());
+        break;
+      case Opcodes.H_GETSTATIC:
+        visitFieldInsn(Opcodes.GETSTATIC, handle.getOwner(), handle.getName(), handle.getDesc());
+        break;
+      case Opcodes.H_PUTFIELD:
+        visitFieldInsn(Opcodes.PUTFIELD, handle.getOwner(), handle.getName(), handle.getDesc());
+        break;
+      case Opcodes.H_PUTSTATIC:
+        visitFieldInsn(Opcodes.PUTSTATIC, handle.getOwner(), handle.getName(), handle.getDesc());
+        break;
+      case Opcodes.H_INVOKEVIRTUAL:
+        visitMethodInsn(
+            Opcodes.INVOKEVIRTUAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+        break;
+      case Opcodes.H_INVOKEINTERFACE:
+        visitMethodInsn(
+            Opcodes.INVOKEINTERFACE, handle.getOwner(), handle.getName(), handle.getDesc(), true);
+        break;
+      case Opcodes.H_INVOKESPECIAL:
+        visitMethodInsn(
+            Opcodes.INVOKESPECIAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+        break;
+      case Opcodes.H_INVOKESTATIC:
+        visitMethodInsn(
+            Opcodes.INVOKESTATIC, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+        break;
+      case Opcodes.H_NEWINVOKESPECIAL:
+        visitMethodInsn(
+            Opcodes.INVOKESPECIAL, handle.getOwner(), handle.getName(), handle.getDesc(), false);
+        break;
+      default:
+        throw new Unreachable("MethodHandle tag is not supported: " + handle.getTag());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/logging/Log.java b/src/main/java/com/android/tools/r8/logging/Log.java
new file mode 100644
index 0000000..8869bf0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/logging/Log.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.logging;
+
+public class Log {
+
+  static public final boolean ENABLED = false;
+
+  static private final boolean VERBOSE_ENABLED = false;
+  static private final boolean INFO_ENABLED = true;
+  static private final boolean DEBUG_ENABLED = true;
+  static private final boolean WARN_ENABLED = true;
+
+  public static void verbose(Class from, String message, Object... arguments) {
+    if (ENABLED && VERBOSE_ENABLED && isClassEnabled(from)) {
+      log("VERB", from, message, arguments);
+    }
+  }
+
+  public static void info(Class from, String message, Object... arguments) {
+    if (ENABLED && INFO_ENABLED && isClassEnabled(from)) {
+      log("INFO", from, message, arguments);
+    }
+  }
+
+  public static void debug(Class from, String message, Object... arguments) {
+    if (ENABLED && DEBUG_ENABLED && isClassEnabled(from)) {
+      log("DBG", from, message, arguments);
+    }
+  }
+
+  public static void warn(Class from, String message, Object... arguments) {
+    if (ENABLED && WARN_ENABLED && isClassEnabled(from)) {
+      log("WARN", from, message, arguments);
+    }
+  }
+
+  private static boolean isClassEnabled(Class clazz) {
+    return true;
+  }
+
+  synchronized private static void log(String kind, Class from, String message, Object... args) {
+    if (args.length > 0) {
+      message = String.format(message, args);
+    }
+    System.out.println("[" + kind + "] {" + from.getSimpleName() + "}: " + message);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
new file mode 100644
index 0000000..9e9a1ad
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -0,0 +1,164 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.IndexedDexItem;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class ClassNameMapper {
+
+  private final ImmutableMap<String, ClassNaming> classNameMappings;
+  private ImmutableBiMap<String, String> nameMapping;
+
+  private Hashtable<Signature, Signature> signatureMap = new Hashtable<>();
+
+  ClassNameMapper(Map<String, ClassNaming> classNameMappings) {
+    this.classNameMappings = ImmutableMap.copyOf(classNameMappings);
+  }
+
+  private Signature canonicalizeSignature(Signature signature) {
+    Signature result = signatureMap.get(signature);
+    if (result != null) {
+      return result;
+    }
+    signatureMap.put(signature, signature);
+    return signature;
+  }
+
+  public MethodSignature getRenamedMethodSignature(DexMethod method) {
+    DexType[] parameters = method.proto.parameters.values;
+    String[] parameterTypes = new String[parameters.length];
+    for (int i = 0; i < parameters.length; i++) {
+      parameterTypes[i] = deobfuscateType(parameters[i].toDescriptorString());
+    }
+    String returnType = deobfuscateType(method.proto.returnType.toDescriptorString());
+
+    MethodSignature signature = new MethodSignature(method.name.toString(), returnType,
+        parameterTypes);
+    return (MethodSignature) canonicalizeSignature(signature);
+  }
+
+  public Signature getRenamedFieldSignature(DexField field) {
+    String type = deobfuscateType(field.type.toDescriptorString());
+    return canonicalizeSignature(new FieldSignature(field.name.toString(), type));
+  }
+
+  public String deobfuscateClassName(String name) {
+    ClassNaming classNaming = classNameMappings.get(name);
+    if (classNaming == null) {
+      return null;
+    }
+    return classNaming.originalName;
+  }
+
+  private String deobfuscateType(String asString) {
+    return DescriptorUtils.descriptorToJavaType(asString, this);
+  }
+
+  public ClassNaming getClassNaming(String name) {
+    return classNameMappings.get(name);
+  }
+
+  public void write(Writer writer, boolean collapseRanges) throws IOException {
+    for (ClassNaming naming : classNameMappings.values()) {
+      naming.write(writer, collapseRanges);
+    }
+  }
+
+  public void forAllClassNamings(Consumer<ClassNaming> consumer) {
+    classNameMappings.values().forEach(consumer);
+  }
+
+  @Override
+  public String toString() {
+    try {
+      StringWriter writer = new StringWriter();
+      write(writer, false);
+      return writer.toString();
+    } catch (IOException e) {
+      return e.toString();
+    }
+  }
+
+  public BiMap<String, String> getObfuscatedToOriginalMapping() {
+    if (nameMapping == null) {
+      ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.builder();
+      for (String name : classNameMappings.keySet()) {
+        builder.put(name, classNameMappings.get(name).originalName);
+      }
+      nameMapping = builder.build();
+    }
+    return nameMapping;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o instanceof ClassNameMapper
+        && classNameMappings.equals(((ClassNameMapper) o).classNameMappings);
+  }
+
+  @Override
+  public int hashCode() {
+    return 31 * classNameMappings.hashCode();
+  }
+
+  public String originalNameOf(IndexedDexItem item) {
+    if (item instanceof DexField) {
+      return lookupName(getRenamedFieldSignature((DexField) item), ((DexField) item).clazz);
+    } else if (item instanceof DexMethod) {
+      return lookupName(getRenamedMethodSignature((DexMethod) item), ((DexMethod) item).holder);
+    } else if (item instanceof DexType) {
+      return DescriptorUtils.descriptorToJavaType(((DexType) item).toDescriptorString(), this);
+    } else {
+      return item.toString();
+    }
+  }
+
+  private String lookupName(Signature signature, DexType clazz) {
+    String decoded = DescriptorUtils.descriptorToJavaType(clazz.descriptor.toString());
+    ClassNaming classNaming = getClassNaming(decoded);
+    if (classNaming == null) {
+      return decoded + " " + signature.toString();
+    }
+    MemberNaming memberNaming = classNaming.lookup(signature);
+    if (memberNaming == null) {
+      return classNaming.originalName + " " + signature.toString();
+    }
+    return classNaming.originalName + " " + memberNaming.signature.toString();
+  }
+
+  public Signature originalSignatureOf(DexMethod method) {
+    String decoded = DescriptorUtils
+        .descriptorToJavaType(method.holder.descriptor.toString());
+    MethodSignature memberSignature = getRenamedMethodSignature(method);
+    ClassNaming classNaming = getClassNaming(decoded);
+    if (classNaming == null) {
+      return memberSignature;
+    }
+    MemberNaming memberNaming = classNaming.lookup(memberSignature);
+    if (memberNaming == null) {
+      return memberSignature;
+    }
+    return memberNaming.signature;
+  }
+
+  public String originalNameOf(DexType clazz) {
+    return deobfuscateType(clazz.descriptor.toString());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
new file mode 100644
index 0000000..de80338
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ClassNameMinifier {
+
+  /**
+   * We ban classes from these prefixes from minification. This is needed as some classes
+   * in the android sdk are given a @hide annotation, which will remove them from the
+   * sdk we tree-shake and minify against. Thus, the class will not be available and hence
+   * we won't find out that it is a library class.
+   * To save space, we by default minify classes we do not have an implementation for.
+   */
+  private static final Set<String> BANNED_CLASS_PREFIXES = ImmutableSet
+      .of("Ljava", "Landroid", "Ldalvik");
+
+  private final AppInfoWithLiveness appInfo;
+  private final RootSet rootSet;
+  private final String packagePrefix;
+  private final Set<DexString> usedTypeNames = Sets.newIdentityHashSet();
+
+  private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
+  private final Map<String, NamingState> states = new HashMap<>();
+  final List<String> dictionary;
+
+  public ClassNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, String packagePrefix,
+      List<String> dictionary) {
+    this.appInfo = appInfo;
+    this.rootSet = rootSet;
+    this.packagePrefix = packagePrefix;
+    this.dictionary = dictionary;
+  }
+
+  public Map<DexType, DexString> computeRenaming() {
+    // Collect names we have to keep.
+    for (DexClass clazz : appInfo.classes()) {
+      if (rootSet.noObfuscation.contains(clazz)) {
+        assert !renaming.containsKey(clazz.type);
+        renaming.put(clazz.type, clazz.type.descriptor);
+        usedTypeNames.add(clazz.type.descriptor);
+      }
+    }
+    for (DexClass clazz : appInfo.classes()) {
+      if (!renaming.containsKey(clazz.type)) {
+        String packageName = getPackageNameFor(clazz);
+        NamingState state = getStateFor(packageName);
+        renaming.put(clazz.type, state.nextTypeName());
+      }
+    }
+    renameTypesInProtosOf(appInfo.staticInvokes);
+    renameTypesInProtosOf(appInfo.superInvokes);
+    renameTypesInProtosOf(appInfo.directInvokes);
+    renameTypesInProtosOf(appInfo.virtualInvokes);
+    appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
+
+    return Collections.unmodifiableMap(renaming);
+  }
+
+  private String getPackageNameFor(DexClass clazz) {
+    if (packagePrefix == null || rootSet.keepPackageName.contains(clazz)) {
+      return clazz.type.getPackageDescriptor();
+    } else {
+      return packagePrefix;
+    }
+  }
+
+  private NamingState getStateFor(String packageName) {
+    return states.computeIfAbsent(packageName, NamingState::new);
+  }
+
+  private void renameTypesInProtosOf(Iterable<DexMethod> methods) {
+    for (DexMethod method : methods) {
+      renameTypeWithoutClassDefinition(method.proto.returnType);
+      for (DexType type : method.proto.parameters.values) {
+        renameTypeWithoutClassDefinition(type);
+      }
+    }
+  }
+
+  private void renameTypeWithoutClassDefinition(DexType type) {
+    if (type.isArrayType()) {
+      type = type.toBaseType(appInfo.dexItemFactory);
+    }
+    if (type.isClassType() && !renaming.containsKey(type)) {
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz == null || !clazz.isLibraryClass()) {
+        if (!classIsBannedFromRenaming(type)) {
+          String packageName = packagePrefix == null ? type.getPackageDescriptor() : packagePrefix;
+          NamingState state = getStateFor(packageName);
+          renaming.put(type, state.nextTypeName());
+        }
+      }
+    }
+  }
+
+  private boolean classIsBannedFromRenaming(DexType type) {
+    String desc = type.toDescriptorString();
+    int index = desc.indexOf('/');
+    String prefix = desc.substring(0, index);
+    return index != -1 && BANNED_CLASS_PREFIXES.contains(prefix);
+  }
+
+  private void renameArrayTypeIfNeeded(DexType type) {
+    if (type.isArrayType()) {
+      DexType base = type.toBaseType(appInfo.dexItemFactory);
+      DexString value = renaming.get(base);
+      if (value != null) {
+        int dimensions = type.descriptor.numberOfLeadingSquareBrackets();
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < dimensions; i++) {
+          builder.append('[');
+        }
+        builder.append(value.toString());
+        DexString descriptor = appInfo.dexItemFactory.createString(builder.toString());
+        renaming.put(type, descriptor);
+      }
+    }
+  }
+
+  private class NamingState {
+
+    private final char[] packagePrefix;
+    private int typeCounter = 1;
+    private Iterator<String> dictionaryIterator;
+
+    NamingState(String packageName) {
+      this.packagePrefix = ("L" + packageName + (packageName.isEmpty() ? "" : "/")).toCharArray();
+      this.dictionaryIterator = dictionary.iterator();
+    }
+
+    public char[] getPackagePrefix() {
+      return packagePrefix;
+    }
+
+    protected String nextSuggestedName() {
+      StringBuilder nextName = new StringBuilder();
+      if (dictionaryIterator.hasNext()) {
+        nextName.append(getPackagePrefix()).append(dictionaryIterator.next()).append(';');
+        return nextName.toString();
+      } else {
+        return StringUtils.numberToIdentifier(packagePrefix, typeCounter++, true);
+      }
+    }
+
+    private DexString nextTypeName() {
+      DexString candidate;
+      do {
+        candidate = appInfo.dexItemFactory.createString(nextSuggestedName());
+      } while (usedTypeNames.contains(candidate));
+      return candidate;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
new file mode 100644
index 0000000..a00455f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Stores name information for a class.
+ * <p>
+ * This includes how the class was renamed and information on the classes members.
+ */
+public class ClassNaming {
+
+  public final String originalName;
+  final String renamedName;
+
+  /**
+   * Mapping from the renamed signature to the naming information for a member.
+   * <p>
+   * A renamed signature is a signature where the member's name has been obfuscated but not the type
+   * information.
+   **/
+  final Map<Signature, MemberNaming> members = new LinkedHashMap<>();
+
+  ClassNaming(String renamedName, String originalName) {
+    this.renamedName = renamedName;
+    this.originalName = originalName;
+  }
+
+  void addMemberEntry(MemberNaming entry) {
+    Signature renamedSignature = entry.renamedSignature;
+    members.put(renamedSignature, entry);
+  }
+
+  public MemberNaming lookup(Signature renamedSignature) {
+    return members.get(renamedSignature);
+  }
+
+  public MemberNaming lookupByOriginalSignature(Signature original) {
+    for (MemberNaming naming : members.values()) {
+      if (naming.signature.equals(original)) {
+        return naming;
+      }
+    }
+    return null;
+  }
+
+  public void forAllMemberNaming(Consumer<MemberNaming> consumer) {
+    members.values().forEach(consumer);
+  }
+
+  void write(Writer writer, boolean collapseRanges) throws IOException {
+    writer.append(originalName);
+    writer.append(" -> ");
+    writer.append(renamedName);
+    writer.append(":\n");
+    for (MemberNaming member : members.values()) {
+      member.write(writer, collapseRanges, true);
+    }
+  }
+
+  @Override
+  public String toString() {
+    try {
+      StringWriter writer = new StringWriter();
+      write(writer, false);
+      return writer.toString();
+    } catch (IOException e) {
+      return e.toString();
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof ClassNaming)) {
+      return false;
+    }
+
+    ClassNaming that = (ClassNaming) o;
+
+    return originalName.equals(that.originalName)
+        && renamedName.equals(that.renamedName)
+        && members.equals(that.members);
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = originalName.hashCode();
+    result = 31 * result + renamedName.hashCode();
+    result = 31 * result + members.hashCode();
+    return result;
+  }
+}
+
diff --git a/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java b/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java
new file mode 100644
index 0000000..6b283f0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ClassRenamingMapper.java
@@ -0,0 +1,98 @@
+package com.android.tools.r8.naming;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+
+/**
+ * Provides a translation between class names based on a source and target proguard map.
+ *
+ * A mapping consists of:
+ *
+ * <ul>
+ *   <li> {@link #translation} a bidirectional mapping between obfuscated names on the source
+ *   proguard map to the corresponding class in the target proguard map
+ *   <li> {@link #newClasses} a set of the unobfuscated names of classes that are in the source but
+ *   not the target map
+ *   <li> {@link #unusedNames} a set of names in the target map that are not used by the source map
+ * </ul>
+ */
+public class ClassRenamingMapper {
+
+  public static ClassRenamingMapper from(ClassNameMapper originalMap, ClassNameMapper targetMap) {
+    ImmutableBiMap.Builder<String, String> translationBuilder = ImmutableBiMap.builder();
+    ImmutableSet.Builder<String> newClasses = ImmutableSet.builder();
+
+    BiMap<String, String> sourceObfuscatedToOriginal = originalMap.getObfuscatedToOriginalMapping();
+    BiMap<String, String> sourceOriginalToObfuscated = sourceObfuscatedToOriginal.inverse();
+    BiMap<String, String> targetObfuscatedToOriginal = targetMap.getObfuscatedToOriginalMapping();
+    BiMap<String, String> targetOriginalToObfuscated = targetObfuscatedToOriginal.inverse();
+
+    for (String originalName : sourceOriginalToObfuscated.keySet()) {
+      String sourceObfuscatedName = sourceOriginalToObfuscated.get(originalName);
+      String targetObfuscatedName = targetOriginalToObfuscated.get(originalName);
+      if (targetObfuscatedName == null) {
+        newClasses.add(originalName);
+        continue;
+      }
+      translationBuilder.put(sourceObfuscatedName, targetObfuscatedName);
+    }
+
+    ImmutableBiMap<String, String> translation = translationBuilder.build();
+    ImmutableSet<String> unusedNames = ImmutableSet
+        .copyOf(Sets.difference(targetObfuscatedToOriginal.keySet(), translation.values()));
+
+    return new ClassRenamingMapper(translation, newClasses.build(), unusedNames);
+  }
+
+  /**
+   * Mapping from obfuscated class names in the source map to their counterpart in the target name
+   * map.
+   */
+  public final ImmutableBiMap<String, String> translation;
+
+  /**
+   * Set of (unobfuscated) class names that are present in the source map but not in the target map.
+   */
+  public final ImmutableSet<String> newClasses;
+
+  /**
+   * Set of (obfuscated) class names that are present in the target map but not in the source map.
+   */
+  public final ImmutableSet<String> unusedNames;
+
+  private ClassRenamingMapper(ImmutableBiMap<String, String> translation,
+      ImmutableSet<String> newClasses, ImmutableSet<String> unusedNames) {
+    this.translation = translation;
+    this.newClasses = newClasses;
+    this.unusedNames = unusedNames;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("Translation:\n\n");
+    for (String name : translation.keySet()) {
+      String newName = translation.get(name);
+      builder.append(name.equals(newName) ? "    " : " --- ")
+          .append(name)
+          .append(" -> ")
+          .append(newName)
+          .append('\n');
+    }
+    builder.append("\nNew classes:\n\n");
+    for (String name : newClasses) {
+      builder.append("    ")
+          .append(name)
+          .append('\n');
+    }
+    builder.append("\nUnused names:\n\n");
+    for (String unused : unusedNames) {
+      builder.append("    ")
+          .append(unused)
+          .append('\n');
+    }
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/DictionaryReader.java b/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
new file mode 100644
index 0000000..6c1e8e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/DictionaryReader.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+public class DictionaryReader implements AutoCloseable {
+
+  private BufferedReader reader;
+
+  public DictionaryReader(Path path) throws IOException {
+    this.reader = Files.newBufferedReader(path);
+  }
+
+  public String readName() throws IOException {
+    assert reader != null;
+
+    StringBuilder name = new StringBuilder();
+    int readCharAsInt;
+
+    while ((readCharAsInt = reader.read()) != -1) {
+      char readChar = (char) readCharAsInt;
+
+      if ((name.length() != 0 && Character.isJavaIdentifierPart(readChar))
+          || (name.length() == 0 && Character.isJavaIdentifierStart(readChar))) {
+        name.append(readChar);
+      } else {
+        if (readChar == '#') {
+          reader.readLine();
+        }
+
+        if (name.length() != 0) {
+          return name.toString();
+        }
+      }
+    }
+
+    return name.toString();
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (reader != null) {
+      reader.close();
+    }
+  }
+
+  public static List<String> readAllNames(Path path) {
+    if (path != null) {
+      Builder<String> namesBuilder = new ImmutableList.Builder<String>();
+      try (DictionaryReader reader = new DictionaryReader(path);) {
+        String name = reader.readName();
+        while (!name.isEmpty()) {
+          namesBuilder.add(name);
+          name = reader.readName();
+        }
+      } catch (IOException e) {
+        System.err.println("Unable to create dictionary from file " + path.toString());
+      }
+      return namesBuilder.build();
+    } else {
+      return ImmutableList.of();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
new file mode 100644
index 0000000..3a72512
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class FieldNameMinifier {
+
+  private final AppInfoWithSubtyping appInfo;
+  private final RootSet rootSet;
+  private final Map<DexField, DexString> renaming = new IdentityHashMap<>();
+  private final List<String> dictionary;
+  private final Map<DexType, NamingState<DexType>> states = new IdentityHashMap<>();
+
+  public FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, List<String> dictionary) {
+    this.appInfo = appInfo;
+    this.rootSet = rootSet;
+    this.dictionary = dictionary;
+  }
+
+  Map<DexField, DexString> computeRenaming() {
+    NamingState<DexType> rootState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+    // Reserve names in all classes first. We do this in subtyping order so we do not
+    // shadow a reserved field in subclasses. While there is no concept of virtual field
+    // dispatch in Java, field resolution still traverses the super type chain and external
+    // code might use a subtype to reference the field.
+    reserveNamesInSubtypes(appInfo.dexItemFactory.objectType, rootState);
+    // Next, reserve field names in interfaces. These should only be static.
+    DexType.forAllInterfaces(appInfo.dexItemFactory,
+        iface -> reserveNamesInSubtypes(iface, rootState));
+    // Now rename the rest.
+    renameFieldsInSubtypes(appInfo.dexItemFactory.objectType);
+    DexType.forAllInterfaces(appInfo.dexItemFactory, this::renameFieldsInSubtypes);
+    return renaming;
+  }
+
+  private void reserveNamesInSubtypes(DexType type, NamingState<DexType> state) {
+    DexClass holder = appInfo.definitionFor(type);
+    if (holder == null) {
+      return;
+    }
+    NamingState<DexType> newState = states.computeIfAbsent(type, t -> state.createChild());
+    reserveFieldNames(newState, holder.instanceFields(), holder.isLibraryClass());
+    reserveFieldNames(newState, holder.staticFields(), holder.isLibraryClass());
+    type.forAllExtendsSubtypes(subtype -> reserveNamesInSubtypes(subtype, newState));
+  }
+
+  private void reserveFieldNames(NamingState<DexType> state, DexEncodedField[] fields,
+      boolean isLibrary) {
+    for (DexEncodedField encodedField : fields) {
+      if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
+        DexField field = encodedField.field;
+        state.reserveName(field.name, field.type);
+      }
+    }
+  }
+
+  private void renameFieldsInSubtypes(DexType type) {
+    DexClass clazz = appInfo.definitionFor(type);
+    if (clazz == null) {
+      return;
+    }
+    NamingState<DexType> state = states.get(clazz.type);
+    assert state != null;
+    renameFields(clazz.instanceFields(), state);
+    renameFields(clazz.staticFields(), state);
+    type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
+  }
+
+  private void renameFields(DexEncodedField[] fields, NamingState<DexType> state) {
+    for (DexEncodedField encodedField : fields) {
+      DexField field = encodedField.field;
+      if (!state.isReserved(field.name, field.type)) {
+        renaming.put(field, state.assignNewNameFor(field.name, field.type, false));
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
new file mode 100644
index 0000000..9c45617
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -0,0 +1,468 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Stores renaming information for a member.
+ * <p>
+ * This includes the signature, the original name and inlining range information.
+ */
+public class MemberNaming {
+
+  private static final int UNDEFINED_START_NUMBER = -1;
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof MemberNaming)) {
+      return false;
+    }
+
+    MemberNaming that = (MemberNaming) o;
+    return signature.equals(that.signature)
+        && renamedSignature.equals(that.renamedSignature)
+        && topLevelRange.equals(that.topLevelRange)
+        && inlineInformation.equals(that.inlineInformation);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = signature.hashCode();
+    result = 31 * result + renamedSignature.hashCode();
+    result = 31 * result + inlineInformation.hashCode();
+    result = 31 * result + topLevelRange.hashCode();
+    return result;
+  }
+
+  /**
+   * Original signature of the member
+   */
+  final Signature signature;
+  /**
+   * Renamed signature where the name (but not the types) have been renamed.
+   */
+  final Signature renamedSignature;
+  public final List<InlineInformation> inlineInformation = new LinkedList<>();
+  public final Range topLevelRange;
+
+  private int collapsedStartLineNumber = UNDEFINED_START_NUMBER;
+  private int originalStartLineNumber = UNDEFINED_START_NUMBER;
+
+  MemberNaming(Signature signature, String renamedName, Range inlinedLineRange) {
+    this.signature = signature;
+    this.renamedSignature = signature.asRenamed(renamedName);
+    topLevelRange = inlinedLineRange == null ? fakeZeroRange : inlinedLineRange;
+  }
+
+  public void addInliningRange(Range inlinedRange, Signature signature, Range originalRange) {
+    inlineInformation.add(new InlineInformation(inlinedRange, originalRange, signature));
+  }
+
+  public List<Range> getInlineRanges() {
+    List<Range> inlineRanges = new ArrayList<>();
+    for (InlineInformation information : inlineInformation) {
+      if (information.isActualInlining()) {
+        inlineRanges.add(information.inlinedRange);
+      }
+    }
+    return inlineRanges;
+  }
+
+  public Signature getOriginalSignature() {
+    return signature;
+  }
+
+  public String getRenamedName() {
+    return renamedSignature.name;
+  }
+
+  public void setCollapsedStartLineNumber(int value) {
+    assert collapsedStartLineNumber == UNDEFINED_START_NUMBER;
+    collapsedStartLineNumber = value;
+  }
+
+  public boolean isMethodNaming() {
+    return signature.kind() == SignatureKind.METHOD;
+  }
+
+  private int getCollapsedStartLineNumber() {
+    return collapsedStartLineNumber;
+  }
+
+  protected void write(Writer writer, boolean collapseRanges, boolean indent) throws IOException {
+    if (indent) {
+      writer.append("    ");
+    }
+    int rangeCounter =
+        collapseRanges ? getCollapsedStartLineNumber() : InlineInformation.DO_NOT_COLLAPSE;
+    // Avoid printing the range information if there was none in the original file.
+    if (topLevelRange != fakeZeroRange || rangeCounter != UNDEFINED_START_NUMBER) {
+      if (collapseRanges) {
+        // Skip ranges for methods that are used only once, as they do not have debug information.
+        if (rangeCounter != UNDEFINED_START_NUMBER) {
+          String rangeString = Integer.toString(rangeCounter);
+          writer.append(rangeString).append(":").append(rangeString).append(":");
+        } else {
+          rangeCounter = 0;
+        }
+      } else {
+        writer.append(topLevelRange.toString());
+        writer.append(':');
+      }
+    } else {
+      // We might end up in a case where we have no line information for the top entry but still
+      // have inline ranges. Just to be sure, set rangeCounter to a useful value.
+      if (collapseRanges) {
+        rangeCounter = 0;
+      }
+    }
+    signature.write(writer);
+    if (originalStartLineNumber != UNDEFINED_START_NUMBER) {
+      // If we have original line number information, print it here.
+      String originalSourceLineString = Integer.toString(originalStartLineNumber);
+      writer.append(':')
+          .append(originalSourceLineString)
+          .append(':')
+          .append(originalSourceLineString);
+    }
+    writer.append(" -> ");
+    writer.append(renamedSignature.name);
+    writer.append("\n");
+    for (InlineInformation information : inlineInformation) {
+      assert !collapseRanges || rangeCounter >= 0;
+      if (collapseRanges && information.isActualInlining()) {
+        rangeCounter++;
+      }
+      information.write(writer, rangeCounter, indent);
+    }
+  }
+
+  @Override
+  public String toString() {
+    try {
+      StringWriter writer = new StringWriter();
+      write(writer, false, false);
+      return writer.toString();
+    } catch (IOException e) {
+      return e.toString();
+    }
+  }
+
+  public void setOriginalStartLineNumber(int originalStartLineNumber) {
+    assert this.originalStartLineNumber == UNDEFINED_START_NUMBER;
+    this.originalStartLineNumber = originalStartLineNumber;
+  }
+
+  public abstract static class Signature {
+
+    public final String name;
+
+    protected Signature(String name) {
+      this.name = name;
+    }
+
+    abstract Signature asRenamed(String renamedName);
+
+    abstract public SignatureKind kind();
+
+    @Override
+    abstract public boolean equals(Object o);
+
+    @Override
+    abstract public int hashCode();
+
+    abstract void write(Writer builder) throws IOException;
+
+    @Override
+    public String toString() {
+      try {
+        StringWriter writer = new StringWriter();
+        write(writer);
+        return writer.toString();
+      } catch (IOException e) {
+        return e.toString();
+      }
+    }
+
+    enum SignatureKind {
+      METHOD,
+      FIELD
+    }
+  }
+
+  public static class FieldSignature extends Signature {
+
+    public final String type;
+
+    public FieldSignature(String name, String type) {
+      super(name);
+      this.type = type;
+    }
+
+    public static FieldSignature fromDexField(DexField field) {
+      return new FieldSignature(field.name.toSourceString(),
+          field.type.toSourceString());
+    }
+
+    @Override
+    Signature asRenamed(String renamedName) {
+      return new FieldSignature(renamedName, type);
+    }
+
+    @Override
+    public SignatureKind kind() {
+      return SignatureKind.FIELD;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof FieldSignature)) {
+        return false;
+      }
+      FieldSignature that = (FieldSignature) o;
+      return name.equals(that.name) && type.equals(that.type);
+    }
+
+    @Override
+    public int hashCode() {
+      return name.hashCode() * 31 + type.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return type + " " + name;
+    }
+
+    @Override
+    void write(Writer writer) throws IOException {
+      writer.append(type);
+      writer.append(' ');
+      writer.append(name);
+    }
+  }
+
+  public static class MethodSignature extends Signature {
+
+    public final String type;
+    public final String[] parameters;
+
+    public MethodSignature(String name, String type, String[] parameters) {
+      super(name);
+      this.type = type;
+      this.parameters = parameters;
+    }
+
+    public static MethodSignature fromDexMethod(DexMethod method) {
+      String[] paramNames = new String[method.proto.parameters.values.length];
+      DexType[] values = method.proto.parameters.values;
+      for (int i = 0; i < values.length; i++) {
+        paramNames[i] = values[i].toSourceString();
+      }
+      return new MethodSignature(method.name.toSourceString(),
+          method.proto.returnType.toSourceString(), paramNames);
+    }
+
+    @Override
+    Signature asRenamed(String renamedName) {
+      return new MethodSignature(renamedName, type, parameters);
+    }
+
+    @Override
+    public SignatureKind kind() {
+      return SignatureKind.METHOD;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof MethodSignature)) {
+        return false;
+      }
+
+      MethodSignature that = (MethodSignature) o;
+      return type.equals(that.type)
+          && name.equals(that.name)
+          && Arrays.equals(parameters, that.parameters);
+    }
+
+    @Override
+    public int hashCode() {
+      return (type.hashCode() * 17
+          + name.hashCode()) * 31
+          + Arrays.hashCode(parameters);
+    }
+
+    @Override
+    void write(Writer writer) throws IOException {
+      writer.append(type)
+          .append(' ')
+          .append(name)
+          .append('(');
+      for (int i = 0; i < parameters.length; i++) {
+        writer.append(parameters[i]);
+        if (i < parameters.length - 1) {
+          writer.append(',');
+        }
+      }
+      writer.append(')');
+    }
+  }
+
+  public class InlineInformation {
+    static final int DO_NOT_COLLAPSE = -1;
+
+    public final Range inlinedRange;
+    public final Range originalRange;
+    public final Signature signature;
+
+    public InlineInformation(Range inlinedRange, Range originalRange, Signature signature) {
+      this.inlinedRange = inlinedRange;
+      this.originalRange = originalRange;
+      this.signature = signature;
+    }
+
+    public boolean isActualInlining() {
+      return !(originalRange instanceof SingleLineRange);
+    }
+
+    public void write(Writer writer, int collapsedRange, boolean indent) throws IOException {
+      if (indent) {
+        writer.append("    ");
+      }
+      if (collapsedRange == DO_NOT_COLLAPSE) {
+        writer.append(inlinedRange.toString());
+      } else {
+        writer.append(Range.toCollapsedString(collapsedRange));
+      }
+      writer.append(":");
+      signature.write(writer);
+      if (originalRange != null) {
+        writer.append(':')
+            .append(originalRange.toString());
+      }
+      writer.append(" -> ")
+          .append(renamedSignature.name);
+      writer.append("\n");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof InlineInformation)) {
+        return false;
+      }
+
+      InlineInformation that = (InlineInformation) o;
+
+      return inlinedRange.equals(that.inlinedRange)
+          && ((originalRange == null && that.originalRange == null)
+              || originalRange.equals(that.originalRange))
+          && signature.equals(that.signature);
+
+    }
+
+    @Override
+    public int hashCode() {
+      int result = inlinedRange.hashCode();
+      result = 31 * result + originalRange.hashCode();
+      result = 31 * result + signature.hashCode();
+      return result;
+    }
+  }
+
+  /**
+   * Represents a linenumber range.
+   */
+  public static class Range {
+
+    public final int from;
+    public final int to;
+
+    Range(int from, int to) {
+      this.from = from;
+      this.to = to;
+    }
+
+    public boolean contains(int value) {
+      return value >= from && value <= to;
+    }
+
+    public boolean isSingle() {
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return from + ":" + to;
+    }
+
+    public static String toCollapsedString(int value) {
+      return value + ":" + value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (!(o instanceof Range)) {
+        return false;
+      }
+
+      Range range = (Range) o;
+      return from == range.from && to == range.to;
+    }
+
+    @Override
+    public int hashCode() {
+      int result = from;
+      result = 31 * result + to;
+      return result;
+    }
+
+  }
+
+  /**
+   * Represents a single linenumber range (':' followed by a signle number), which
+   * is different semantically from a normal range that has the same from and to.
+   */
+  public static class SingleLineRange extends Range {
+    public SingleLineRange(int fromAndTo) {
+      super(fromAndTo, fromAndTo);
+    }
+
+    @Override
+    public boolean isSingle() {
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      return Integer.toString(from);
+    }
+  }
+
+  public final static Range fakeZeroRange = new Range(0, 0);
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
new file mode 100644
index 0000000..d7b17c6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -0,0 +1,355 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A pass to rename methods using common, short names.
+ * <p>
+ * To assign names, we model the scopes of methods names and overloading/shadowing based on the
+ * subtyping tree of classes. Such a naming scope is encoded by {@link NamingState}. It keeps
+ * track of its parent node, names that have been reserved (due to keep annotations or otherwise)
+ * and what names have been used for renaming so far.
+ * <p>
+ * As in the Dalvik VM method dispatch takes argument and return types of methods into account, we
+ * can further reuse names if the prototypes of two methods differ. For this, we store the above
+ * state separately for each proto using a map from protos to {@link NamingState.InternalState}
+ * objects. These internal state objects are also linked.
+ * <p>
+ * Name assignment happens in 4 stages. In the first stage, we record all names that are used by
+ * library classes or are flagged using a keep rule as reserved. This step also allocates the
+ * {@link NamingState} objects for library classes. We can fully allocate these objects as we
+ * never perform naming for library classes. For non-library classes, we only allocate a state
+ * for the highest non-library class, i.e., we allocate states for every direct subtype of a library
+ * class. The states at the boundary between library and program classes are referred to as the
+ * frontier states in the code.
+ * <p>
+ * When reserving names in program classes, we reserve them in the state of the corresponding
+ * frontier class. This is to ensure that the names are not used for renaming in any supertype.
+ * Thus, they will still be available in the subtype where they are reserved. Note that name
+ * reservation only blocks names from being used for minification. We assume that the input program
+ * is correctly named.
+ * <p>
+ * In stage 2, we reserve names that stem from interfaces. These are not propagated to
+ * subinterfaces or implementing classes. Instead, stage 3 makes sure to query related states when
+ * making naming decisions.
+ * <p>
+ * In stage 3, we compute minified names for all interface methods. We do this first to reduce
+ * assignment conflicts. Interfaces do not build a tree-like inheritance structure we can exploit.
+ * Thus, we have to infer the structure on the fly. For this, we compute a sets of reachable
+ * interfaces. i.e., interfaces that are related via subtyping. Based on these sets, we then
+ * find, for each method signature, the classes and interfaces this method signature is defined in.
+ * For classes, as we still use frontier states at this point, we do not have to consider subtype
+ * relations. For interfaces, we reserve the name in all reachable interfaces and thus ensure
+ * availability.
+ * <p>
+ * Name assignment in this phase is a search over all impacted naming states. Using the naming state
+ * of the interface this method first originated from, we propose names until we find a matching
+ * one. We use the naming state of the interface to not impact name availability in naming states of
+ * classes. Hence, skipping over names during interface naming does not impact their availability in
+ * the next phase.
+ * <p>
+ * In the final stage, we assign names to methods by traversing the subtype tree, now allocating
+ * separate naming states for each class starting from the frontier. In the first swoop, we allocate
+ * all non-private methods, updating naming states accordingly. In a second swoop, we then allocate
+ * private methods, as those may safely use names that are used by a public method further down in
+ * the subtyping tree.
+ * <p>
+ * Finally, the computed renamings are returned as a map from {@link DexMethod} to
+ * {@link DexString}. The MethodNameMinifier object should not be retained to ensure all
+ * intermediate state is freed.
+ * <p>
+ * TODO(herhut): Currently, we do not minify members of annotation interfaces, as this would require
+ * parsing and minification of the string arguments to annotations.
+ */
+public class MethodNameMinifier {
+
+  private final AppInfoWithSubtyping appInfo;
+  private final RootSet rootSet;
+  private final Map<DexType, NamingState<DexProto>> states = new IdentityHashMap<>();
+  private final NamingState<DexProto> globalState;
+  private MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+  private final List<String> dictionary;
+
+  public MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet,
+      List<String> dictionary) {
+    this.appInfo = appInfo;
+    this.rootSet = rootSet;
+    this.dictionary = dictionary;
+    this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
+  }
+
+  public Map<DexMethod, DexString> computeRenaming(Timing timing) {
+    // Phase 1: Reserve all the names that need to be kept and allocate linked state in the
+    //          library part.
+    timing.begin("Phase 1");
+    Map<DexType, DexType> frontierMap = new IdentityHashMap<>();
+    reserveNamesInClasses(appInfo.dexItemFactory.objectType,
+        appInfo.dexItemFactory.objectType,
+        null, frontierMap);
+    timing.end();
+    // Phase 2: Reserve all the names that are required for interfaces.
+    timing.begin("Phase 2");
+    DexType.forAllInterfaces(appInfo.dexItemFactory, iface -> {
+      reserveNamesInInterfaces(iface, frontierMap);
+    });
+    timing.end();
+    // Phase 3: Assign names to interface methods. These are assigned by finding a name that is
+    //          free in all naming states that may hold an implementation.
+    timing.begin("Phase 3");
+    Map<DexMethod, DexString> renaming = new IdentityHashMap<>();
+    assignNamesToInterfaceMethods(frontierMap, renaming, timing);
+    timing.end();
+    // Phase 4: Assign names top-down by traversing the subtype hierarchy.
+    timing.begin("Phase 4");
+    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, false, renaming);
+    timing.end();
+    // Phase 4: Do the same for private methods.
+    timing.begin("Phase 5");
+    assignNamesToClassesMethods(appInfo.dexItemFactory.objectType, true, renaming);
+    timing.end();
+
+    return renaming;
+  }
+
+  private void assignNamesToClassesMethods(DexType type, boolean doPrivates,
+      Map<DexMethod, DexString> renaming) {
+    DexClass holder = appInfo.definitionFor(type);
+    if (holder != null && !holder.isLibraryClass()) {
+      NamingState<DexProto> state = states
+          .computeIfAbsent(type, k -> states.get(holder.superType).createChild());
+      assignNamesToMethods(holder.directMethods(), state, doPrivates, renaming);
+      assignNamesToMethods(holder.virtualMethods(), state, doPrivates, renaming);
+    }
+    type.forAllExtendsSubtypes(
+        subtype -> assignNamesToClassesMethods(subtype, doPrivates, renaming));
+  }
+
+  private void assignNamesToMethods(DexEncodedMethod[] methods,
+      NamingState<DexProto> state, boolean doPrivates, Map<DexMethod, DexString> renaming) {
+    for (DexEncodedMethod encodedMethod : methods) {
+      if (encodedMethod.accessFlags.isPrivate() != doPrivates) {
+        continue;
+      }
+      DexMethod method = encodedMethod.method;
+      if (!state.isReserved(method.name, method.proto)
+          && !encodedMethod.accessFlags.isConstructor()) {
+        renaming.put(method, state.assignNewNameFor(method.name, method.proto, !doPrivates));
+      }
+    }
+  }
+
+  private Set<NamingState<DexProto>> getReachableStates(DexType type,
+      Map<DexType, DexType> frontierMap) {
+    Set<DexType> interfaces = Sets.newIdentityHashSet();
+    interfaces.add(type);
+    collectSuperInterfaces(type, interfaces);
+    collectSubInterfaces(type, interfaces);
+    Set<NamingState<DexProto>> reachableStates = new HashSet<>();
+    for (DexType iface : interfaces) {
+      // Add the interface itself
+      reachableStates.add(states.get(iface));
+      // And the frontiers that correspond to the classes that implement the interface.
+      iface.forAllImplementsSubtypes(t -> {
+        NamingState<DexProto> state = states.get(frontierMap.get(t));
+        assert state != null;
+        reachableStates.add(state);
+      });
+    }
+    return reachableStates;
+  }
+
+  private void assignNamesToInterfaceMethods(Map<DexType, DexType> frontierMap,
+      Map<DexMethod, DexString> renaming, Timing timing) {
+    // First compute a map from method signatures to a set of naming states for interfaces and
+    // frontier states of classes that implement them. We add the frontier states so that we can
+    // reserve the names for later method naming.
+    timing.begin("Compute map");
+    // A map from DexMethods to all the states linked to interfaces they appear in.
+    Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap = new HashMap<>();
+    // A map from DexMethods to all the definitions seen. Needed as the Wrapper equalizes them all.
+    Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap = new HashMap<>();
+    // A map from DexMethods to the first interface state it was seen in. Used to pick good names.
+    Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates = new HashMap<>();
+    DexType.forAllInterfaces(appInfo.dexItemFactory, iface -> {
+      assert iface.isInterface();
+      DexClass clazz = appInfo.definitionFor(iface);
+      if (clazz != null) {
+        Set<NamingState<DexProto>> collectedStates = getReachableStates(iface, frontierMap);
+        addStatesToGlobalMapForMethods(clazz.directMethods(), collectedStates, globalStateMap,
+            sourceMethodsMap, originStates, iface);
+        addStatesToGlobalMapForMethods(clazz.virtualMethods(), collectedStates, globalStateMap,
+            sourceMethodsMap, originStates, iface);
+      }
+    });
+    timing.end();
+    // Go over every method and assign a name.
+    timing.begin("Allocate names");
+    // Sort the methods by the number of dependent states, so that we use short names for methods
+    // references in many places.
+    List<Wrapper<DexMethod>> methods = new ArrayList<>(globalStateMap.keySet());
+    methods.sort((a, b) -> globalStateMap.get(b).size() - globalStateMap.get(a).size());
+    for (Wrapper<DexMethod> key : methods) {
+      DexMethod method = key.get();
+      assignNameForInterfaceMethodInAllStates(method,
+          globalStateMap.get(key),
+          sourceMethodsMap.get(key),
+          renaming,
+          originStates.get(key));
+    }
+    timing.end();
+  }
+
+  private void collectSuperInterfaces(DexType iface, Set<DexType> interfaces) {
+    DexClass clazz = appInfo.definitionFor(iface);
+    // In cases where we lack the interface's definition, we can at least look at subtypes and
+    // tie those up to get proper naming.
+    if (clazz != null) {
+      for (DexType type : clazz.interfaces.values) {
+        if (interfaces.add(type)) {
+          collectSuperInterfaces(type, interfaces);
+        }
+      }
+    }
+  }
+
+
+  private void collectSubInterfaces(DexType iface, Set<DexType> interfaces) {
+    DexClass clazz = appInfo.definitionFor(iface);
+    iface.forAllExtendsSubtypes(subtype -> {
+      assert subtype.isInterface();
+      if (interfaces.add(subtype)) {
+        collectSubInterfaces(subtype, interfaces);
+      }
+    });
+  }
+
+  private void addStatesToGlobalMapForMethods(
+      DexEncodedMethod[] methods, Set<NamingState<DexProto>> collectedStates,
+      Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap,
+      Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap,
+      Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates, DexType originInterface) {
+    for (DexEncodedMethod method : methods) {
+      Wrapper<DexMethod> key = equivalence.wrap(method.method);
+      Set<NamingState<DexProto>> stateSet = globalStateMap
+          .computeIfAbsent(key, k -> new HashSet<>());
+      stateSet.addAll(collectedStates);
+      sourceMethodsMap.computeIfAbsent(key, k -> new HashSet<>()).add(method.method);
+      originStates.putIfAbsent(key, states.get(originInterface));
+    }
+  }
+
+  private void assignNameForInterfaceMethodInAllStates(DexMethod method,
+      Set<NamingState<DexProto>> collectedStates,
+      Set<DexMethod> sourceMethods,
+      Map<DexMethod, DexString> renaming, NamingState<DexProto> originState) {
+    boolean isReserved = false;
+    if (globalState.isReserved(method.name, method.proto)) {
+      for (NamingState<DexProto> state : collectedStates) {
+        if (state.isReserved(method.name, method.proto)) {
+          isReserved = true;
+          break;
+        }
+      }
+      if (isReserved) {
+        // This method's name is reserved in at least on naming state, so reserve it everywhere.
+        for (NamingState<DexProto> state : collectedStates) {
+          state.reserveName(method.name, method.proto);
+        }
+        return;
+      }
+    }
+    // We use the origin state to allocate a name here so that we can reuse names between different
+    // unrelated interfaces. This saves some space. The alternative would be to use a global state
+    // for allocating names, which would save the work to search here.
+    DexString candidate = null;
+    do {
+      candidate = originState.assignNewNameFor(method.name, method.proto, false);
+      for (NamingState<DexProto> state : collectedStates) {
+        if (!state.isAvailable(method.name, method.proto, candidate)) {
+          candidate = null;
+          break;
+        }
+      }
+    } while (candidate == null);
+    for (NamingState<DexProto> state : collectedStates) {
+      state.addRenaming(method.name, method.proto, candidate);
+    }
+    // Rename all methods in interfaces that gave rise to this renaming.
+    for (DexMethod sourceMethod : sourceMethods) {
+      renaming.put(sourceMethod, candidate);
+    }
+  }
+
+  private void reserveNamesInClasses(DexType type, DexType libraryFrontier,
+      NamingState<DexProto> parent,
+      Map<DexType, DexType> frontierMap) {
+    assert !type.isInterface();
+    DexClass holder = appInfo.definitionFor(type);
+    NamingState<DexProto> state = allocateNamingStateAndReserve(holder, type, libraryFrontier,
+        parent, frontierMap);
+    // If this is a library class (or effectively a library class as it is missing) move the
+    // frontier forward.
+    type.forAllExtendsSubtypes(subtype -> {
+      assert !subtype.isInterface();
+      reserveNamesInClasses(subtype,
+          holder == null || holder.isLibraryClass() ? subtype : libraryFrontier,
+          state, frontierMap);
+    });
+  }
+
+  private void reserveNamesInInterfaces(DexType type, Map<DexType, DexType> frontierMap) {
+    assert type.isInterface();
+    frontierMap.put(type, type);
+    DexClass holder = appInfo.definitionFor(type);
+    allocateNamingStateAndReserve(holder, type, type, null, frontierMap);
+  }
+
+  private NamingState<DexProto> allocateNamingStateAndReserve(DexClass holder, DexType type,
+      DexType libraryFrontier,
+      NamingState<DexProto> parent,
+      Map<DexType, DexType> frontierMap) {
+    frontierMap.put(type, libraryFrontier);
+    NamingState<DexProto> state =
+        states.computeIfAbsent(libraryFrontier,
+            ignore -> parent == null
+                ? NamingState.createRoot(appInfo.dexItemFactory, dictionary)
+                : parent.createChild());
+    if (holder != null) {
+      boolean keepAll = holder.isLibraryClass() || holder.accessFlags.isAnnotation();
+      reserveNamesForMethods(holder.virtualMethods(), keepAll, state);
+      reserveNamesForMethods(holder.directMethods(), keepAll, state);
+    }
+    return state;
+  }
+
+  private void reserveNamesForMethods(DexEncodedMethod[] methods,
+      boolean keepAll, NamingState<DexProto> state) {
+    for (DexEncodedMethod method : methods) {
+      if (keepAll || rootSet.noObfuscation.contains(method)) {
+        state.reserveName(method.method.name, method.method.proto);
+        globalState.reserveName(method.method.name, method.method.proto);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
new file mode 100644
index 0000000..8c5da6f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+
+public class MinifiedNameMapPrinter {
+
+  private final DexApplication application;
+  private final NamingLens namingLens;
+  private final Set<DexType> seenTypes = Sets.newIdentityHashSet();
+
+  public MinifiedNameMapPrinter(DexApplication application, NamingLens namingLens) {
+    this.application = application;
+    this.namingLens = namingLens;
+  }
+
+  private void write(DexProgramClass clazz, PrintStream out) {
+    seenTypes.add(clazz.type);
+    DexString descriptor = namingLens.lookupDescriptor(clazz.type);
+    out.print(DescriptorUtils.descriptorToJavaType(clazz.type.descriptor.toSourceString()));
+    out.print(" -> ");
+    out.print(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
+    out.println(":");
+    write(clazz.instanceFields(), out);
+    write(clazz.staticFields(), out);
+    write(clazz.directMethods(), out);
+    write(clazz.virtualMethods(), out);
+  }
+
+  private void write(DexType type, PrintStream out) {
+    if (type.isClassType() && seenTypes.add(type)) {
+      DexString descriptor = namingLens.lookupDescriptor(type);
+      out.print(DescriptorUtils.descriptorToJavaType(type.descriptor.toSourceString()));
+      out.print(" -> ");
+      out.print(DescriptorUtils.descriptorToJavaType(descriptor.toSourceString()));
+      out.println(":");
+    }
+  }
+
+  private void write(DexEncodedField[] fields, PrintStream out) {
+    for (DexEncodedField encodedField : fields) {
+      DexField field = encodedField.field;
+      DexString renamed = namingLens.lookupName(field);
+      if (renamed != field.name) {
+        out.print("    ");
+        out.print(field.type.toSourceString());
+        out.print(" ");
+        out.print(field.name.toSourceString());
+        out.print(" -> ");
+        out.println(renamed.toSourceString());
+      }
+    }
+  }
+
+  private void write(DexEncodedMethod[] methods, PrintStream out) {
+    for (DexEncodedMethod encodedMethod : methods) {
+      DexMethod method = encodedMethod.method;
+      DexString renamed = namingLens.lookupName(method);
+      if (renamed != method.name) {
+        MethodSignature signature = MethodSignature.fromDexMethod(method);
+        out.print("    ");
+        out.print(signature);
+        out.print(" -> ");
+        out.println(renamed.toSourceString());
+      }
+    }
+  }
+
+  public void write(PrintStream out) {
+    // First write out all classes that have been renamed.
+    application.classes().forEach(clazz -> write(clazz, out));
+    // Now write out all types only mentioned in descriptors that have been renamed.
+    namingLens.forAllRenamedTypes(type -> write(type, out));
+  }
+
+  public void write(Path destination) throws IOException {
+    PrintStream out = new PrintStream(Files.newOutputStream(destination), true,
+        StandardCharsets.UTF_8.name());
+    write(out);
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
new file mode 100644
index 0000000..18099fb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -0,0 +1,134 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Iterables;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class Minifier {
+
+  private final AppInfoWithLiveness appInfo;
+  private final RootSet rootSet;
+  private final InternalOptions options;
+
+  public Minifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) {
+    this.appInfo = appInfo;
+    this.rootSet = rootSet;
+    this.options = options;
+  }
+
+  public NamingLens run(Timing timing) {
+    assert !options.skipMinification;
+    if (!options.allowAccessModification) {
+      throw new CompilationError("Minification requires allowaccessmodification.");
+    }
+    timing.begin("MinifyClasses");
+    Map<DexType, DexString> classRenaming =
+        new ClassNameMinifier(
+                appInfo, rootSet, options.packagePrefix, options.classObfuscationDictionary)
+            .computeRenaming();
+    timing.end();
+    timing.begin("MinifyMethods");
+    Map<DexMethod, DexString> methodRenaming =
+        new MethodNameMinifier(appInfo, rootSet, options.obfuscationDictionary)
+            .computeRenaming(timing);
+    timing.end();
+    timing.begin("MinifyFields");
+    Map<DexField, DexString> fieldRenaming =
+        new FieldNameMinifier(appInfo, rootSet, options.obfuscationDictionary).computeRenaming();
+    timing.end();
+    return new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
+  }
+
+  private static class MinifiedRenaming extends NamingLens {
+
+    private final AppInfo appInfo;
+    private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+
+    private MinifiedRenaming(Map<DexType, DexString> classRenaming,
+        Map<DexMethod, DexString> methodRenaming, Map<DexField, DexString> fieldRenaming,
+        AppInfo appInfo) {
+      this.appInfo = appInfo;
+      renaming.putAll(classRenaming);
+      renaming.putAll(methodRenaming);
+      renaming.putAll(fieldRenaming);
+    }
+
+    @Override
+    public DexString lookupDescriptor(DexType type) {
+      return renaming.getOrDefault(type, type.descriptor);
+    }
+
+    @Override
+    public DexString lookupName(DexMethod method) {
+      return renaming.getOrDefault(method, method.name);
+    }
+
+    @Override
+    public DexString lookupName(DexField field) {
+      return renaming.getOrDefault(field, field.name);
+    }
+
+    @Override
+    void forAllRenamedTypes(Consumer<DexType> consumer) {
+      Iterables.filter(renaming.keySet(), DexType.class).forEach(consumer);
+    }
+
+    /**
+     * Checks whether the target is precise enough to be translated,
+     * <p>
+     * We only track the renaming of actual definitions, Thus, if we encounter a method id that
+     * does not directly point at a definition, we won't find the actual renaming. To avoid
+     * dispatching on every lookup, we assume that the tree has been fully dispatched by
+     * {@link MemberRebindingAnalysis}.
+     * <p>
+     * Library methods are excluded from this check, as those are never renamed.
+     */
+    @Override
+    public boolean checkTargetCanBeTranslated(DexMethod item) {
+      if (item.holder.isArrayType()) {
+        // Array methods are never renamed, so do not bother to check.
+        return true;
+      }
+      DexClass holder = appInfo.definitionFor(item.holder);
+      if (holder == null || holder.isLibraryClass()) {
+        return true;
+      }
+      // We don't know which invoke type this method is used for, so checks that it has been
+      // rebound either way.
+      DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
+      DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
+      DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
+      DexClass staticTargetHolder =
+          staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
+      DexClass directTargetHolder =
+          directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
+      DexClass virtualTargetHolder =
+          virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
+      return directTarget == null && staticTarget == null && virtualTarget == null
+          || virtualTarget != null && virtualTarget.method == item
+          || directTarget != null && directTarget.method == item
+          || staticTarget != null && staticTarget.method == item
+          || directTargetHolder != null && directTargetHolder.isLibraryClass()
+          || virtualTargetHolder != null && virtualTargetHolder.isLibraryClass()
+          || staticTargetHolder != null && staticTargetHolder.isLibraryClass();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
new file mode 100644
index 0000000..604190c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import java.util.function.Consumer;
+
+/**
+ * Implements a translation of the Dex graph from original names to new names produced by
+ * the {@link Minifier}.
+ * <p>
+ * The minifier does not actually rename classes and members but instead only produces a mapping
+ * from original ids to renamed ids. When writing the file, the graph has to be interpreted with
+ * that mapping in mind, i.e., it should be looked at only through this lens.
+ * <p>
+ * The translation relies on members being statically dispatched to actual definitions, as done
+ * by the {@link MemberRebindingAnalysis} optimization.
+ */
+public abstract class NamingLens {
+
+  public abstract DexString lookupDescriptor(DexType type);
+
+  public abstract DexString lookupName(DexMethod method);
+
+  public abstract DexString lookupName(DexField field);
+
+  public static NamingLens getIdentityLens() {
+    return new IdentityLens();
+  }
+
+  public final boolean isIdentityLens() {
+    return this instanceof IdentityLens;
+  }
+
+  abstract void forAllRenamedTypes(Consumer<DexType> consumer);
+
+  /**
+   * Checks whether the target will be translated properly by this lense.
+   * <p>
+   * Normally, this means that the target corresponds to an actual definition that has been
+   * renamed. For identity renamings, we are more relaxed, as no targets will be translated
+   * anyway.
+   */
+  public abstract boolean checkTargetCanBeTranslated(DexMethod item);
+
+  private static class IdentityLens extends NamingLens {
+
+    private IdentityLens() {
+      // Intentionally left empty.
+    }
+
+    @Override
+    public DexString lookupDescriptor(DexType type) {
+      return type.descriptor;
+    }
+
+    @Override
+    public DexString lookupName(DexMethod method) {
+      return method.name;
+    }
+
+    @Override
+    public DexString lookupName(DexField field) {
+      return field.name;
+    }
+
+    @Override
+    void forAllRenamedTypes(Consumer<DexType> consumer) {
+      // Intentionally left empty.
+    }
+
+    public boolean checkTargetCanBeTranslated(DexMethod item) {
+      return true;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
new file mode 100644
index 0000000..0474c1a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.graph.CanonicalizedDexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class NamingState<T extends CanonicalizedDexItem> {
+
+  private final NamingState<T> parent;
+  private final Map<T, InternalState> usedNames = new IdentityHashMap<>();
+  private final DexItemFactory itemFactory;
+  private final List<String> dictionary;
+
+  public static <T extends CanonicalizedDexItem> NamingState<T> createRoot(
+      DexItemFactory itemFactory, List<String> dictionary) {
+    return new NamingState<>(null, itemFactory, dictionary);
+  }
+
+  private NamingState(NamingState<T> parent, DexItemFactory itemFactory, List<String> dictionary) {
+    this.parent = parent;
+    this.itemFactory = itemFactory;
+    this.dictionary = dictionary;
+  }
+
+  public NamingState<T> createChild() {
+    return new NamingState<>(this, itemFactory, dictionary);
+  }
+
+  private InternalState findInternalStateFor(T proto) {
+    InternalState result = usedNames.get(proto);
+    if (result == null && parent != null) {
+      result = parent.findInternalStateFor(proto);
+    }
+    return result;
+  }
+
+  private InternalState getOrCreateInternalStateFor(T proto) {
+    // TODO(herhut): Maybe allocate these sparsely and search via state chain.
+    InternalState result = usedNames.get(proto);
+    if (result == null) {
+      if (parent != null) {
+        InternalState parentState = parent.getOrCreateInternalStateFor(proto);
+        result = parentState.createChild();
+      } else {
+        result = new InternalState(itemFactory, null, dictionary);
+      }
+      usedNames.put(proto, result);
+    }
+    return result;
+  }
+
+  public DexString getAssignedNameFor(DexString name, T proto) {
+    InternalState state = findInternalStateFor(proto);
+    if (state == null) {
+      return null;
+    }
+    return state.getAssignedNameFor(name);
+  }
+
+  public DexString assignNewNameFor(DexString original, T proto, boolean markAsUsed) {
+    DexString result = getAssignedNameFor(original, proto);
+    if (result == null) {
+      InternalState state = getOrCreateInternalStateFor(proto);
+      result = state.getNameFor(original, markAsUsed);
+    }
+    return result;
+  }
+
+  public void reserveName(DexString name, T proto) {
+    InternalState state = getOrCreateInternalStateFor(proto);
+    state.reserveName(name);
+  }
+
+  public boolean isReserved(DexString name, T proto) {
+    InternalState state = findInternalStateFor(proto);
+    if (state == null) {
+      return false;
+    }
+    return state.isReserved(name);
+  }
+
+  public boolean isAvailable(DexString original, T proto, DexString candidate) {
+    InternalState state = findInternalStateFor(proto);
+    if (state == null) {
+      return true;
+    }
+    assert state.getAssignedNameFor(original) != candidate;
+    return state.isAvailable(candidate);
+  }
+
+  public void addRenaming(DexString original, T proto, DexString newName) {
+    InternalState state = getOrCreateInternalStateFor(proto);
+    state.addRenaming(original, newName);
+  }
+
+  private static class InternalState {
+
+    private static int INITIAL_NAME_COUNT = 1;
+    private final static char[] EMPTY_CHAR_ARRARY = new char[0];
+
+    protected final DexItemFactory itemFactory;
+    private final InternalState parentInternalState;
+    private Set<DexString> reservedNames = null;
+    private Map<DexString, DexString> renamings = null;
+    private int nameCount;
+    private final Iterator<String> dictionaryIterator;
+
+    private InternalState(DexItemFactory itemFactory, InternalState parentInternalState,
+        Iterator<String> dictionaryIterator) {
+      this.itemFactory = itemFactory;
+      this.parentInternalState = parentInternalState;
+      this.nameCount =
+          parentInternalState == null ? INITIAL_NAME_COUNT : parentInternalState.nameCount;
+      this.dictionaryIterator = dictionaryIterator;
+    }
+
+    private InternalState(DexItemFactory itemFactory, InternalState parentInternalState,
+        List<String> dictionary) {
+      this(itemFactory, parentInternalState, dictionary.iterator());
+    }
+
+    private boolean isReserved(DexString name) {
+      return (reservedNames != null && reservedNames.contains(name))
+          || (parentInternalState != null && parentInternalState.isReserved(name));
+    }
+
+    private boolean isAvailable(DexString name) {
+      return !(renamings != null && renamings.containsValue(name))
+          && !(reservedNames != null && reservedNames.contains(name))
+          && (parentInternalState == null || parentInternalState.isAvailable(name));
+    }
+
+    public InternalState createChild() {
+      return new InternalState(itemFactory, this, dictionaryIterator);
+    }
+
+    public void reserveName(DexString name) {
+      if (reservedNames == null) {
+        reservedNames = Sets.newIdentityHashSet();
+      }
+      reservedNames.add(name);
+    }
+
+    public DexString getAssignedNameFor(DexString original) {
+      DexString result = renamings == null ? null : renamings.get(original);
+      if (result == null && parentInternalState != null) {
+        result = parentInternalState.getAssignedNameFor(original);
+      }
+      return result;
+    }
+
+    public DexString getNameFor(DexString original, boolean markAsUsed) {
+      DexString name = getAssignedNameFor(original);
+      if (name != null) {
+        return name;
+      }
+      do {
+        name = itemFactory.createString(nextSuggestedName());
+      } while (!isAvailable(name));
+      if (markAsUsed) {
+        addRenaming(original, name);
+      }
+      return name;
+    }
+
+    public void addRenaming(DexString original, DexString newName) {
+      if (renamings == null) {
+        renamings = HashBiMap.create();
+      }
+      renamings.put(original, newName);
+    }
+
+    protected String nextSuggestedName() {
+      if (dictionaryIterator.hasNext()) {
+        return dictionaryIterator.next();
+      } else {
+        return StringUtils.numberToIdentifier(EMPTY_CHAR_ARRARY, nameCount++, false);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
new file mode 100644
index 0000000..564e6de
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -0,0 +1,422 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Range;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.MemberNaming.SingleLineRange;
+import com.google.common.collect.ImmutableMap;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * Parses a Proguard mapping file and produces mappings from obfuscated class names to the original
+ * name and from obfuscated member signatures to the original members the obfuscated member
+ * was formed of.
+ * <p>
+ * The expected format is as follows
+ * <p>
+ * original-type-name ARROW obfuscated-type-name COLON starts a class mapping
+ * description and maps original to obfuscated.
+ * <p>
+ * followed by one or more of
+ * <p>
+ * signature ARROW name
+ * <p>
+ * which maps the member with the given signature to the new name. This mapping is not
+ * bidirectional as member names are overloaded by signature. To make it bidirectional, we extend
+ * the name with the signature of the original member.
+ * <p>
+ * Due to inlining, we might have the above prefixed with a range (two numbers separated by :).
+ * <p>
+ * range COLON signature ARROW name
+ * <p>
+ * This has the same meaning as the above but also encodes the line number range of the member. This
+ * may be followed by multiple inline mappings of the form
+ * <p>
+ * range COLON signature COLON range ARROW name
+ * <p>
+ * to identify that signature was inlined from the second range to the new line numbers in the first
+ * range. This is then followed by information on the call trace to where the member was inlined.
+ * These entries have the form
+ * <p>
+ * range COLON signature COLON number ARROW name
+ * <p>
+ * and are currently only stored to be able to reproduce them later.
+ */
+public class ProguardMapReader implements AutoCloseable {
+
+  private final BufferedReader reader;
+
+  public void close() throws IOException {
+    if (reader != null) {
+      reader.close();
+    }
+  }
+
+  private ProguardMapReader(BufferedReader reader) throws IOException {
+    this.reader = reader;
+  }
+
+  public static ClassNameMapper mapperFromInputStream(InputStream in) throws IOException {
+    BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF8"));
+    try (ProguardMapReader proguardReader = new ProguardMapReader(reader)) {
+      return proguardReader.parse();
+    }
+  }
+
+  public static ClassNameMapper mapperFromFile(Path path) throws IOException {
+    return mapperFromInputStream(Files.newInputStream(path));
+  }
+
+  public static ClassNameMapper mapperFromString(String contents) throws IOException {
+    return mapperFromInputStream(
+        new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)));
+  }
+
+  // Internal parser state
+  private int lineNo = 0;
+  private int lineOffset = 0;
+  private String line;
+
+  private char peek() {
+    return peek(0);
+  }
+
+  private char peek(int distance) {
+    return lineOffset + distance < line.length()
+        ? line.charAt(lineOffset + distance)
+        : '\n';
+  }
+
+  private char next() {
+    try {
+      return line.charAt(lineOffset++);
+    } catch (ArrayIndexOutOfBoundsException e) {
+      throw new ParseException("Unexpected end of line");
+    }
+  }
+
+  private boolean nextLine() throws IOException {
+    if (line.length() != lineOffset) {
+      throw new ParseException("Expected end of line");
+    }
+    return skipLine();
+  }
+
+  private boolean skipLine() throws IOException {
+    lineNo++;
+    lineOffset = 0;
+    line = reader.readLine();
+    return hasLine();
+  }
+
+  private boolean hasLine() {
+    return line != null;
+  }
+
+  // Helpers for common pattern
+  private void skipWhitespace() {
+    while (Character.isWhitespace(peek())) {
+      next();
+    }
+  }
+
+  private char expect(char c) {
+    if (next() != c) {
+      throw new ParseException("Expected '" + c + "'");
+    }
+    return c;
+  }
+
+  public ClassNameMapper parse() throws IOException {
+    // Read the first line.
+    line = reader.readLine();
+    Map<String, ClassNaming> classNames = parseClassMappings();
+    return new ClassNameMapper(classNames);
+  }
+
+  // Parsing of entries
+
+  private Map<String, ClassNaming> parseClassMappings() throws IOException {
+    ImmutableMap.Builder<String, ClassNaming> builder = ImmutableMap.builder();
+    while (hasLine()) {
+      String before = parseType(false);
+      skipWhitespace();
+      // Workaround for proguard map files that contain entries for package-info.java files.
+      if (!acceptArrow()) {
+        // If this was a package-info line, we parsed the "package" string.
+        if (!before.endsWith("package") || !acceptString("-info")) {
+          throw new ParseException("Expected arrow after class name " + before);
+        }
+        skipLine();
+        continue;
+      }
+      skipWhitespace();
+      String after = parseType(false);
+      expect(':');
+      ClassNaming currentClass = new ClassNaming(after, before);
+      builder.put(after, currentClass);
+      if (nextLine()) {
+        parseMemberMappings(currentClass);
+      }
+    }
+    return builder.build();
+  }
+
+  private void parseMemberMappings(ClassNaming currentClass) throws IOException {
+    MemberNaming current = null;
+    Range previousInlineRange = null;
+    Signature previousSignature = null;
+    String previousRenamedName = null;
+    List<Consumer<MemberNaming>> collectedInfos = new ArrayList<>(10);
+
+    while (Character.isWhitespace(peek())) {
+      skipWhitespace();
+      Range inlinedLineRange = maybeParseRange();
+      if (inlinedLineRange != null) {
+        expect(':');
+      }
+      Signature signature = parseSignature();
+      Range originalLineRange;
+      if (peek() == ':') {
+        // This is an inlining definition
+        next();
+        originalLineRange = maybeParseRange();
+        if (originalLineRange == null) {
+          if (!skipLine()) {
+            break;
+          }
+          continue;
+        }
+      } else {
+        originalLineRange = null;
+      }
+      skipWhitespace();
+      skipArrow();
+      skipWhitespace();
+      String renamedName = parseMethodName();
+      // If there is no line number information at the front or if it changes, we have a new
+      // segment. Likewise, if the range information on the right hand side has two values, we have
+      // a new segment.
+      if (inlinedLineRange == null
+          || previousInlineRange == null
+          || originalLineRange == null
+          || !previousInlineRange.equals(inlinedLineRange)
+          || !originalLineRange.isSingle()) {
+        // We are at a range boundary. Either we parsed something new, or an inline frame is over.
+        // We detect this by checking whether the previous signature matches the one of current.
+        if (current == null || !previousSignature.equals(current.signature)) {
+          if (collectedInfos.size() == 1) {
+            current = new MemberNaming(previousSignature, previousRenamedName, previousInlineRange);
+            currentClass.addMemberEntry(current);
+          } else {
+            if (Log.ENABLED && !collectedInfos.isEmpty()) {
+              Log.warn(getClass(),
+                  "More than one member entry that forms a new group at %s %s -> %s",
+                  previousInlineRange, previousSignature, previousRenamedName);
+            }
+          }
+        } else {
+          MemberNaming finalCurrent = current;
+          collectedInfos.forEach(info -> info.accept(finalCurrent));
+        }
+        collectedInfos.clear();
+      }
+      // Defer the creation of the info until we have the correct member.
+      collectedInfos.add((m) -> m.addInliningRange(inlinedLineRange, signature, originalLineRange));
+      // We have parsed the whole line, move on.
+      previousInlineRange = inlinedLineRange;
+      previousSignature = signature;
+      previousRenamedName = renamedName;
+      if (!nextLine()) {
+        break;
+      }
+    }
+    // Process the last round if lines have been read.
+    if (current == null || !previousSignature.equals(current.signature)) {
+      if (collectedInfos.size() == 1) {
+        current = new MemberNaming(previousSignature, previousRenamedName, previousInlineRange);
+        currentClass.addMemberEntry(current);
+      }
+    } else {
+      MemberNaming finalCurrent = current;
+      collectedInfos.forEach(info -> info.accept(finalCurrent));
+    }
+    collectedInfos.clear();
+  }
+
+  // Parsing of components
+
+  private void skipIdentifier(boolean allowInit) {
+    boolean isInit = false;
+    if (allowInit && peek() == '<') {
+      // swallow the leading < character
+      next();
+      isInit = true;
+    }
+    if (!Character.isJavaIdentifierStart(peek())) {
+      throw new ParseException("Identifier expected");
+    }
+    next();
+    while (Character.isJavaIdentifierPart(peek())) {
+      next();
+    }
+    if (isInit) {
+      expect('>');
+    }
+    if (Character.isJavaIdentifierPart(peek())) {
+      throw new ParseException("End of identifier expected");
+    }
+  }
+
+  // Cache for canonicalizing strings.
+  // This saves 10% of heap space for large programs.
+  final HashMap<String, String> cache = new HashMap<>();
+
+  private String substring(int start) {
+    String result = line.substring(start, lineOffset);
+    if (cache.containsKey(result)) {
+      return cache.get(result);
+    }
+    cache.put(result, result);
+    return result;
+  }
+
+  private String parseMethodName() {
+    int startPosition = lineOffset;
+    skipIdentifier(true);
+    while (peek() == '.') {
+      next();
+      skipIdentifier(true);
+    }
+    return substring(startPosition);
+  }
+
+  private String parseType(boolean allowArray) {
+    int startPosition = lineOffset;
+    skipIdentifier(false);
+    while (peek() == '.') {
+      next();
+      skipIdentifier(false);
+    }
+    if (allowArray) {
+      while (peek() == '[') {
+        next();
+        expect(']');
+      }
+    }
+    return substring(startPosition);
+  }
+
+  private Signature parseSignature() {
+    String type = parseType(true);
+    expect(' ');
+    String name = parseMethodName();
+    Signature signature;
+    if (peek() == '(') {
+      next();
+      String[] arguments;
+      if (peek() == ')') {
+        arguments = new String[0];
+      } else {
+        List<String> items = new LinkedList<>();
+        items.add(parseType(true));
+        while (peek() != ')') {
+          expect(',');
+          items.add(parseType(true));
+        }
+        arguments = items.toArray(new String[items.size()]);
+      }
+      expect(')');
+      signature = new MethodSignature(name, type, arguments);
+    } else {
+      signature = new FieldSignature(name, type);
+    }
+    return signature;
+  }
+
+  private void skipArrow() {
+    expect('-');
+    expect('>');
+  }
+
+  private boolean acceptArrow() {
+    if (peek() == '-' && peek(1) == '>') {
+      next();
+      next();
+      return true;
+    }
+    return false;
+  }
+
+  private boolean acceptString(String s) {
+    for (int i = 0; i < s.length(); i++) {
+      if (peek(i) != s.charAt(i)) {
+        return false;
+      }
+    }
+    for (int i = 0; i < s.length(); i++) {
+      next();
+    }
+    return true;
+  }
+
+  private Range maybeParseRange() {
+    if (!Character.isDigit(peek())) {
+      return null;
+    }
+    int from = parseNumber();
+    if (peek() != ':') {
+      return new SingleLineRange(from);
+    }
+    expect(':');
+    int to = parseNumber();
+    return new Range(from, to);
+  }
+
+  private int parseNumber() {
+    int result = 0;
+    if (!Character.isDigit(peek())) {
+      throw new ParseException("Number expected");
+    }
+    do {
+      result *= 10;
+      result += Character.getNumericValue(next());
+    } while (Character.isDigit(peek()));
+    return result;
+  }
+
+  private class ParseException extends RuntimeException {
+
+    private final int lineNo;
+    private final int lineOffset;
+    private final String msg;
+
+    ParseException(String msg) {
+      lineNo = ProguardMapReader.this.lineNo;
+      lineOffset = ProguardMapReader.this.lineOffset;
+      this.msg = msg;
+    }
+
+    public String toString() {
+      return "Parse error [" + lineNo + ":" + lineOffset + "] " + msg;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
new file mode 100644
index 0000000..f36e8c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -0,0 +1,119 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class BridgeMethodAnalysis {
+
+  private final GraphLense lense;
+  private final AppInfoWithSubtyping appInfo;
+  private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap = new IdentityHashMap<>();
+
+  public BridgeMethodAnalysis(GraphLense lense, AppInfoWithSubtyping appInfo) {
+    this.lense = lense;
+    this.appInfo = appInfo;
+  }
+
+  public GraphLense run() {
+    for (DexClass clazz : appInfo.classes()) {
+      identifyBridgeMethods(clazz.virtualMethods());
+      identifyBridgeMethods(clazz.directMethods());
+    }
+    return new BridgeLense(lense, bridgeTargetToBridgeMap);
+  }
+
+  private void identifyBridgeMethods(DexEncodedMethod[] dexEncodedMethods) {
+    for (DexEncodedMethod method : dexEncodedMethods) {
+      if (method.accessFlags.isBridge()) {
+        InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+        method.getCode().registerReachableDefinitions(targetExtractor);
+        DexMethod target = targetExtractor.getTarget();
+        InvokeKind kind = targetExtractor.getKind();
+        if (target != null &&
+            target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
+          assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
+          target = lense.lookupMethod(target, method);
+          if (kind == InvokeKind.STATIC) {
+            assert method.accessFlags.isStatic();
+            DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(target);
+            if (targetMethod != null) {
+              addForwarding(method, targetMethod);
+            }
+          } else if (kind == InvokeKind.VIRTUAL) {
+            // TODO(herhut): Add support for bridges with multiple targets.
+            DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(target);
+            if (targetMethod != null) {
+              addForwarding(method, targetMethod);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private void addForwarding(DexEncodedMethod method, DexEncodedMethod target) {
+    // This is a single target bridge we can inline.
+    if (Log.ENABLED) {
+      Log.info(getClass(), "Adding bridge forwarding %s -> %s.", method.method,
+          target.method);
+    }
+    bridgeTargetToBridgeMap.put(target.method, method.method);
+    // Force the target to be inlined into the bridge.
+    target.markForceInline();
+  }
+
+
+
+  private static class BridgeLense extends GraphLense {
+
+    private final GraphLense previousLense;
+    private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap;
+
+    private BridgeLense(GraphLense previousLense,
+        Map<DexMethod, DexMethod> bridgeTargetToBridgeMap) {
+      this.previousLense = previousLense;
+      this.bridgeTargetToBridgeMap = bridgeTargetToBridgeMap;
+    }
+
+    @Override
+    public DexType lookupType(DexType type, DexEncodedMethod context) {
+      return previousLense.lookupType(type, context);
+    }
+
+    @Override
+    public DexMethod lookupMethod(DexMethod method, DexEncodedMethod context) {
+      DexMethod previous = previousLense.lookupMethod(method, context);
+      DexMethod target = bridgeTargetToBridgeMap.get(previous);
+      // Do not forward calls from a bridge method to itself while the bridge method is still
+      // a bridge.
+      if (target == null ||
+          context.accessFlags.isBridge() && target == context.method) {
+        return previous;
+      } else {
+        return target;
+      }
+    }
+
+    @Override
+    public DexField lookupField(DexField field, DexEncodedMethod context) {
+      return previousLense.lookupField(field, context);
+    }
+
+    @Override
+    public boolean isContextFree() {
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/DebugStripper.java b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
new file mode 100644
index 0000000..d045f76
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/DebugStripper.java
@@ -0,0 +1,192 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEventBuilder;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.Range;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.utils.HashMapInt;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.List;
+
+public class DebugStripper {
+
+  private static final int USED_MORE_THAN_ONCE = 0;
+  private static final int USED_ONCE = -1;
+
+  private final ClassNameMapper classNameMapper;
+  private final InternalOptions options;
+
+  public DebugStripper(ClassNameMapper classNameMapper, InternalOptions options) {
+    this.classNameMapper = classNameMapper;
+    this.options = options;
+  }
+
+  private String descriptorToName(String descriptor) {
+    // The format is L<name>; and '/' is used as package separator.
+    return descriptor.substring(1, descriptor.length() - 1).replace('/', '.');
+  }
+
+  private Range findRange(int value, List<Range> ranges, Range defaultRange) {
+    for (Range range : ranges) {
+      if (range.contains(value)) {
+        return range;
+      }
+    }
+    return defaultRange;
+  }
+
+  private static class NumberedDebugInfo {
+    final int numberOfEntries;
+    final DexDebugInfo info;
+
+    public NumberedDebugInfo(int numberOfEntries, DexDebugInfo info) {
+      this.numberOfEntries = numberOfEntries;
+      this.info = info;
+    }
+  }
+
+  private NumberedDebugInfo processDebugInfo(DexMethod method, DexDebugInfo info,
+      MemberNaming naming, int startLine) {
+    if (info == null || naming == null) {
+      return new NumberedDebugInfo(0, null);
+    }
+    List<Range> ranges = naming.getInlineRanges();
+    // Maintain line and address but only when entering or leaving a range of line numbers
+    // that pertains to a different method body.
+    Range currentRange = naming.topLevelRange;
+    DexDebugEventBuilder builder = new DexDebugEventBuilder(method);
+    // Always start with a no-op bytecode to make sure that the start-line is manifested by
+    // the Dalvik VM and the event based processing in R8. This also avoids empty bytecode
+    // sequences.
+    int entryCount = 1;
+    DexString file = null;
+    ImmutableMap<Integer, DebugLocalInfo> locals = null;
+    builder.setPosition(0, startLine, file, locals);
+    for (DexDebugEntry entry : info.computeEntries()) {
+      boolean addEntry = false;
+      // We are in a range, check whether we have left it.
+      if (currentRange != null && !currentRange.contains(entry.line)) {
+        currentRange = null;
+        addEntry = true;
+      }
+      // We have no range (because we left the old one or never were in a range).
+      if (currentRange == null) {
+        currentRange = findRange(entry.line, ranges, naming.topLevelRange);
+        // We entered a new Range, emit this entry.
+        if (currentRange != null) {
+          addEntry = true;
+        }
+      }
+      if (addEntry) {
+        int line = options.skipDebugLineNumberOpt
+            ? entry.line
+            : startLine + ranges.indexOf(currentRange) + 1;
+        builder.setPosition(entry.address, line, file, locals);
+        ++entryCount;
+      }
+    }
+    return new NumberedDebugInfo(entryCount, builder.build());
+  }
+
+  private void processCode(DexEncodedMethod encodedMethod, MemberNaming naming,
+      HashMapInt<DexString> nameCounts) {
+    if (encodedMethod.getCode() == null) {
+      return;
+    }
+    DexCode code = encodedMethod.getCode().asDexCode();
+    DexString name = encodedMethod.method.name;
+    DexDebugInfo originalInfo = code.getDebugInfo();
+    if (originalInfo == null) {
+      return;
+    }
+    int startLine;
+    boolean isUsedOnce = false;
+    if (options.skipDebugLineNumberOpt) {
+      startLine = originalInfo.startLine;
+    } else {
+      int nameCount = nameCounts.get(name);
+      if (nameCount == USED_ONCE) {
+        isUsedOnce = true;
+        startLine = 0;
+      } else {
+        startLine = nameCount;
+      }
+    }
+
+    NumberedDebugInfo numberedInfo = processDebugInfo(
+        encodedMethod.method, originalInfo, naming, startLine);
+    DexDebugInfo newInfo = numberedInfo.info;
+    if (!options.skipDebugLineNumberOpt) {
+      // Fix up the line information.
+      int previousCount = nameCounts.get(name);
+      nameCounts.put(name, previousCount + numberedInfo.numberOfEntries);
+      // If we don't actually need line information and there are no debug entries, throw it away.
+      if (newInfo != null && isUsedOnce && newInfo.events.length == 0) {
+        newInfo = null;
+      } else if (naming != null && newInfo != null) {
+        naming.setCollapsedStartLineNumber(startLine);
+        // Preserve the line number information we had.
+        naming.setOriginalStartLineNumber(originalInfo.startLine);
+      }
+    }
+    code.setDebugInfo(newInfo);
+  }
+
+  private void processMethod(DexEncodedMethod method, ClassNaming classNaming,
+      HashMapInt<DexString> nameCounts) {
+    MemberNaming naming = null;
+    if (classNaming != null) {
+      Signature renamedSignature = classNameMapper.getRenamedMethodSignature(method.method);
+      naming = classNaming.lookup(renamedSignature);
+    }
+    processCode(method, naming, nameCounts);
+  }
+
+  private void processMethods(DexEncodedMethod[] methods, ClassNaming naming,
+      HashMapInt<DexString> nameCounts) {
+    if (methods == null) {
+      return;
+    }
+    for (DexEncodedMethod method : methods) {
+      processMethod(method, naming, nameCounts);
+    }
+  }
+
+  public void processClass(DexProgramClass clazz) {
+    if (!clazz.hasMethodsOrFields()) {
+      return;
+    }
+    String name = descriptorToName(clazz.type.toDescriptorString());
+    ClassNaming naming = classNameMapper == null ? null : classNameMapper.getClassNaming(name);
+    HashMapInt<DexString> nameCounts = new HashMapInt<>();
+    setIntialNameCounts(nameCounts, clazz.directMethods());
+    setIntialNameCounts(nameCounts, clazz.virtualMethods());
+    processMethods(clazz.directMethods(), naming, nameCounts);
+    processMethods(clazz.virtualMethods(), naming, nameCounts);
+  }
+
+  private void setIntialNameCounts(HashMapInt<DexString> nameCounts,
+      DexEncodedMethod[] methods) {
+    for (DexEncodedMethod method : methods) {
+      if (nameCounts.containsKey(method.method.name)) {
+        nameCounts.put(method.method.name, USED_MORE_THAN_ONCE);
+      } else {
+        nameCounts.put(method.method.name, USED_ONCE);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
new file mode 100644
index 0000000..61b270d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
+
+class InvokeSingleTargetExtractor extends UseRegistry {
+  private InvokeKind kind = InvokeKind.NONE;
+  private DexMethod target;
+
+  InvokeSingleTargetExtractor() {
+  }
+
+  private boolean setTarget(DexMethod target, InvokeKind kind) {
+    if (this.kind != InvokeKind.NONE) {
+      this.kind = InvokeKind.ILLEGAL;
+      this.target = null;
+    } else {
+      assert this.target == null;
+      this.target = target;
+      this.kind = kind;
+    }
+    return false;
+  }
+
+  private boolean invalid() {
+    kind = InvokeKind.ILLEGAL;
+    return false;
+  }
+
+  public DexMethod getTarget() {
+    return target;
+  }
+
+  public InvokeKind getKind() {
+    return kind;
+  }
+
+  @Override
+  public boolean registerInvokeVirtual(DexMethod method) {
+    return setTarget(method, InvokeKind.VIRTUAL);
+  }
+
+  @Override
+  public boolean registerInvokeDirect(DexMethod method) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerInvokeStatic(DexMethod method) {
+    return setTarget(method, InvokeKind.STATIC);
+  }
+
+  @Override
+  public boolean registerInvokeInterface(DexMethod method) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerInvokeSuper(DexMethod method) {
+    return setTarget(method, InvokeKind.SUPER);
+  }
+
+  @Override
+  public boolean registerInstanceFieldWrite(DexField field) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerInstanceFieldRead(DexField field) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerNewInstance(DexType type) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerStaticFieldRead(DexField field) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerStaticFieldWrite(DexField field) {
+    return invalid();
+  }
+
+  @Override
+  public boolean registerTypeReference(DexType type) {
+    return invalid();
+  }
+
+  public enum InvokeKind {
+    VIRTUAL,
+    STATIC,
+    SUPER,
+    ILLEGAL,
+    NONE
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
new file mode 100644
index 0000000..1e4c51b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+public class MemberRebindingAnalysis {
+  private final AppInfoWithLiveness appInfo;
+  private final GraphLense lense;
+  private final GraphLense.Builder builder = new GraphLense.Builder();
+
+  public MemberRebindingAnalysis(AppInfoWithLiveness appInfo, GraphLense lense) {
+    assert lense.isContextFree();
+    this.appInfo = appInfo;
+    this.lense = lense;
+  }
+
+  private DexMethod validTargetFor(DexMethod target, DexMethod original,
+      BiFunction<DexClass, DexMethod, DexEncodedMethod> lookup) {
+    DexClass clazz = appInfo.definitionFor(target.getHolder());
+    assert clazz != null;
+    if (!clazz.isLibraryClass()) {
+      return target;
+    }
+    DexType newHolder;
+    if (clazz.isInterface()) {
+      newHolder = firstLibraryClassForInterfaceTarget(target, original.getHolder(), lookup);
+    } else {
+      newHolder = firstLibraryClass(target.getHolder(), original.getHolder());
+    }
+    return appInfo.dexItemFactory.createMethod(newHolder, original.proto, original.name);
+  }
+
+  private DexField validTargetFor(DexField target, DexField original,
+      BiFunction<DexClass, DexField, DexEncodedField> lookup) {
+    DexClass clazz = appInfo.definitionFor(target.getHolder());
+    assert clazz != null;
+    if (!clazz.isLibraryClass()) {
+      return target;
+    }
+    DexType newHolder;
+    if (clazz.isInterface()) {
+      newHolder = firstLibraryClassForInterfaceTarget(target, original.getHolder(), lookup);
+    } else {
+      newHolder = firstLibraryClass(target.getHolder(), original.getHolder());
+    }
+    return appInfo.dexItemFactory.createField(newHolder, original.type, original.name);
+  }
+
+  private <T> DexType firstLibraryClassForInterfaceTarget(T target, DexType current,
+      BiFunction<DexClass, T, ?> lookup) {
+    DexClass clazz = appInfo.definitionFor(current);
+    Object potential = lookup.apply(clazz, target);
+    if (potential != null) {
+      // Found, return type.
+      return current;
+    }
+    if (clazz.superType != null) {
+      DexType matchingSuper = firstLibraryClassForInterfaceTarget(target, clazz.superType, lookup);
+      if (matchingSuper != null) {
+        // Found in supertype, return first libray class.
+        return clazz.isLibraryClass() ? current : matchingSuper;
+      }
+    }
+    for (DexType iface : clazz.interfaces.values) {
+      DexType matchingIface = firstLibraryClassForInterfaceTarget(target, iface, lookup);
+      if (matchingIface != null) {
+        // Found in interface, return first library class.
+        return clazz.isLibraryClass() ? current : matchingIface;
+      }
+    }
+    return null;
+  }
+
+  private DexType firstLibraryClass(DexType top, DexType bottom) {
+    assert appInfo.definitionFor(top).isLibraryClass();
+    DexClass searchClass = appInfo.definitionFor(bottom);
+    while (!searchClass.isLibraryClass()) {
+      searchClass = appInfo.definitionFor(searchClass.superType);
+    }
+    return searchClass.type;
+  }
+
+  private boolean isNotFromLibrary(Descriptor item) {
+    DexClass clazz = appInfo.definitionFor(item.getHolder());
+    return clazz != null && !clazz.isLibraryClass();
+  }
+
+  private DexEncodedMethod virtualLookup(DexMethod method) {
+    return appInfo.lookupVirtualDefinition(method.getHolder(), method);
+  }
+
+  private DexEncodedMethod superLookup(DexMethod method) {
+    return appInfo.lookupVirtualTarget(method.getHolder(), method);
+  }
+
+  private void computeMethodRebinding(Set<DexMethod> methods,
+      Function<DexMethod, DexEncodedMethod> lookupTarget,
+      BiFunction<DexClass, DexMethod, DexEncodedMethod> lookupTargetOnClass) {
+    for (DexMethod method : methods) {
+      method = lense.lookupMethod(method, null);
+      // We can safely ignore array types, as the corresponding methods are defined in a library.
+      if (!method.getHolder().isClassType()) {
+        continue;
+      }
+      DexEncodedMethod target = lookupTarget.apply(method);
+      // Rebind to the lowest library class or program class.
+      if (target != null && target.method != method) {
+        builder.map(method, validTargetFor(target.method, method, lookupTargetOnClass));
+      }
+    }
+  }
+
+  private void computeFieldRebinding(Set<DexField> fields,
+      BiFunction<DexType, DexField, DexEncodedField> lookup,
+      BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
+    for (DexField field : fields) {
+      field = lense.lookupField(field, null);
+      DexEncodedField target = lookup.apply(field.getHolder(), field);
+      // Rebind to the lowest library class or program class.
+      if (target != null && target.field != field) {
+        builder.map(field, validTargetFor(target.field, field, lookupTargetOnClass));
+      }
+    }
+  }
+
+  public GraphLense run() {
+    computeMethodRebinding(appInfo.virtualInvokes, this::virtualLookup,
+        DexClass::findVirtualTarget);
+    computeMethodRebinding(appInfo.superInvokes, this::superLookup, DexClass::findVirtualTarget);
+    computeMethodRebinding(appInfo.directInvokes, appInfo::lookupDirectTarget,
+        DexClass::findDirectTarget);
+    computeMethodRebinding(appInfo.staticInvokes, appInfo::lookupStaticTarget,
+        DexClass::findDirectTarget);
+
+    computeFieldRebinding(Sets.union(appInfo.staticFieldsRead, appInfo.staticFieldsWritten),
+        appInfo::lookupStaticTarget, DexClass::findStaticTarget);
+    computeFieldRebinding(Sets.union(appInfo.instanceFieldsRead, appInfo.instanceFieldsWritten),
+        appInfo::lookupInstanceTarget, DexClass::findInstanceTarget);
+    return builder.build(lense);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
new file mode 100644
index 0000000..e4892f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class VisibilityBridgeRemover {
+  private final AppInfoWithSubtyping appInfo;
+  private final DexApplication application;
+  private final Set<DexEncodedMethod> unneededVisibilityBridges = Sets.newIdentityHashSet();
+
+  public VisibilityBridgeRemover(AppInfoWithSubtyping appInfo, DexApplication application) {
+    this.appInfo = appInfo;
+    this.application = application;
+  }
+
+  private void identifyBridgeMethods(DexEncodedMethod[] dexEncodedMethods) {
+    for (DexEncodedMethod method : dexEncodedMethods) {
+      if (method.accessFlags.isBridge()) {
+        InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor();
+        method.getCode().registerReachableDefinitions(targetExtractor);
+        DexMethod target = targetExtractor.getTarget();
+        InvokeKind kind = targetExtractor.getKind();
+        if (target != null &&
+            target.proto.parameters.values.length == method.method.proto.parameters.values.length) {
+          assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
+          if (kind == InvokeKind.SUPER) {
+            // This is a visibility forward, so check for the direct target.
+            DexEncodedMethod targetMethod
+                = appInfo.lookupVirtualDefinition(target.getHolder(), target);
+            if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
+              if (Log.ENABLED) {
+                Log.info(getClass(), "Removing visibility forwarding %s -> %s", method.method,
+                    targetMethod.method);
+              }
+              unneededVisibilityBridges.add(method);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private void removeUnneededVisibilityBridges() {
+    Set<DexType> classes = unneededVisibilityBridges.stream()
+        .map(method -> method.method.getHolder())
+        .collect(Collectors.toSet());
+    for (DexType type : classes) {
+      DexClass clazz = appInfo.definitionFor(type);
+      clazz.virtualMethods = removeMethods(clazz.virtualMethods, unneededVisibilityBridges);
+    }
+  }
+
+  private DexEncodedMethod[] removeMethods(DexEncodedMethod[] methods,
+      Set<DexEncodedMethod> removals) {
+    assert methods != null;
+    List<DexEncodedMethod> newMethods = Arrays.stream(methods)
+        .filter(method -> !removals.contains(method))
+        .collect(Collectors.toList());
+    assert newMethods.size() < methods.length;
+    return newMethods.toArray(new DexEncodedMethod[newMethods.size()]);
+  }
+
+  public DexApplication run() {
+    for (DexClass clazz : appInfo.classes()) {
+      identifyBridgeMethods(clazz.virtualMethods());
+      identifyBridgeMethods(clazz.directMethods());
+    }
+    if (!unneededVisibilityBridges.isEmpty()) {
+      removeUnneededVisibilityBridges();
+    }
+    return application;
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
new file mode 100644
index 0000000..2850790
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class AbstractMethodRemover {
+
+  private final AppInfoWithSubtyping appInfo;
+  private ScopedDexItemSet scope;
+
+  public AbstractMethodRemover(AppInfoWithSubtyping appInfo) {
+    this.appInfo = appInfo;
+  }
+
+  public void run() {
+    assert scope == null;
+    processClass(appInfo.dexItemFactory.objectType);
+  }
+
+  private void processClass(DexType type) {
+    DexClass holder = appInfo.definitionFor(type);
+    scope = new ScopedDexItemSet(scope);
+    if (holder != null) {
+      holder.virtualMethods = processMethods(holder.virtualMethods);
+    }
+    type.forAllExtendsSubtypes(this::processClass);
+    scope = scope.getParent();
+  }
+
+  private DexEncodedMethod[] processMethods(DexEncodedMethod[] virtualMethods) {
+    if (virtualMethods == null) {
+      return null;
+    }
+    // Removal of abstract methods is rare, so avoid copying the array until we find one.
+    List<DexEncodedMethod> methods = null;
+    for (int i = 0; i < virtualMethods.length; i++) {
+      DexEncodedMethod method = virtualMethods[i];
+      if (scope.addMethod(method.method) || !method.accessFlags.isAbstract()) {
+        if (methods != null) {
+          methods.add(method);
+        }
+      } else {
+        if (methods == null) {
+          methods = new ArrayList<>(virtualMethods.length - 1);
+          for (int j = 0; j < i; j++) {
+            methods.add(virtualMethods[j]);
+          }
+        }
+        if (Log.ENABLED) {
+          Log.debug(getClass(), "Removing abstract method %s.", method.method);
+        }
+      }
+    }
+    return methods == null ? virtualMethods : methods.toArray(new DexEncodedMethod[methods.size()]);
+  }
+
+  private static class ScopedDexItemSet {
+    private static Equivalence<DexMethod> METHOD_EQUIVALENCE = MethodSignatureEquivalence.get();
+
+    private final ScopedDexItemSet parent;
+    private final Set<Wrapper<DexMethod>> items = new HashSet<>();
+
+    private ScopedDexItemSet() {
+      this(null);
+    }
+
+    private ScopedDexItemSet(ScopedDexItemSet parent) {
+      this.parent = parent;
+    }
+
+    private boolean contains(Wrapper<DexMethod> item) {
+      return items.contains(item)
+          || ((parent != null) && parent.contains(item));
+    }
+
+    boolean addMethod(DexMethod method) {
+      Wrapper<DexMethod> wrapped = METHOD_EQUIVALENCE.wrap(method);
+      return !contains(wrapped) && items.add(wrapped);
+    }
+
+    ScopedDexItemSet getParent() {
+      return parent;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
new file mode 100644
index 0000000..e60f241
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -0,0 +1,175 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.AttributeRemovalOptions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+public class AnnotationRemover {
+
+  private final AppInfoWithLiveness appInfo;
+  private final boolean minificationEnabled;
+  private final AttributeRemovalOptions keep;
+
+  public AnnotationRemover(AppInfoWithLiveness appInfo, InternalOptions options) {
+    this(appInfo, !options.skipMinification, options.attributeRemoval);
+  }
+
+  public AnnotationRemover(AppInfoWithLiveness appInfo, boolean minificationEnabled,
+      AttributeRemovalOptions keep) {
+    this.appInfo = appInfo;
+    this.minificationEnabled = minificationEnabled;
+    this.keep = keep;
+  }
+
+  /**
+   * Used to filter annotations on classes, methods and fields.
+   */
+  private boolean filterAnnotations(DexAnnotation annotation) {
+    switch (annotation.visibility) {
+      case DexAnnotation.VISIBILITY_SYSTEM:
+        // EnclosingClass and InnerClass are used in Dalvik to represent the InnerClasses
+        // attribute section from class files.
+        if (keep.innerClasses &&
+            (DexAnnotation.isInnerClassesAnnotation(annotation) ||
+                DexAnnotation.isEnclosingClassAnnotation(annotation))) {
+          return true;
+        }
+        if (keep.enclosingMethod && DexAnnotation.isEnclosingMethodAnnotation(annotation)) {
+          return true;
+        }
+        if (keep.exceptions && DexAnnotation.isThrowingAnnotation(annotation)) {
+          return true;
+        }
+        if (keep.signature && DexAnnotation.isSignatureAnnotation(annotation)) {
+          return true;
+        }
+        if (keep.sourceDebugExtension && DexAnnotation.isSourceDebugExtension(annotation)) {
+          return true;
+        }
+        return false;
+      case DexAnnotation.VISIBILITY_RUNTIME:
+        if (!keep.runtimeVisibleAnnotations) {
+          return false;
+        }
+        break;
+      case DexAnnotation.VISIBILITY_BUILD:
+        if (!keep.runtimeInvisibleAnnotations) {
+          return false;
+        }
+        break;
+      default:
+        throw new Unreachable("Unexpected annotation visiility.");
+    }
+    return appInfo.liveTypes.contains(annotation.annotation.type);
+  }
+
+  /**
+   * Used to filter annotations on parameters.
+   */
+  private boolean filterParameterAnnotations(DexAnnotation annotation) {
+    switch (annotation.visibility) {
+      case DexAnnotation.VISIBILITY_SYSTEM:
+        return false;
+      case DexAnnotation.VISIBILITY_RUNTIME:
+        if (!keep.runtimeVisibleParameterAnnotations) {
+          return false;
+        }
+        break;
+      case DexAnnotation.VISIBILITY_BUILD:
+        if (!keep.runtimeInvisibleParamterAnnotations) {
+          return false;
+        }
+        break;
+      default:
+        throw new Unreachable("Unexpected annotation visibility.");
+    }
+    return appInfo.liveTypes.contains(annotation.annotation.type);
+  }
+
+  public void run() {
+    keep.ensureValid(minificationEnabled);
+    for (DexProgramClass clazz : appInfo.classes()) {
+      clazz.annotations = stripAnnotations(clazz.annotations, this::filterAnnotations);
+      processMethods(clazz.virtualMethods());
+      processMethods(clazz.directMethods());
+      processFields(clazz.staticFields());
+      processFields(clazz.instanceFields());
+    }
+  }
+
+  private void processMethods(DexEncodedMethod[] methods) {
+    for (DexEncodedMethod method : methods) {
+      method.annotations = stripAnnotations(method.annotations, this::filterAnnotations);
+      method.parameterAnnotations = stripAnnotations(method.parameterAnnotations,
+          this::filterParameterAnnotations);
+    }
+  }
+
+  private void processFields(DexEncodedField[] fields) {
+    for (DexEncodedField field : fields) {
+      field.annotations = stripAnnotations(field.annotations, this::filterAnnotations);
+    }
+  }
+
+  private DexAnnotationSetRefList stripAnnotations(DexAnnotationSetRefList annotations,
+      Predicate<DexAnnotation> filter) {
+    DexAnnotationSet[] filtered = null;
+    for (int i = 0; i < annotations.values.length; i++) {
+      DexAnnotationSet updated = stripAnnotations(annotations.values[i], filter);
+      if (updated != annotations.values[i]) {
+        if (filtered == null) {
+          filtered = annotations.values.clone();
+          filtered[i] = updated;
+        }
+      }
+    }
+    if (filtered == null) {
+      return annotations;
+    } else {
+      if (Arrays.stream(filtered).allMatch(DexAnnotationSet::isEmpty)) {
+        return DexAnnotationSetRefList.empty();
+      }
+      return new DexAnnotationSetRefList(filtered);
+    }
+  }
+
+  private DexAnnotationSet stripAnnotations(DexAnnotationSet annotations,
+      Predicate<DexAnnotation> filter) {
+    ArrayList<DexAnnotation> filtered = null;
+    for (int i = 0; i < annotations.annotations.length; i++) {
+      DexAnnotation annotation = annotations.annotations[i];
+      if (filter.test(annotation)) {
+        if (filtered != null) {
+          filtered.add(annotation);
+        }
+      } else {
+        if (filtered == null) {
+          filtered = new ArrayList<>(annotations.annotations.length);
+          for (int j = 0; j < i; j++) {
+            filtered.add(annotations.annotations[j]);
+          }
+        }
+      }
+    }
+    if (filtered == null) {
+      return annotations;
+    } else if (filtered.isEmpty()) {
+      return DexAnnotationSet.empty();
+    } else {
+      return new DexAnnotationSet(filtered.toArray(new DexAnnotation[filtered.size()]));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
new file mode 100644
index 0000000..75b7495
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import java.util.Arrays;
+import java.util.Set;
+
+public class DiscardedChecker {
+
+  private final Set<DexItem> checkDiscarded;
+  private final DexApplication application;
+  private boolean fail = false;
+
+  public DiscardedChecker(RootSet rootSet, DexApplication application) {
+    this.checkDiscarded = rootSet.checkDiscarded;
+    this.application = application;
+  }
+
+  public void run() {
+    for (DexProgramClass clazz : application.classes()) {
+      if (checkDiscarded.contains(clazz)) {
+        report(clazz);
+      }
+      processSubItems(clazz.directMethods());
+      processSubItems(clazz.virtualMethods());
+      processSubItems(clazz.staticFields());
+      processSubItems(clazz.instanceFields());
+    }
+    if (fail) {
+      throw new CompilationError("Discard checks failed.");
+    }
+  }
+
+  private void processSubItems(DexItem[] items) {
+    Arrays.stream(items).filter(checkDiscarded::contains).forEach(this::report);
+  }
+
+  private void report(DexItem item) {
+    System.err.println("Item " + item.toSourceString() + " was not discarded.");
+    fail = true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
new file mode 100644
index 0000000..07e641a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -0,0 +1,1253 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLense;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+/**
+ * Approximates the runtime dependencies for the given set of roots.
+ *
+ * <p>The implementation filters the static call-graph with liveness information on classes to
+ * remove virtual methods that are reachable by their static type but are unreachable at runtime as
+ * they are not visible from any instance.
+ *
+ * <p>As result of the analysis, an instance of {@link AppInfoWithLiveness} is returned. See the
+ * field descriptions for details.
+ */
+public class Enqueuer {
+
+  private final AppInfoWithSubtyping appInfo;
+  private final RootSet rootSet;
+
+  private Map<DexType, Set<DexMethod>> virtualInvokes = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexMethod>> superInvokes = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexMethod>> directInvokes = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexMethod>> staticInvokes = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexField>> instanceFieldsWritten = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexField>> instanceFieldsRead = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexField>> staticFieldsRead = Maps.newIdentityHashMap();
+  private Map<DexType, Set<DexField>> staticFieldsWritten = Maps.newIdentityHashMap();
+
+  /**
+   * This map keeps a view of all virtual methods that are reachable from virtual invokes. A method
+   * is reachable even if no live subtypes exist, so this is not sufficient for inclusion in the
+   * live set.
+   */
+  private Map<DexType, SetWithReason<DexEncodedMethod>> reachableVirtualMethods = Maps
+      .newIdentityHashMap();
+  /**
+   * Tracks the dependency between a method and the super-method it calls, if any. Used to make
+   * super methods become live when they become reachable from a live sub-method.
+   */
+  private Map<DexEncodedMethod, Set<DexEncodedMethod>> superInvokeDependencies = Maps
+      .newIdentityHashMap();
+  /**
+   * Set of instance fields that can be reached by read/write operations.
+   */
+  private Map<DexType, SetWithReason<DexEncodedField>> reachableInstanceFields = Maps
+      .newIdentityHashMap();
+
+  /**
+   * Set of types that are mentioned in the program. We at least need an empty abstract classitem
+   * for these.
+   */
+  private Set<DexType> liveTypes = Sets.newIdentityHashSet();
+  /**
+   * Set of types that are actually instantiated. These cannot be abstract.
+   */
+  private SetWithReason<DexType> instantiatedTypes = new SetWithReason<>();
+  /**
+   * Set of methods that are the immediate target of an invoke. They might not actually be live but
+   * are required so that invokes can find the method. If a method is only a target but not live,
+   * its implementation may be removed and it may be marked abstract.
+   */
+  private SetWithReason<DexEncodedMethod> targetedMethods = new SetWithReason<>();
+  /**
+   * Set of methods that belong to live classes and can be reached by invokes. These need to be
+   * kept.
+   */
+  private SetWithReason<DexEncodedMethod> liveMethods = new SetWithReason<>();
+
+  /**
+   * Set of fields that belong to live classes and can be reached by invokes. These need to be
+   * kept.
+   */
+  private SetWithReason<DexEncodedField> liveFields = new SetWithReason<>();
+
+  /**
+   * A queue of items that need processing. Different items trigger different actions:
+   */
+  private Queue<Action> workList = Queues.newArrayDeque();
+
+  /**
+   * A cache for DexMethod that have been marked reachable.
+   */
+  private Set<DexMethod> virtualTargetsMarkedAsReachable = Sets.newIdentityHashSet();
+
+  /**
+   * A set of dexitems we have reported missing to dedupe warnings.
+   */
+  private Set<DexItem> reportedMissing = Sets.newIdentityHashSet();
+
+  /**
+   * A set of items that we are keeping due to keep rules. This may differ from the rootSet
+   * due to dependent keep rules.
+   */
+  private Set<DexItem> pinnedItems = Sets.newIdentityHashSet();
+
+  /**
+   * A map from classes to annotations that need to be processed should the classes ever
+   * become live.
+   */
+  private final Map<DexType, Set<DexAnnotation>> deferredAnnotations = new IdentityHashMap<>();
+
+  public Enqueuer(RootSet rootSet, AppInfoWithSubtyping appInfo) {
+    this.appInfo = appInfo;
+    this.rootSet = rootSet;
+    // Translate the result of root-set computation into enqueuer actions.
+    enqueueRootItems(rootSet.noShrinking);
+  }
+
+  private void enqueueRootItems(Map<DexItem, ProguardKeepRule> items) {
+    workList.addAll(
+        items.entrySet().stream().map(Action::forRootItem).collect(Collectors.toList()));
+    pinnedItems.addAll(items.keySet());
+  }
+
+  //
+  // Things to do with registering events. This is essentially the interface for byte-code
+  // traversals.
+  //
+
+  private <S extends DexItem, T extends Descriptor<S, T>> boolean registerItemWithTarget(
+      Map<DexType, Set<T>> seen, T item) {
+    DexType holder = item.getHolder();
+    if (holder.isArrayType()) {
+      holder = holder.toBaseType(appInfo.dexItemFactory);
+    }
+    if (!holder.isClassType()) {
+      return false;
+    }
+    markTypeAsLive(holder);
+    return seen.computeIfAbsent(item.getHolder(), (ignore) -> Sets.newIdentityHashSet()).add(item);
+  }
+
+  private class UseRegistry extends com.android.tools.r8.graph.UseRegistry {
+
+    private final DexEncodedMethod currentMethod;
+
+    private UseRegistry(DexEncodedMethod currentMethod) {
+      this.currentMethod = currentMethod;
+    }
+
+    @Override
+    public boolean registerInvokeVirtual(DexMethod method) {
+      if (!registerItemWithTarget(virtualInvokes, method)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register invokeVirtual `%s`.", method);
+      }
+      workList.add(Action.markReachableVirtual(method, KeepReason.invokedFrom(currentMethod)));
+      return true;
+    }
+
+    @Override
+    public boolean registerInvokeDirect(DexMethod method) {
+      if (!registerItemWithTarget(directInvokes, method)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register invokeDirect `%s`.", method);
+      }
+      handleInvokeOfDirectTarget(method, KeepReason.invokedFrom(currentMethod));
+      return true;
+    }
+
+    @Override
+    public boolean registerInvokeStatic(DexMethod method) {
+      if (!registerItemWithTarget(staticInvokes, method)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register invokeStatic `%s`.", method);
+      }
+      handleInvokeOfStaticTarget(method, KeepReason.invokedFrom(currentMethod));
+      return true;
+    }
+
+    @Override
+    public boolean registerInvokeInterface(DexMethod method) {
+      if (!registerItemWithTarget(virtualInvokes, method)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register invokeInterface `%s`.", method);
+      }
+      workList.add(Action.markReachableInterface(method, KeepReason.invokedFrom(currentMethod)));
+      return true;
+    }
+
+    @Override
+    public boolean registerInvokeSuper(DexMethod method) {
+      if (!registerItemWithTarget(superInvokes, method)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register invokeSuper `%s`.", method);
+      }
+      workList.add(Action.markReachableSuper(method, currentMethod));
+      return true;
+    }
+
+    @Override
+    public boolean registerInstanceFieldWrite(DexField field) {
+      if (!registerItemWithTarget(instanceFieldsWritten, field)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register Iput `%s`.", field);
+      }
+      // TODO(herhut): We have to add this, but DCR should eliminate dead writes.
+      workList.add(Action.markReachableField(field, KeepReason.fieldReferencedIn(currentMethod)));
+      return true;
+    }
+
+    @Override
+    public boolean registerInstanceFieldRead(DexField field) {
+      if (!registerItemWithTarget(instanceFieldsRead, field)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register Iget `%s`.", field);
+      }
+      workList.add(Action.markReachableField(field, KeepReason.fieldReferencedIn(currentMethod)));
+      return true;
+    }
+
+    @Override
+    public boolean registerNewInstance(DexType type) {
+      if (instantiatedTypes.contains(type)) {
+        return false;
+      }
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz == null) {
+        reportMissingClass(type);
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register new instatiation of `%s`.", clazz);
+      }
+      workList.add(Action.markInstantiated(clazz, KeepReason.instantiatedIn(currentMethod)));
+      return true;
+    }
+
+    @Override
+    public boolean registerStaticFieldRead(DexField field) {
+      if (!registerItemWithTarget(staticFieldsRead, field)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register Sget `%s`.", field);
+      }
+      markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
+      return true;
+    }
+
+    @Override
+    public boolean registerStaticFieldWrite(DexField field) {
+      if (!registerItemWithTarget(staticFieldsWritten, field)) {
+        return false;
+      }
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Register Sput `%s`.", field);
+      }
+      // TODO(herhut): We have to add this, but DCR should eliminate dead writes.
+      markStaticFieldAsLive(field, KeepReason.fieldReferencedIn(currentMethod));
+      return true;
+    }
+
+    @Override
+    public boolean registerTypeReference(DexType type) {
+      DexType baseType = type.toBaseType(appInfo.dexItemFactory);
+      if (baseType.isClassType()) {
+        markTypeAsLive(baseType);
+        return true;
+      }
+      return false;
+    }
+  }
+
+  //
+  // Actual actions performed.
+  //
+
+  private void markTypeAsLive(DexType type) {
+    assert type.isClassType();
+    if (liveTypes.add(type)) {
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Type `%s` has become live.", type);
+      }
+      DexClass holder = appInfo.definitionFor(type);
+      if (holder == null) {
+        reportMissingClass(type);
+        return;
+      }
+      for (DexType iface : holder.interfaces.values) {
+        markTypeAsLive(iface);
+      }
+      if (holder.superType != null) {
+        markTypeAsLive(holder.superType);
+      }
+      if (!holder.annotations.isEmpty()) {
+        processAnnotations(holder.annotations.annotations);
+      }
+      // We also need to add the corresponding <clinit> to the set of live methods, as otherwise
+      // static field initialization (and other class-load-time sideeffects) will not happen.
+      DexEncodedMethod clinit = holder.getClassInitializer(appInfo.dexItemFactory);
+      if (clinit != null) {
+        markDirectStaticOrConstructorMethodAsLive(clinit, KeepReason.reachableFromLiveType(type));
+      }
+      // If this type has deferred annotations, we have to process those now, too.
+      Set<DexAnnotation> annotations = deferredAnnotations.remove(type);
+      if (annotations != null) {
+        annotations.forEach(this::handleAnnotationOfLiveType);
+      }
+    }
+  }
+
+  private void handleAnnotationOfLiveType(DexAnnotation annotation) {
+    AnnotationReferenceMarker referenceMarker = new AnnotationReferenceMarker(
+        annotation.annotation.type, appInfo.dexItemFactory);
+    annotation.annotation.collectIndexedItems(referenceMarker);
+  }
+
+  private void processAnnotations(DexAnnotation[] annotations) {
+    for (DexAnnotation annotation : annotations) {
+      DexType type = annotation.annotation.type;
+      if (liveTypes.contains(type)) {
+        // The type of this annotation is already live, so pick up its dependencies.
+        handleAnnotationOfLiveType(annotation);
+      } else {
+        // Remember this annotation for later.
+        deferredAnnotations.computeIfAbsent(type, ignore -> new HashSet<>()).add(annotation);
+      }
+    }
+  }
+
+  private void handleInvokeOfStaticTarget(DexMethod method, KeepReason reason) {
+    DexEncodedMethod encodedMethod = appInfo.lookupStaticTarget(method);
+    if (encodedMethod == null) {
+      reportMissingMethod(method);
+      return;
+    }
+    markDirectStaticOrConstructorMethodAsLive(encodedMethod, reason);
+  }
+
+  private void handleInvokeOfDirectTarget(DexMethod method, KeepReason reason) {
+    DexEncodedMethod encodedMethod = appInfo.lookupDirectTarget(method);
+    if (encodedMethod == null) {
+      reportMissingMethod(method);
+      return;
+    }
+    markDirectStaticOrConstructorMethodAsLive(encodedMethod, reason);
+  }
+
+  private void reportMissingClass(DexType clazz) {
+    if (Log.ENABLED && reportedMissing.add(clazz)) {
+      Log.verbose(Enqueuer.class, "Class `%s` is missing.", clazz);
+    }
+  }
+
+  private void reportMissingMethod(DexMethod method) {
+    if (Log.ENABLED && reportedMissing.add(method)) {
+      Log.verbose(Enqueuer.class, "Method `%s` is missing.", method);
+    }
+  }
+
+  private void reportMissingField(DexField field) {
+    if (Log.ENABLED && reportedMissing.add(field)) {
+      Log.verbose(Enqueuer.class, "Field `%s` is missing.", field);
+    }
+  }
+
+  private void markMethodAsTargeted(DexEncodedMethod encodedMethod, KeepReason reason) {
+    markTypeAsLive(encodedMethod.method.holder);
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Method `%s` is targeted.", encodedMethod.method);
+    }
+    targetedMethods.add(encodedMethod, reason);
+  }
+
+  /**
+   * Adds the class to the set of instantiated classes and marks its fields and methods live
+   * depending on the currently seen invokes and field reads.
+   */
+  private void processNewlyInstantiatedClass(DexClass clazz, KeepReason reason) {
+    if (!instantiatedTypes.add(clazz.type, reason)) {
+      return;
+    }
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
+    }
+    // This class becomes live, so it and all its supertypes become live types.
+    markTypeAsLive(clazz.type);
+    // For all methods of the class, if we have seen a call, mark the method live.
+    // We only do this for virtual calls, as the other ones will be done directly.
+    transitionMethodsForInstantiatedClass(clazz.type);
+    // For all instance fields visible from the class, mark them live if we have seen a read.
+    transitionFieldsForInstantiatedClass(clazz.type);
+    // Add all dependent members to the workqueue.
+    enqueueRootItems(rootSet.getDependentItems(clazz.type));
+  }
+
+  /**
+   * Marks all methods live that can be reached by calls previously seen.
+   *
+   * <p>This should only be invoked if the given type newly becomes instantiated. In essence, this
+   * method replays all the invokes we have seen so far that could apply to this type and marks
+   * the corresponding methods live.
+   *
+   * <p>Only methods that are visible in this type are considered. That is, only those methods that
+   * are either defined directly on this type or that are defined on a supertype but are not
+   * shadowed by another inherited method.
+   */
+  private void transitionMethodsForInstantiatedClass(DexType type) {
+    Set<Wrapper<DexMethod>> seen = new HashSet<>();
+    MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
+    do {
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz == null) {
+        reportMissingClass(type);
+        // TODO(herhut): In essence, our subtyping chain is broken here. Handle that case better.
+        break;
+      }
+      SetWithReason<DexEncodedMethod> reachableMethods = reachableVirtualMethods.get(type);
+      if (reachableMethods != null) {
+        for (DexEncodedMethod encodedMethod : reachableMethods.getItems()) {
+          Wrapper<DexMethod> ignoringClass = equivalence.wrap(encodedMethod.method);
+          if (!seen.contains(ignoringClass)) {
+            seen.add(ignoringClass);
+            markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(type));
+          }
+        }
+      }
+      type = clazz.superType;
+    } while (type != null && !instantiatedTypes.contains(type));
+  }
+
+  /**
+   * Marks all fields live that can be reached by a read assuming that the given type or one of
+   * its subtypes is instantiated.
+   */
+  private void transitionFieldsForInstantiatedClass(DexType type) {
+    do {
+      DexClass clazz = appInfo.definitionFor(type);
+      if (clazz == null) {
+        // TODO(herhut) The subtype chain is broken. We need a way to deal with this better.
+        reportMissingClass(type);
+        break;
+      }
+      SetWithReason<DexEncodedField> reachableFields = reachableInstanceFields.get(type);
+      if (reachableFields != null) {
+        for (DexEncodedField field : reachableFields.getItems()) {
+          markInstanceFieldAsLive(field, KeepReason.reachableFromLiveType(type));
+        }
+      }
+      type = clazz.superType;
+    } while (type != null && !instantiatedTypes.contains(type));
+  }
+
+  private void markStaticFieldAsLive(DexField field, KeepReason reason) {
+    // Mark the type live here, so that the class exists at runtime. Note that this also marks all
+    // supertypes as live, so even if the field is actually on a supertype, its class will be live.
+    markTypeAsLive(field.clazz);
+    // Find the actual field.
+    DexEncodedField encodedField = appInfo.lookupStaticTarget(field.clazz, field);
+    if (encodedField == null) {
+      reportMissingField(field);
+      return;
+    }
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Adding static field `%s` to live set.", encodedField.field);
+    }
+    liveFields.add(encodedField, reason);
+  }
+
+  private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
+    assert field != null;
+    markTypeAsLive(field.field.clazz);
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
+    }
+    liveFields.add(field, reason);
+  }
+
+  private void markDirectStaticOrConstructorMethodAsLive(
+      DexEncodedMethod encodedMethod, KeepReason reason) {
+    assert encodedMethod != null;
+    if (!liveMethods.contains(encodedMethod)) {
+      markTypeAsLive(encodedMethod.method.holder);
+      markMethodAsTargeted(encodedMethod, reason);
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Method `%s` has become live due to direct invoke",
+            encodedMethod.method);
+      }
+      workList.add(Action.markMethodLive(encodedMethod, reason));
+    }
+  }
+
+  private void markVirtualMethodAsLive(DexEncodedMethod method, KeepReason reason) {
+    assert method != null;
+    if (!liveMethods.contains(method)) {
+      if (Log.ENABLED) {
+        Log.verbose(getClass(), "Adding virtual method `%s` to live set.", method.method);
+      }
+      workList.add(Action.markMethodLive(method, reason));
+    }
+  }
+
+  private boolean isInstantiatedOrHasInstantiatedSubtype(DexType type) {
+    return instantiatedTypes.contains(type)
+        || appInfo.subtypes(type).stream().anyMatch(instantiatedTypes::contains);
+  }
+
+  private void markFieldAsReachable(DexField field, KeepReason reason) {
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Marking instance field `%s` as reachable.", field);
+    }
+    DexEncodedField encodedField = appInfo.lookupInstanceTarget(field.clazz, field);
+    if (encodedField == null) {
+      reportMissingField(field);
+      return;
+    }
+    SetWithReason<DexEncodedField> reachable = reachableInstanceFields
+        .computeIfAbsent(encodedField.field.clazz, ignore -> new SetWithReason<>());
+    if (reachable.add(encodedField, reason) && isInstantiatedOrHasInstantiatedSubtype(
+        encodedField.field.clazz)) {
+      // We have at least one live subtype, so mark it as live.
+      markInstanceFieldAsLive(encodedField, reason);
+    }
+  }
+
+  private void markVirtualMethodAsReachable(DexMethod method, boolean interfaceInvoke,
+      KeepReason reason) {
+    if (!virtualTargetsMarkedAsReachable.add(method)) {
+      return;
+    }
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
+    }
+    if (method.holder.isArrayType()) {
+      // This is an array type, so the actual class will be generated at runtime. We treat this
+      // like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
+      // As it has no subtypes, it cannot affect liveness of the program we are processing.
+      // Ergo, we can ignore it. We need to make sure that the element type is available, though.
+      DexType baseType = method.holder.toBaseType(appInfo.dexItemFactory);
+      if (baseType.isClassType()) {
+        markTypeAsLive(baseType);
+      }
+      return;
+    }
+    DexClass holder = appInfo.definitionFor(method.holder);
+    if (holder == null) {
+      reportMissingClass(method.holder);
+      return;
+    }
+    DexEncodedMethod topTarget = appInfo.lookupVirtualDefinition(method.holder, method);
+    if (topTarget == null) {
+      reportMissingMethod(method);
+      return;
+    }
+    // We have to mark this as targeted, as even if this specific instance never becomes live, we
+    // need at least an abstract version of it so that we have a target for the corresponding
+    // invoke.
+    markMethodAsTargeted(topTarget, reason);
+    Set<DexEncodedMethod> targets;
+    if (interfaceInvoke) {
+      if (!holder.isInterface()) {
+        throw new CompilationError(
+            "InvokeInterface on non-interface method " + method.toSourceString() + ".");
+      }
+      targets = appInfo.lookupInterfaceTargets(method);
+    } else {
+      if (holder.isInterface()) {
+        throw new CompilationError(
+            "InvokeVirtual on interface method " + method.toSourceString() + ".");
+      }
+      targets = appInfo.lookupVirtualTargets(method);
+    }
+    for (DexEncodedMethod encodedMethod : targets) {
+      SetWithReason<DexEncodedMethod> reachable = reachableVirtualMethods
+          .computeIfAbsent(encodedMethod.method.holder, (ignore) -> new SetWithReason<>());
+      if (reachable.add(encodedMethod, reason)) {
+        // If the holder type is instantiated, the method is live. Otherwise check whether we find
+        // a subtype that does not shadow this methods but is instantiated.
+        // Note that library classes are always considered instantiated, as we do not know where
+        // they are instantiated.
+        if (isInstantiatedOrHasInstantiatedSubtype(encodedMethod.method.holder)) {
+          if (instantiatedTypes.contains(encodedMethod.method.holder)) {
+            markVirtualMethodAsLive(encodedMethod,
+                KeepReason.reachableFromLiveType(encodedMethod.method.holder));
+          } else {
+            Deque<DexType> worklist = new ArrayDeque<>();
+            fillWorkList(worklist, encodedMethod.method.holder);
+            while (!worklist.isEmpty()) {
+              DexType current = worklist.pollFirst();
+              DexClass currentHolder = appInfo.definitionFor(current);
+              if (currentHolder == null
+                  || currentHolder.findVirtualTarget(encodedMethod.method) != null) {
+                continue;
+              }
+              if (instantiatedTypes.contains(current)) {
+                markVirtualMethodAsLive(encodedMethod, KeepReason.reachableFromLiveType(current));
+                break;
+              }
+              fillWorkList(worklist, current);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private static void fillWorkList(Deque<DexType> worklist, DexType type) {
+    if (type.isInterface()) {
+      // We need to check if the method is shadowed by a class that directly implements
+      // the interface and go recursively down to the sub interfaces to reach class
+      // implementing the interface
+      type.forAllImplementsSubtypes(worklist::addLast);
+      type.forAllExtendsSubtypes(worklist::addLast);
+    } else {
+      type.forAllExtendsSubtypes(worklist::addLast);
+    }
+  }
+
+  private void markSuperMethodAsReachable(DexMethod method, DexEncodedMethod from) {
+    DexEncodedMethod target = appInfo.lookupVirtualTarget(method.holder, method);
+    if (target == null) {
+      reportMissingMethod(method);
+      return;
+    }
+    assert !superInvokeDependencies.containsKey(from) || !superInvokeDependencies.get(from)
+        .contains(target);
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Adding super constraint from `%s` to `%s`", from.method,
+          target.method);
+    }
+    superInvokeDependencies.computeIfAbsent(from, ignore -> Sets.newIdentityHashSet()).add(target);
+    if (liveMethods.contains(from)) {
+      markVirtualMethodAsLive(target, KeepReason.invokedViaSuperFrom(from));
+    }
+  }
+
+  public ReasonPrinter getReasonPrinter(Set<DexItem> queriedItems) {
+    // If no reason was asked, just return a no-op printer to avoid computing the information.
+    // This is the common path.
+    if (queriedItems.isEmpty()) {
+      return ReasonPrinter.getNoOpPrinter();
+    }
+    Map<DexItem, KeepReason> reachability = new HashMap<>();
+    for (SetWithReason<DexEncodedMethod> mappings : reachableVirtualMethods.values()) {
+      reachability.putAll(mappings.getReasons());
+    }
+    for (SetWithReason<DexEncodedField> mappings : reachableInstanceFields.values()) {
+      reachability.putAll(mappings.getReasons());
+    }
+    return new ReasonPrinter(queriedItems, liveFields.getReasons(), liveMethods.getReasons(),
+        reachability, instantiatedTypes.getReasons());
+  }
+
+  public AppInfoWithLiveness run(Timing timing) {
+    timing.begin("Grow the tree.");
+    appInfo.libraryClasses().forEach(this::markAllVirtualMethodsReachable);
+    try {
+      while (!workList.isEmpty()) {
+        Action action = workList.poll();
+        switch (action.kind) {
+          case MARK_INSTANTIATED:
+            processNewlyInstantiatedClass((DexClass) action.target, action.reason);
+            break;
+          case MARK_REACHABLE_FIELD:
+            markFieldAsReachable((DexField) action.target, action.reason);
+            break;
+          case MARK_REACHABLE_VIRTUAL:
+            markVirtualMethodAsReachable((DexMethod) action.target, false, action.reason);
+            break;
+          case MARK_REACHABLE_INTERFACE:
+            markVirtualMethodAsReachable((DexMethod) action.target, true, action.reason);
+            break;
+          case MARK_REACHABLE_SUPER:
+            markSuperMethodAsReachable((DexMethod) action.target,
+                (DexEncodedMethod) action.context);
+            break;
+          case MARK_METHOD_KEPT:
+            markMethodAsKept((DexEncodedMethod) action.target, action.reason);
+            break;
+          case MARK_FIELD_KEPT:
+            markFieldAsKept((DexEncodedField) action.target, action.reason);
+            break;
+          case MARK_METHOD_LIVE:
+            processNewlyLiveMethod(((DexEncodedMethod) action.target), action.reason);
+            break;
+          default:
+            throw new IllegalArgumentException(action.kind.toString());
+        }
+      }
+      if (Log.ENABLED) {
+        Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
+        for (Entry<DexType, SetWithReason<DexEncodedMethod>> entry : reachableVirtualMethods
+            .entrySet()) {
+          allLive.addAll(entry.getValue().getItems());
+        }
+        Set reachableNotLive = Sets.difference(allLive, liveMethods.getItems());
+        Log.debug(getClass(), "%s methods are reachable but not live", reachableNotLive.size());
+        Log.info(getClass(), "Only reachable: %s", reachableNotLive);
+        Set liveButNotInstantiated = Sets.difference(liveTypes, instantiatedTypes.getItems());
+        Log.debug(getClass(), "%s classes are live but not instantiated",
+            liveButNotInstantiated.size());
+        Log.info(getClass(), "Live but not instantiated: %s", liveButNotInstantiated);
+        SetView<DexEncodedMethod> targetedButNotLive = Sets
+            .difference(targetedMethods.getItems(), liveMethods.getItems());
+        Log.debug(getClass(), "%s methods are targeted but not live", targetedButNotLive.size());
+        Log.info(getClass(), "Targeted but not live: %s", targetedButNotLive);
+      }
+      assert liveTypes.stream().allMatch(DexType::isClassType);
+      assert instantiatedTypes.getItems().stream().allMatch(DexType::isClassType);
+    } finally {
+      timing.end();
+    }
+    return new AppInfoWithLiveness(appInfo, this);
+  }
+
+  private void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
+    DexClass holder = appInfo.definitionFor(target.method.holder);
+    // If this method no longer has a corresponding class then we have shaken it away before.
+    if (holder == null) {
+      return;
+    }
+    if (!target.accessFlags.isStatic()
+        && !target.accessFlags.isConstructor()
+        && !target.accessFlags.isPrivate()) {
+      // A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
+      // their overrides. However, we don't mark it live, as a keep rule might not imply that
+      // the corresponding class is live.
+      markVirtualMethodAsReachable(target.method, holder.accessFlags.isInterface(), reason);
+    } else {
+      markDirectStaticOrConstructorMethodAsLive(target, reason);
+    }
+  }
+
+  private void markFieldAsKept(DexEncodedField target, KeepReason reason) {
+    // If this field no longer has a corresponding class, then we have shaken it away before.
+    if (appInfo.definitionFor(target.field.clazz) == null) {
+      return;
+    }
+    if (target.accessFlags.isStatic()) {
+      markStaticFieldAsLive(target.field, reason);
+    } else {
+      markFieldAsReachable(target.field, reason);
+    }
+  }
+
+  private void markAllVirtualMethodsReachable(DexClass clazz) {
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Marking all methods of library class `%s` as reachable.",
+          clazz.type);
+    }
+    for (DexEncodedMethod encodedMethod : clazz.virtualMethods()) {
+      markMethodAsTargeted(encodedMethod, KeepReason.isLibraryMethod());
+      markVirtualMethodAsReachable(encodedMethod.method, clazz.isInterface(),
+          KeepReason.isLibraryMethod());
+    }
+  }
+
+  private void processNewlyLiveMethod(DexEncodedMethod method, KeepReason reason) {
+    if (liveMethods.add(method, reason)) {
+      DexClass holder = appInfo.definitionFor(method.method.holder);
+      assert holder != null;
+      if (holder.isLibraryClass()) {
+        // We do not process library classes.
+        return;
+      }
+      Set<DexEncodedMethod> superCallTargets = superInvokeDependencies.get(method);
+      if (superCallTargets != null) {
+        for (DexEncodedMethod superCallTarget : superCallTargets) {
+          if (Log.ENABLED) {
+            Log.verbose(getClass(), "Found super invoke constraint on `%s`.",
+                superCallTarget.method);
+          }
+          markVirtualMethodAsLive(superCallTarget, KeepReason.invokedViaSuperFrom(method));
+        }
+      }
+      processAnnotations(method.annotations.annotations);
+      for (DexAnnotationSet parameterAnnotation : method.parameterAnnotations.values) {
+        processAnnotations(parameterAnnotation.annotations);
+      }
+      method.registerReachableDefinitions(new UseRegistry(method));
+    }
+  }
+
+  private Set<DexField> collectFields(Map<DexType, Set<DexField>> map) {
+    Set<DexField> set = Sets.newIdentityHashSet();
+    map.values().forEach(set::addAll);
+    return set;
+  }
+
+  Set<DexField> collectInstanceFieldsRead() {
+    return Collections.unmodifiableSet(collectFields(instanceFieldsRead));
+  }
+
+  Set<DexField> collectInstanceFieldsWritten() {
+    return Collections.unmodifiableSet(collectFields(instanceFieldsWritten));
+  }
+
+  Set<DexField> collectStaticFieldsRead() {
+    return Collections.unmodifiableSet(collectFields(staticFieldsRead));
+  }
+
+  Set<DexField> collectStaticFieldsWritten() {
+    return Collections.unmodifiableSet(collectFields(staticFieldsWritten));
+  }
+
+  private static class Action {
+
+    final Kind kind;
+    final DexItem target;
+    final DexItem context;
+    final KeepReason reason;
+
+    private Action(Kind kind, DexItem target, DexItem context, KeepReason reason) {
+      this.kind = kind;
+      this.target = target;
+      this.context = context;
+      this.reason = reason;
+    }
+
+    public static Action markReachableVirtual(DexMethod method, KeepReason reason) {
+      return new Action(Kind.MARK_REACHABLE_VIRTUAL, method, null, reason);
+    }
+
+    public static Action markReachableInterface(DexMethod method, KeepReason reason) {
+      return new Action(Kind.MARK_REACHABLE_INTERFACE, method, null, reason);
+    }
+
+    public static Action markReachableSuper(DexMethod method, DexEncodedMethod from) {
+      return new Action(Kind.MARK_REACHABLE_SUPER, method, from, null);
+    }
+
+    public static Action markReachableField(DexField field, KeepReason reason) {
+      return new Action(Kind.MARK_REACHABLE_FIELD, field, null, reason);
+    }
+
+    public static Action markInstantiated(DexClass clazz, KeepReason reason) {
+      return new Action(Kind.MARK_INSTANTIATED, clazz, null, reason);
+    }
+
+    public static Action markMethodLive(DexEncodedMethod method, KeepReason reason) {
+      return new Action(Kind.MARK_METHOD_LIVE, method, null, reason);
+    }
+
+    public static Action markMethodKept(DexEncodedMethod method, KeepReason reason) {
+      return new Action(Kind.MARK_METHOD_KEPT, method, null, reason);
+    }
+
+    public static Action markFieldKept(DexEncodedField method, KeepReason reason) {
+      return new Action(Kind.MARK_FIELD_KEPT, method, null, reason);
+    }
+
+    public static Action forRootItem(Map.Entry<DexItem, ProguardKeepRule> root) {
+      DexItem item = root.getKey();
+      KeepReason reason = KeepReason.dueToKeepRule(root.getValue());
+      if (item instanceof DexClass) {
+        return markInstantiated((DexClass) item, reason);
+      } else if (item instanceof DexEncodedField) {
+        return markFieldKept((DexEncodedField) item, reason);
+      } else if (item instanceof DexEncodedMethod) {
+        return markMethodKept((DexEncodedMethod) item, reason);
+      } else {
+        throw new IllegalArgumentException(item.toString());
+      }
+    }
+
+    private enum Kind {
+      MARK_REACHABLE_VIRTUAL,
+      MARK_REACHABLE_INTERFACE,
+      MARK_REACHABLE_SUPER,
+      MARK_REACHABLE_FIELD,
+      MARK_INSTANTIATED,
+      MARK_METHOD_LIVE,
+      MARK_METHOD_KEPT,
+      MARK_FIELD_KEPT
+    }
+  }
+
+  /**
+   * Encapsulates liveness and reachability information for an application.
+   */
+  public static class AppInfoWithLiveness extends AppInfoWithSubtyping {
+
+    /**
+     * Set of types that are mentioned in the program. We at least need an empty abstract classitem
+     * for these.
+     */
+    final Set<DexType> liveTypes;
+    /**
+     * Set of types that are actually instantiated. These cannot be abstract.
+     */
+    final Set<DexType> instantiatedTypes;
+    /**
+     * Set of methods that are the immediate target of an invoke. They might not actually be live
+     * but are required so that invokes can find the method. If such a method is not live (i.e. not
+     * contained in {@link #liveMethods}, it may be marked as abstract and its implementation may be
+     * removed.
+     */
+    final Set<DexMethod> targetedMethods;
+    /**
+     * Set of methods that belong to live classes and can be reached by invokes. These need to be
+     * kept.
+     */
+    final Set<DexMethod> liveMethods;
+    /**
+     * Set of fields that belong to live classes and can be reached by invokes. These need to be
+     * kept.
+     */
+    final Set<DexField> liveFields;
+    /**
+     * Set of all fields which are read. Combines {@link #instanceFieldsRead} and
+     * {@link #staticFieldsRead}.
+     */
+    public final Set<DexField> fieldsRead;
+    /**
+     * Set of all fields which are written. Combines {@link #instanceFieldsWritten} and
+     * {@link #staticFieldsWritten}.
+     */
+    public final Set<DexField> fieldsWritten;
+    /**
+     * Set of all instance fields which are read.
+     */
+    public final Set<DexField> instanceFieldsRead;
+    /**
+     * Set of all instance fields which are written.
+     */
+    public final Set<DexField> instanceFieldsWritten;
+    /**
+     * Set of all static fields which are read.
+     */
+    public final Set<DexField> staticFieldsRead;
+    /**
+     * Set of all static fields which are written.
+     */
+    public final Set<DexField> staticFieldsWritten;
+    /**
+     * Set of all methods referenced in virtual invokes;
+     */
+    public final Set<DexMethod> virtualInvokes;
+    /**
+     * Set of all methods referenced in super invokes;
+     */
+    public final Set<DexMethod> superInvokes;
+    /**
+     * Set of all methods referenced in direct invokes;
+     */
+    public final Set<DexMethod> directInvokes;
+    /**
+     * Set of all methods referenced in static invokes;
+     */
+    public final Set<DexMethod> staticInvokes;
+    /**
+     * Set of all items that have to be kept independent of whether they are used.
+     */
+    public final Set<DexItem> pinnedItems;
+    /**
+     * All items with assumenosideeffects rule.
+     */
+    public final Map<DexItem, ProguardMemberRule> noSideEffects;
+    /**
+     * All items with assumevalues rule.
+     */
+    public final Map<DexItem, ProguardMemberRule> assumedValues;
+
+    private AppInfoWithLiveness(AppInfoWithSubtyping appInfo, Enqueuer enqueuer) {
+      super(appInfo);
+      this.liveTypes = Collections.unmodifiableSet(enqueuer.liveTypes);
+      this.instantiatedTypes = enqueuer.instantiatedTypes.getItems();
+      this.targetedMethods = toDescriptorSet(enqueuer.targetedMethods.getItems());
+      this.liveMethods = toDescriptorSet(enqueuer.liveMethods.getItems());
+      this.liveFields = toDescriptorSet(enqueuer.liveFields.getItems());
+      this.instanceFieldsRead = enqueuer.collectInstanceFieldsRead();
+      this.instanceFieldsWritten = enqueuer.collectInstanceFieldsWritten();
+      this.staticFieldsRead = enqueuer.collectStaticFieldsRead();
+      this.staticFieldsWritten = enqueuer.collectStaticFieldsWritten();
+      this.fieldsRead = Sets.union(staticFieldsRead, instanceFieldsRead);
+      this.fieldsWritten = Sets.union(staticFieldsWritten, instanceFieldsWritten);
+      this.pinnedItems = Collections.unmodifiableSet(enqueuer.pinnedItems);
+      this.virtualInvokes = joinInvokedMethods(enqueuer.virtualInvokes);
+      this.superInvokes = joinInvokedMethods(enqueuer.superInvokes);
+      this.directInvokes = joinInvokedMethods(enqueuer.directInvokes);
+      this.staticInvokes = joinInvokedMethods(enqueuer.staticInvokes);
+      this.noSideEffects = enqueuer.rootSet.noSideEffects;
+      this.assumedValues = enqueuer.rootSet.assumedValues;
+      assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
+      assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+    }
+
+    private AppInfoWithLiveness(AppInfoWithLiveness previous, DexApplication application) {
+      super(application);
+      this.liveTypes = previous.liveTypes;
+      this.instantiatedTypes = previous.instantiatedTypes;
+      this.targetedMethods = previous.targetedMethods;
+      this.liveMethods = previous.liveMethods;
+      this.liveFields = previous.liveFields;
+      this.instanceFieldsRead = previous.instanceFieldsRead;
+      this.instanceFieldsWritten = previous.instanceFieldsWritten;
+      this.staticFieldsRead = previous.staticFieldsRead;
+      this.staticFieldsWritten = previous.staticFieldsWritten;
+      this.fieldsRead = previous.fieldsRead;
+      this.fieldsWritten = previous.fieldsWritten;
+      this.pinnedItems = previous.pinnedItems;
+      this.noSideEffects = previous.noSideEffects;
+      this.assumedValues = previous.assumedValues;
+      this.virtualInvokes = previous.virtualInvokes;
+      this.superInvokes = previous.superInvokes;
+      this.directInvokes = previous.directInvokes;
+      this.staticInvokes = previous.staticInvokes;
+      assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
+      assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+    }
+
+    private AppInfoWithLiveness(AppInfoWithLiveness previous, GraphLense lense) {
+      super(previous, lense);
+      this.liveTypes = previous.liveTypes;
+      this.instantiatedTypes = rewriteItems(previous.instantiatedTypes, lense::lookupType);
+      this.targetedMethods = rewriteItems(previous.targetedMethods, lense::lookupMethod);
+      this.liveMethods = rewriteItems(previous.liveMethods, lense::lookupMethod);
+      this.liveFields = rewriteItems(previous.liveFields, lense::lookupField);
+      this.instanceFieldsRead = rewriteItems(previous.instanceFieldsRead, lense::lookupField);
+      this.instanceFieldsWritten = rewriteItems(previous.instanceFieldsWritten, lense::lookupField);
+      this.staticFieldsRead = rewriteItems(previous.staticFieldsRead, lense::lookupField);
+      this.staticFieldsWritten = rewriteItems(previous.staticFieldsWritten, lense::lookupField);
+      this.fieldsRead = Sets.union(staticFieldsRead, instanceFieldsRead);
+      this.fieldsWritten = Sets.union(staticFieldsWritten, instanceFieldsWritten);
+      // TODO(herhut): Migrate these to Descriptors, as well.
+      this.pinnedItems = previous.pinnedItems;
+      this.noSideEffects = previous.noSideEffects;
+      this.assumedValues = previous.assumedValues;
+      this.virtualInvokes = rewriteItems(previous.virtualInvokes, lense::lookupMethod);
+      this.superInvokes = rewriteItems(previous.superInvokes, lense::lookupMethod);
+      this.directInvokes = rewriteItems(previous.directInvokes, lense::lookupMethod);
+      this.staticInvokes = rewriteItems(previous.staticInvokes, lense::lookupMethod);
+      assert Sets.intersection(instanceFieldsRead, staticFieldsRead).size() == 0;
+      assert Sets.intersection(instanceFieldsWritten, staticFieldsWritten).size() == 0;
+    }
+
+    private Set<DexMethod> joinInvokedMethods(Map<DexType, Set<DexMethod>> invokes) {
+      ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+      invokes.values().forEach(builder::addAll);
+      return builder.build();
+    }
+
+    private <T extends PresortedComparable<T>> Set<T> toDescriptorSet(
+        Set<? extends KeyedDexItem<T>> set) {
+      ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+      for (KeyedDexItem<T> item : set) {
+        builder.add(item.getKey());
+      }
+      return builder.build();
+    }
+
+    private static <T> ImmutableSet<T> rewriteItems(Set<T> original,
+        BiFunction<T, DexEncodedMethod, T> rewrite) {
+      ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+      for (T item : original) {
+        builder.add(rewrite.apply(item, null));
+      }
+      return builder.build();
+    }
+
+    @Override
+    public boolean hasLiveness() {
+      return true;
+    }
+
+    @Override
+    public AppInfoWithLiveness withLiveness() {
+      return this;
+    }
+
+    /**
+     * Returns a copy of this AppInfoWithLiveness where the set of classes is pruned using the
+     * given DexApplication object.
+     */
+    public AppInfoWithLiveness prunedCopyFrom(DexApplication application) {
+      return new AppInfoWithLiveness(this, application);
+    }
+
+    public AppInfoWithLiveness rewrittenWithLense(GraphLense lense) {
+      assert lense.isContextFree();
+      return new AppInfoWithLiveness(this, lense);
+    }
+  }
+
+  private static class SetWithReason<T> {
+
+    private final Set<T> items = Sets.newIdentityHashSet();
+    private final Map<T, KeepReason> reasons = Maps.newIdentityHashMap();
+
+    boolean add(T item, KeepReason reason) {
+      if (items.add(item)) {
+        reasons.put(item, reason);
+        return true;
+      }
+      return false;
+    }
+
+    boolean contains(T item) {
+      return items.contains(item);
+    }
+
+    Set<T> getItems() {
+      return Collections.unmodifiableSet(items);
+    }
+
+    Map<T, KeepReason> getReasons() {
+      return Collections.unmodifiableMap(reasons);
+    }
+  }
+
+  private class AnnotationReferenceMarker implements IndexedItemCollection {
+
+    private final DexItem annotationHolder;
+    private final DexItemFactory dexItemFactory;
+
+    private AnnotationReferenceMarker(DexItem annotationHolder, DexItemFactory dexItemFactory) {
+      this.annotationHolder = annotationHolder;
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    @Override
+    public boolean addClass(DexProgramClass dexProgramClass) {
+      return false;
+    }
+
+    @Override
+    public boolean addField(DexField field) {
+      DexClass holder = appInfo.definitionFor(field.clazz);
+      if (holder == null) {
+        return false;
+      }
+      DexEncodedField target = holder.findStaticTarget(field);
+      if (target != null) {
+        // There is no dispatch on annotations, so only keep what is directly referenced.
+        if (target.field == field) {
+          markStaticFieldAsLive(field, KeepReason.referencedInAnnotation(annotationHolder));
+        }
+      } else {
+        target = holder.findInstanceTarget(field);
+        // There is no dispatch on annotations, so only keep what is directly referenced.
+        if (target != null && target.field != field) {
+          markFieldAsReachable(field, KeepReason.referencedInAnnotation(annotationHolder));
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public boolean addMethod(DexMethod method) {
+      DexClass holder = appInfo.definitionFor(method.holder);
+      if (holder == null) {
+        return false;
+      }
+      DexEncodedMethod target = holder.findDirectTarget(method);
+      if (target != null) {
+        // There is no dispatch on annotations, so only keep what is directly referenced.
+        if (target.method == method) {
+          markDirectStaticOrConstructorMethodAsLive(
+              target, KeepReason.referencedInAnnotation(annotationHolder));
+        }
+      } else {
+        target = holder.findVirtualTarget(method);
+        // There is no dispatch on annotations, so only keep what is directly referenced.
+        if (target != null && target.method == method) {
+          markMethodAsTargeted(target, KeepReason.referencedInAnnotation(annotationHolder));
+        }
+      }
+      return false;
+    }
+
+    @Override
+    public boolean addString(DexString string) {
+      return false;
+    }
+
+    @Override
+    public boolean addProto(DexProto proto) {
+      return false;
+    }
+
+    @Override
+    public boolean addCallSite(DexCallSite callSite) {
+      return false;
+    }
+
+    @Override
+    public boolean addMethodHandle(DexMethodHandle methodHandle) {
+      return false;
+    }
+
+    @Override
+    public boolean addType(DexType type) {
+      // Annotations can also contain the void type, which is not a class type, so filter it out
+      // here.
+      if (type != dexItemFactory.voidType) {
+        markTypeAsLive(type);
+      }
+      return false;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepReason.java b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
new file mode 100644
index 0000000..4e2f0b2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/KeepReason.java
@@ -0,0 +1,176 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ReasonPrinter.ReasonFormatter;
+
+// TODO(herhut): Canonicalize reason objects.
+abstract class KeepReason {
+
+  static KeepReason dueToKeepRule(ProguardKeepRule rule) {
+    return new DueToKeepRule(rule);
+  }
+
+  static KeepReason instantiatedIn(DexEncodedMethod method) {
+    return new InstatiatedIn(method);
+  }
+
+  public static KeepReason invokedViaSuperFrom(DexEncodedMethod from) {
+    return new InvokedViaSuper(from);
+  }
+
+  public static KeepReason reachableFromLiveType(DexType type) {
+    return new ReachableFromLiveType(type);
+  }
+
+  public static KeepReason invokedFrom(DexEncodedMethod method) {
+    return new InvokedFrom(method);
+  }
+
+  public static KeepReason isLibraryMethod() {
+    return new IsLibraryMethod();
+  }
+
+  public static KeepReason fieldReferencedIn(DexEncodedMethod method) {
+    return new ReferenedFrom(method);
+  }
+
+  public static KeepReason referencedInAnnotation(DexItem holder) {
+    return new ReferencedInAnnotation(holder);
+  }
+
+  public abstract void print(ReasonFormatter formatter);
+
+  private static class DueToKeepRule extends KeepReason {
+
+    private final ProguardKeepRule keepRule;
+
+    private DueToKeepRule(ProguardKeepRule keepRule) {
+      this.keepRule = keepRule;
+    }
+
+    @Override
+    public void print(ReasonFormatter formatter) {
+      formatter.addReason("referenced in keep rule:");
+      formatter.addMessage("  " + keepRule + " {");
+      int ruleCount = 0;
+      for (ProguardMemberRule memberRule : keepRule.getMemberRules()) {
+        formatter.addMessage("    " + memberRule);
+        if (++ruleCount > 10) {
+          formatter.addMessage("      <...>");
+          break;
+        }
+      }
+      formatter.addMessage("  };");
+    }
+  }
+
+  private abstract static class BasedOnOtherMethod extends KeepReason {
+
+    private final DexEncodedMethod method;
+
+    private BasedOnOtherMethod(DexEncodedMethod method) {
+      this.method = method;
+    }
+
+    abstract String getKind();
+
+    public void print(ReasonFormatter formatter) {
+      formatter.addReason("is " + getKind() + " " + method.toSourceString());
+      formatter.addMethodReferenceReason(method);
+    }
+
+  }
+
+  private static class InstatiatedIn extends BasedOnOtherMethod {
+
+    private InstatiatedIn(DexEncodedMethod method) {
+      super(method);
+    }
+
+    @Override
+    String getKind() {
+      return "instantiated in";
+    }
+  }
+
+  private static class InvokedViaSuper extends BasedOnOtherMethod {
+
+    private InvokedViaSuper(DexEncodedMethod method) {
+      super(method);
+    }
+
+    @Override
+    String getKind() {
+      return "invoked via super from";
+    }
+  }
+
+  private static class InvokedFrom extends BasedOnOtherMethod {
+
+    private InvokedFrom(DexEncodedMethod method) {
+      super(method);
+    }
+
+    @Override
+    String getKind() {
+      return "invoked from";
+    }
+  }
+
+  private static class ReferenedFrom extends BasedOnOtherMethod {
+
+    private ReferenedFrom(DexEncodedMethod method) {
+      super(method);
+    }
+
+    @Override
+    String getKind() {
+      return "referenced from";
+    }
+  }
+
+  private static class ReachableFromLiveType extends KeepReason {
+
+    private final DexType type;
+
+    private ReachableFromLiveType(DexType type) {
+      this.type = type;
+    }
+
+    @Override
+    public void print(ReasonFormatter formatter) {
+      formatter.addReason("is reachable from type " + type.toSourceString());
+      formatter.addTypeLivenessReason(type);
+    }
+  }
+
+  private static class IsLibraryMethod extends KeepReason {
+
+    private IsLibraryMethod() {
+    }
+
+    @Override
+    public void print(ReasonFormatter formatter) {
+      formatter.addReason("is defined in a library.");
+    }
+  }
+
+  private static class ReferencedInAnnotation extends KeepReason {
+
+    private final DexItem holder;
+
+    private ReferencedInAnnotation(DexItem holder) {
+      this.holder = holder;
+    }
+
+    @Override
+    public void print(ReasonFormatter formatter) {
+      formatter.addReason("is referenced in annotation on " + holder.toSourceString());
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
new file mode 100644
index 0000000..cc54a49
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
+
+  public static class Builder extends ProguardClassSpecification.Builder {
+
+    private Builder() {}
+
+    public ProguardAssumeNoSideEffectRule build() {
+      return new ProguardAssumeNoSideEffectRule(classAnnotation, classAccessFlags,
+          negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+          inheritanceClassName, inheritanceIsExtends, memberRules);
+    }
+  }
+
+  private ProguardAssumeNoSideEffectRule(
+      ProguardTypeMatcher classAnnotation,
+      DexAccessFlags classAccessFlags,
+      DexAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      List<ProguardTypeMatcher> classNames,
+      ProguardTypeMatcher inheritanceAnnotation,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      Set<ProguardMemberRule> memberRules) {
+    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+  }
+
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  public boolean applyToLibraryClasses() {
+    return true;
+  }
+
+  @Override
+  String typeString() {
+    return "assumenosideeffects";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
new file mode 100644
index 0000000..73825f1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
+  public static class Builder extends ProguardClassSpecification.Builder {
+
+    private Builder() {}
+
+    public ProguardAssumeValuesRule build() {
+      return new ProguardAssumeValuesRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
+          classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+          inheritanceIsExtends, memberRules);
+    }
+  }
+
+  private ProguardAssumeValuesRule(
+      ProguardTypeMatcher classAnnotation,
+      DexAccessFlags classAccessFlags,
+      DexAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      List<ProguardTypeMatcher> classNames,
+      ProguardTypeMatcher inheritanceAnnotation,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      Set<ProguardMemberRule> memberRules) {
+    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+  }
+
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  String typeString() {
+    return "assumevalues";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
new file mode 100644
index 0000000..396d37d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -0,0 +1,288 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+public abstract class ProguardClassSpecification {
+
+  public static class Builder {
+
+    protected ProguardTypeMatcher classAnnotation;
+    protected DexAccessFlags classAccessFlags = new DexAccessFlags(0);
+    protected DexAccessFlags negatedClassAccessFlags = new DexAccessFlags(0);
+    protected boolean classTypeNegated = false;
+    protected ProguardClassType classType;
+    protected List<ProguardTypeMatcher> classNames;
+    protected ProguardTypeMatcher inheritanceAnnotation;
+    protected ProguardTypeMatcher inheritanceClassName;
+    protected boolean inheritanceIsExtends = false;
+    protected Set<ProguardMemberRule> memberRules = new LinkedHashSet<>();
+
+    protected Builder() {
+    }
+
+    public Set<ProguardMemberRule> getMemberRules() {
+      return memberRules;
+    }
+
+    public void setMemberRules(Set<ProguardMemberRule> memberRules) {
+      this.memberRules = memberRules;
+    }
+
+    public boolean getInheritanceIsExtends() {
+      return inheritanceIsExtends;
+    }
+
+    public void setInheritanceIsExtends(boolean inheritanceIsExtends) {
+      this.inheritanceIsExtends = inheritanceIsExtends;
+    }
+
+    public boolean hasInheritanceClassName() {
+      return inheritanceClassName != null;
+    }
+
+    public ProguardTypeMatcher getInheritanceClassName() {
+      return inheritanceClassName;
+    }
+
+    public void setInheritanceClassName(ProguardTypeMatcher inheritanceClassName) {
+      this.inheritanceClassName = inheritanceClassName;
+    }
+
+    public ProguardTypeMatcher getInheritanceAnnotation() {
+      return inheritanceAnnotation;
+    }
+
+    public void setInheritanceAnnotation(ProguardTypeMatcher inheritanceAnnotation) {
+      this.inheritanceAnnotation = inheritanceAnnotation;
+    }
+
+    public List<ProguardTypeMatcher> getClassNames() {
+      return classNames;
+    }
+
+    public void setClassNames(List<ProguardTypeMatcher> classNames) {
+      this.classNames = classNames;
+    }
+
+    public ProguardClassType getClassType() {
+      return classType;
+    }
+
+    public void setClassType(ProguardClassType classType) {
+      this.classType = classType;
+    }
+
+    public boolean getClassTypeNegated() {
+      return classTypeNegated;
+    }
+
+    public void setClassTypeNegated(boolean classTypeNegated) {
+      this.classTypeNegated = classTypeNegated;
+    }
+
+    public DexAccessFlags getClassAccessFlags() {
+      return classAccessFlags;
+    }
+
+    public void setClassAccessFlags(DexAccessFlags flags) {
+      classAccessFlags = flags;
+    }
+
+    public DexAccessFlags getNegatedClassAccessFlags() {
+      return negatedClassAccessFlags;
+    }
+
+    public void setNegatedClassAccessFlags(DexAccessFlags flags) {
+      negatedClassAccessFlags = flags;
+    }
+
+    public ProguardTypeMatcher getClassAnnotation() {
+      return classAnnotation;
+    }
+
+    public void setClassAnnotation(ProguardTypeMatcher classAnnotation) {
+      this.classAnnotation = classAnnotation;
+    }
+
+    protected void matchAllSpecification() {
+      setClassNames(Collections.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
+      setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
+    }
+  }
+
+  private final ProguardTypeMatcher classAnnotation;
+  private final DexAccessFlags classAccessFlags;
+  private final DexAccessFlags negatedClassAccessFlags;
+  private final boolean classTypeNegated;
+  private final ProguardClassType classType;
+  private final List<ProguardTypeMatcher> classNames;
+  private final ProguardTypeMatcher inheritanceAnnotation;
+  private final ProguardTypeMatcher inheritanceClassName;
+  private final boolean inheritanceIsExtends;
+  private final Set<ProguardMemberRule> memberRules;
+
+  protected ProguardClassSpecification(
+      ProguardTypeMatcher classAnnotation,
+      DexAccessFlags classAccessFlags,
+      DexAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      List<ProguardTypeMatcher> classNames,
+      ProguardTypeMatcher inheritanceAnnotation,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      Set<ProguardMemberRule> memberRules) {
+    this.classAnnotation = classAnnotation;
+    this.classAccessFlags = classAccessFlags;
+    this.negatedClassAccessFlags = negatedClassAccessFlags;
+    this.classTypeNegated = classTypeNegated;
+    this.classType = classType;
+    this.classNames = classNames;
+    this.inheritanceAnnotation = inheritanceAnnotation;
+    this.inheritanceClassName = inheritanceClassName;
+    this.inheritanceIsExtends = inheritanceIsExtends;
+    this.memberRules = memberRules;
+  }
+
+  public Set<ProguardMemberRule> getMemberRules() {
+    return memberRules;
+  }
+
+  public boolean getInheritanceIsExtends() {
+    return inheritanceIsExtends;
+  }
+
+  public boolean hasInheritanceClassName() {
+    return inheritanceClassName != null;
+  }
+
+  public ProguardTypeMatcher getInheritanceClassName() {
+    return inheritanceClassName;
+  }
+
+  public ProguardTypeMatcher getInheritanceAnnotation() {
+    return inheritanceAnnotation;
+  }
+
+  public List<ProguardTypeMatcher> getClassNames() {
+    return classNames;
+  }
+
+  public ProguardClassType getClassType() {
+    return classType;
+  }
+
+  public boolean getClassTypeNegated() {
+    return classTypeNegated;
+  }
+
+  public DexAccessFlags getClassAccessFlags() {
+    return classAccessFlags;
+  }
+
+  public DexAccessFlags getNegatedClassAccessFlags() {
+    return negatedClassAccessFlags;
+  }
+
+  public ProguardTypeMatcher getClassAnnotation() {
+    return classAnnotation;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ProguardClassSpecification)) {
+      return false;
+    }
+    ProguardClassSpecification that = (ProguardClassSpecification) o;
+
+    if (classTypeNegated != that.classTypeNegated) {
+      return false;
+    }
+    if (inheritanceIsExtends != that.inheritanceIsExtends) {
+      return false;
+    }
+    if (!Objects.equals(classAnnotation, that.classAnnotation)) {
+      return false;
+    }
+    if (!classAccessFlags.equals(that.classAccessFlags)) {
+      return false;
+    }
+    if (!negatedClassAccessFlags.equals(that.negatedClassAccessFlags)) {
+      return false;
+    }
+    if (classType != that.classType) {
+      return false;
+    }
+    if (!classNames.equals(that.classNames)) {
+      return false;
+    }
+    if (!Objects.equals(inheritanceAnnotation, that.inheritanceAnnotation)) {
+      return false;
+    }
+    if (!Objects.equals(inheritanceClassName, that.inheritanceClassName)) {
+      return false;
+    }
+    return memberRules.equals(that.memberRules);
+  }
+
+  @Override
+  public int hashCode() {
+    // Used multiplier 3 to avoid too much overflow when computing hashCode.
+    int result = (classAnnotation != null ? classAnnotation.hashCode() : 0);
+    result = 3 * result + classAccessFlags.hashCode();
+    result = 3 * result + negatedClassAccessFlags.hashCode();
+    result = 3 * result + (classTypeNegated ? 1 : 0);
+    result = 3 * result + (classType != null ? classType.hashCode() : 0);
+    result = 3 * result + classNames.hashCode();
+    result = 3 * result + (inheritanceAnnotation != null ? inheritanceAnnotation.hashCode() : 0);
+    result = 3 * result + (inheritanceClassName != null ? inheritanceClassName.hashCode() : 0);
+    result = 3 * result + (inheritanceIsExtends ? 1 : 0);
+    result = 3 * result + memberRules.hashCode();
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    StringUtils.appendNonEmpty(builder, " @", classAnnotation, null);
+    StringUtils.appendNonEmpty(builder, " ", classAccessFlags, null);
+    StringUtils.appendNonEmpty(builder, " !", negatedClassAccessFlags.toString().replace(" ", " !"),
+        null);
+    if (builder.length() > 0) {
+      builder.append(' ');
+    }
+    builder.append(classType);
+    builder.append(' ');
+    boolean first = true;
+    for (ProguardTypeMatcher className : classNames) {
+      builder.append(className);
+      if (!first) {
+        builder.append(',');
+      }
+      first = false;
+    }
+    if (hasInheritanceClassName()) {
+      builder.append(inheritanceIsExtends ? " extends" : " implements");
+      StringUtils.appendNonEmpty(builder, " @", inheritanceAnnotation, null);
+      builder.append(' ');
+      builder.append(inheritanceClassName);
+    }
+    builder.append(" {\n");
+    memberRules.forEach(memberRule -> {
+      builder.append("  ");
+      builder.append(memberRule);
+      builder.append(";\n");
+    });
+    builder.append("}");
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassType.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassType.java
new file mode 100644
index 0000000..aab0413
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassType.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum ProguardClassType {
+  ANNOTATION_INTERFACE,
+  CLASS,
+  ENUM,
+  INTERFACE;
+
+  @Override
+  public String toString() {
+    switch (this) {
+      case ANNOTATION_INTERFACE: return "@interface";
+      case CLASS: return "class";
+      case ENUM: return "enum";
+      case INTERFACE: return "interface";
+      default:
+        throw new Unreachable("Invalid proguard class type '" + this + "'");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
new file mode 100644
index 0000000..9a63008
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -0,0 +1,322 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.DictionaryReader;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardConfiguration {
+
+  public static class Builder {
+
+    private final List<Path> injars = new ArrayList<>();
+    private final List<Path> libraryjars = new ArrayList<>();
+    private String packagePrefix = null;
+    private boolean allowAccessModification = false;
+    private boolean ignoreWarnings = false;
+    private boolean obfuscating = true;
+    private boolean shrinking = true;
+    private boolean printMapping;
+    private Path printMappingOutput;
+    private boolean verbose = false;
+    private final List<String> attributesRemovalPatterns = new ArrayList<>();
+    private final Set<ProguardTypeMatcher> dontWarnPatterns = new HashSet<>();
+    protected final List<ProguardConfigurationRule> rules = new ArrayList<>();
+    private final DexItemFactory dexItemFactory;
+    private boolean printSeeds;
+    private Path seedFile;
+    private Path obfuscationDictionary;
+    private Path classObfuscationDictionary;
+    private Path packageObfuscationDictionary;
+
+    private Builder(DexItemFactory dexItemFactory) {
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    public void addInjars(List<Path> injars) {
+      this.injars.addAll(injars);
+    }
+
+    public void addLibraryJars(List<Path> libraryJars) {
+      this.libraryjars.addAll(libraryJars);
+    }
+
+    public void setPackagePrefix(String packagePrefix) {
+      this.packagePrefix = packagePrefix;
+    }
+
+    public void setAllowAccessModification(boolean allowAccessModification) {
+      this.allowAccessModification = allowAccessModification;
+    }
+
+    public void setIgnoreWarnings(boolean ignoreWarnings) {
+      this.ignoreWarnings = ignoreWarnings;
+    }
+
+    public void setObfuscating(boolean obfuscate) {
+      this.obfuscating = obfuscate;
+    }
+
+    public void setShrinking(boolean shrinking) {
+      this.shrinking = shrinking;
+    }
+
+    public void setPrintMapping(boolean printMapping) {
+      this.printMapping = printMapping;
+    }
+
+    public void setPrintMappingOutput(Path file) {
+      this.printMappingOutput = file;
+    }
+
+    public void setVerbose(boolean verbose) {
+      this.verbose = verbose;
+    }
+
+    public void addAttributeRemovalPattern(String attributesRemovalPattern) {
+      this.attributesRemovalPatterns.add(attributesRemovalPattern);
+    }
+
+    public void addRule(ProguardConfigurationRule rule) {
+      this.rules.add(rule);
+    }
+
+    public void addDontWarnPattern(ProguardTypeMatcher pattern) {
+      dontWarnPatterns.add(pattern);
+    }
+
+    public void setSeedFile(Path seedFile) {
+      this.seedFile = seedFile;
+    }
+
+    public void setPrintSeed(boolean printSeeds) {
+      this.printSeeds = printSeeds;
+    }
+
+    public void setObfuscationDictionary(Path obfuscationDictionary) {
+      this.obfuscationDictionary = obfuscationDictionary;
+    }
+
+    public void setClassObfuscationDictionary(Path classObfuscationDictionary) {
+      this.classObfuscationDictionary = classObfuscationDictionary;
+    }
+
+    public void setPackageObfuscationDictionary(Path packageObfuscationDictionary) {
+      this.packageObfuscationDictionary = packageObfuscationDictionary;
+    }
+
+    public ProguardConfiguration build() {
+      return new ProguardConfiguration(
+          dexItemFactory,
+          injars,
+          libraryjars,
+          packagePrefix,
+          allowAccessModification,
+          ignoreWarnings,
+          obfuscating,
+          shrinking,
+          printMapping,
+          printMappingOutput,
+          verbose,
+          attributesRemovalPatterns,
+          dontWarnPatterns,
+          rules,
+          printSeeds,
+          seedFile,
+          DictionaryReader.readAllNames(obfuscationDictionary),
+          DictionaryReader.readAllNames(classObfuscationDictionary),
+          DictionaryReader.readAllNames(packageObfuscationDictionary));
+    }
+  }
+
+  private final DexItemFactory dexItemFactory;
+  private final List<Path> injars;
+  private final List<Path> libraryjars;
+  private final String packagePrefix;
+  private final boolean allowAccessModification;
+  private final boolean ignoreWarnings;
+  private final boolean obfuscating;
+  private final boolean shrinking;
+  private final boolean printMapping;
+  private final Path printMappingOutput;
+  private final boolean verbose;
+  private final List<String> attributesRemovalPatterns;
+  private final ImmutableSet<ProguardTypeMatcher> dontWarnPatterns;
+  protected final ImmutableList<ProguardConfigurationRule> rules;
+  private final boolean printSeeds;
+  private final Path seedFile;
+  private final List<String> obfuscationDictionary;
+  private final List<String> classObfuscationDictionary;
+  private final List<String> packageObfuscationDictionary;
+
+  private ProguardConfiguration(
+      DexItemFactory factory,
+      List<Path> injars,
+      List<Path> libraryjars,
+      String packagePrefix,
+      boolean allowAccessModification,
+      boolean ignoreWarnings,
+      boolean obfuscating,
+      boolean shrinking,
+      boolean printMapping,
+      Path printMappingOutput,
+      boolean verbose,
+      List<String> attributesRemovalPatterns,
+      Set<ProguardTypeMatcher> dontWarnPatterns,
+      List<ProguardConfigurationRule> rules,
+      boolean printSeeds,
+      Path seedFile,
+      List<String> obfuscationDictionary,
+      List<String> classObfuscationDictionary,
+      List<String> packageObfuscationDictionary) {
+    this.dexItemFactory = factory;
+    this.injars = ImmutableList.copyOf(injars);
+    this.libraryjars = ImmutableList.copyOf(libraryjars);
+    this.packagePrefix = packagePrefix;
+    this.allowAccessModification = allowAccessModification;
+    this.ignoreWarnings = ignoreWarnings;
+    this.obfuscating = obfuscating;
+    this.shrinking = shrinking;
+    this.printMapping = printMapping;
+    this.printMappingOutput = printMappingOutput;
+    this.verbose = verbose;
+    this.attributesRemovalPatterns = ImmutableList.copyOf(attributesRemovalPatterns);
+    this.dontWarnPatterns = ImmutableSet.copyOf(dontWarnPatterns);
+    this.rules = ImmutableList.copyOf(rules);
+    this.printSeeds = printSeeds;
+    this.seedFile = seedFile;
+    this.obfuscationDictionary = obfuscationDictionary;
+    this.classObfuscationDictionary = classObfuscationDictionary;
+    this.packageObfuscationDictionary = packageObfuscationDictionary;
+  }
+
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder(DexItemFactory dexItemFactory) {
+    return new Builder(dexItemFactory);
+  }
+
+  public DexItemFactory getDexItemFactory() {
+    return dexItemFactory;
+  }
+
+  public boolean isDefaultConfiguration() {
+    return false;
+  }
+
+  public List<Path> getInjars() {
+    return injars;
+  }
+
+  public List<Path> getLibraryjars() {
+    return libraryjars;
+  }
+
+  public String getPackagePrefix() {
+    return packagePrefix;
+  }
+
+  public boolean getAllowAccessModification() {
+    return allowAccessModification;
+  }
+
+  public boolean isPrintingMapping() {
+    return printMapping;
+  }
+
+  public Path getPrintMappingOutput() {
+    return printMappingOutput;
+  }
+
+  public boolean isIgnoreWarnings() {
+    return ignoreWarnings;
+  }
+
+  public boolean isObfuscating() {
+    return obfuscating;
+  }
+
+  public boolean isShrinking() {
+    return shrinking;
+  }
+
+  public boolean isVerbose() {
+    return verbose;
+  }
+
+  public List<String> getAttributesRemovalPatterns() {
+    return attributesRemovalPatterns;
+  }
+
+  public ImmutableSet<ProguardTypeMatcher> getDontWarnPatterns() {
+    return dontWarnPatterns;
+  }
+
+  public ImmutableList<ProguardConfigurationRule> getRules() {
+    return rules;
+  }
+
+  public List<String> getObfuscationDictionary() {
+    return obfuscationDictionary;
+  }
+
+  public List<String> getPackageObfuscationDictionary() {
+    return packageObfuscationDictionary;
+  }
+
+  public List<String> getClassObfuscationDictionary() {
+    return classObfuscationDictionary;
+  }
+
+  public static ProguardConfiguration defaultConfiguration(DexItemFactory dexItemFactory) {
+    ProguardConfiguration config = new DefaultProguardConfiguration(dexItemFactory);
+    return config;
+  }
+
+  public static class DefaultProguardConfiguration extends ProguardConfiguration {
+
+    public DefaultProguardConfiguration(DexItemFactory factory) {
+      super(factory,
+          ImmutableList.of()    /* injars */,
+          ImmutableList.of()    /* libraryjars */,
+          ""                    /* package prefix */,
+          false                 /* allowAccessModification */,
+          false                 /* ignoreWarnings */,
+          false                 /* obfuscating */,
+          false                 /* shrinking */,
+          false                 /* printMapping */,
+          null                  /* outputMapping */,
+          false                 /* verbose */,
+          ImmutableList.of()    /* attributesRemovalPatterns */,
+          ImmutableSet.of()     /* dontWarnPatterns */,
+          ImmutableList.of(ProguardKeepRule.defaultKeepAllRule()),
+          false                 /* printSeeds */,
+          null                  /* seedFile */,
+          ImmutableList.of()     /* obfuscationDictionary */,
+          ImmutableList.of()     /* classObfuscationDictionary */,
+          ImmutableList.of()     /* packageObfucationDictionary */);
+    }
+
+    @Override
+    public boolean isDefaultConfiguration() {
+      return true;
+    }
+  }
+
+  public boolean getPrintSeeds() {
+    return printSeeds;
+  }
+
+  public Path getSeedFile() {
+    return seedFile;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
new file mode 100644
index 0000000..69db32d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -0,0 +1,963 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.LongInterval;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ProguardConfigurationParser {
+
+  private final Builder configurationBuilder;
+
+  private final DexItemFactory dexItemFactory;
+
+  private static final List<String> ignoredSingleArgOptions = ImmutableList
+      .of("protomapping", "optimizationpasses");
+  private static final List<String> ignoredFlagOptions = ImmutableList
+      .of("forceprocessing", "dontusemixedcaseclassnames",
+          "dontpreverify", "experimentalshrinkunusedprotofields",
+          "filterlibraryjarswithorginalprogramjars",
+          "dontskipnonpubliclibraryclasses", "invokebasemethod");
+  private static final List<String> ignoredClassDescriptorOptions = ImmutableList
+      .of("isclassnamestring",
+          "alwaysinline", "identifiernamestring", "whyarenotsimple");
+
+  private static final List<String> warnedSingleArgOptions = ImmutableList
+      .of("printusage",
+          "renamesourcefileattribute",
+          "dontnote",
+          "printconfiguration",
+          // TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
+          // should be reported as errors, not just as warnings!
+          "outjars",
+          "adaptresourcefilecontents");
+  private static final List<String> warnedFlagOptions = ImmutableList
+      .of("dontoptimize");
+
+  // Those options are unsupported and are treated as compilation errors.
+  // Just ignoring them would produce outputs incompatible with user expectations.
+  private static final List<String> unsupportedFlagOptions = ImmutableList
+      .of("skipnonpubliclibraryclasses");
+
+  public ProguardConfigurationParser(DexItemFactory dexItemFactory) {
+    this.dexItemFactory = dexItemFactory;
+    configurationBuilder = ProguardConfiguration.builder(dexItemFactory);
+  }
+
+  public ProguardConfiguration getConfig() {
+    return configurationBuilder.build();
+  }
+
+  public void parse(Path path) throws ProguardRuleParserException, IOException {
+    parse(Collections.singletonList(path));
+  }
+
+  public void parse(List<Path> pathList) throws ProguardRuleParserException, IOException {
+    for (Path path : pathList) {
+      new ProguardFileParser(path).parse();
+    }
+  }
+
+  private class ProguardFileParser {
+
+    private final Path path;
+    private final String contents;
+    private int position = 0;
+    private Path baseDirectory;
+
+    public ProguardFileParser(Path path) throws IOException {
+      this.path = path;
+      contents = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
+      baseDirectory = path.getParent();
+      if (baseDirectory == null) {
+        // path parent can be null only if it's root dir or if its a one element path relative to
+        // current directory.
+        baseDirectory = Paths.get(".");
+      }
+    }
+
+    public void parse() throws ProguardRuleParserException {
+      do {
+        skipWhitespace();
+      } while (parseOption());
+    }
+
+    private boolean parseOption() throws ProguardRuleParserException {
+      if (eof()) {
+        return false;
+      }
+      if (acceptArobaseInclude()) {
+        return true;
+      }
+      expectChar('-');
+      String option;
+      if (Iterables.any(ignoredSingleArgOptions, this::skipOptionWithSingleArg)
+          || Iterables.any(ignoredFlagOptions, this::skipFlag)
+          || Iterables.any(ignoredClassDescriptorOptions, this::skipOptionWithClassSpec)
+          || parseOptimizationOption()) {
+        // Intentionally left empty.
+      } else if (
+             (option = Iterables.find(warnedSingleArgOptions,
+                 this::skipOptionWithSingleArg, null)) != null
+          || (option = Iterables.find(warnedFlagOptions, this::skipFlag, null)) != null) {
+        System.out.println("WARNING: Ignoring option: -" + option);
+      } else if ((option = Iterables.find(unsupportedFlagOptions, this::skipFlag, null)) != null) {
+        throw parseError("Unsupported option: -" + option);
+      } else if (acceptString("keepattributes")) {
+        parseKeepAttributes();
+      } else if (acceptString("keeppackagenames")) {
+        ProguardKeepRule rule = parseKeepPackageNamesRule();
+        configurationBuilder.addRule(rule);
+      } else if (acceptString("checkdiscard")) {
+        ProguardKeepRule rule = parseCheckDiscardRule();
+        configurationBuilder.addRule(rule);
+      } else if (acceptString("keep")) {
+        ProguardKeepRule rule = parseKeepRule();
+        configurationBuilder.addRule(rule);
+      } else if (acceptString("whyareyoukeeping")) {
+        ProguardKeepRule rule = parseWhyAreYouKeepingRule();
+        configurationBuilder.addRule(rule);
+      } else if (acceptString("dontobfuscate")) {
+        configurationBuilder.setObfuscating(false);
+      } else if (acceptString("dontshrink")) {
+        configurationBuilder.setShrinking(false);
+      } else if (acceptString("verbose")) {
+        configurationBuilder.setVerbose(true);
+      } else if (acceptString("ignorewarnings")) {
+        configurationBuilder.setIgnoreWarnings(true);
+      } else if (acceptString("dontwarn")) {
+        do {
+          ProguardTypeMatcher pattern = ProguardTypeMatcher.create(parseClassName(),
+              ClassOrType.CLASS, dexItemFactory);
+          configurationBuilder.addDontWarnPattern(pattern);
+        } while (acceptChar(','));
+      } else if (acceptString("repackageclasses")) {
+        skipWhitespace();
+        if (acceptChar('\'')) {
+          configurationBuilder.setPackagePrefix(parsePackageNameOrEmptyString());
+          expectChar('\'');
+        } else {
+          configurationBuilder.setPackagePrefix("");
+        }
+      } else if (acceptString("allowaccessmodification")) {
+        configurationBuilder.setAllowAccessModification(true);
+      } else if (acceptString("printmapping")) {
+        configurationBuilder.setPrintMapping(true);
+        skipWhitespace();
+        if (!eof() && peekChar() != '-') {
+          configurationBuilder.setPrintMappingOutput(parseFileName());
+        }
+      } else if (acceptString("assumenosideeffects")) {
+        ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule();
+        configurationBuilder.addRule(rule);
+      } else if (acceptString("assumevalues")) {
+        ProguardAssumeValuesRule rule = parseAssumeValuesRule();
+        configurationBuilder.addRule(rule);
+      } else if (acceptString("include")) {
+        skipWhitespace();
+        parseInclude();
+      } else if (acceptString("basedirectory")) {
+        skipWhitespace();
+        baseDirectory = parseFileName();
+      } else if (acceptString("injars")) {
+        configurationBuilder.addInjars(parseClassPath());
+      } else if (acceptString("libraryjars")) {
+        configurationBuilder.addLibraryJars(parseClassPath());
+      } else if (acceptString("printseeds")) {
+        configurationBuilder.setPrintSeed(true);
+        skipWhitespace();
+        if (!eof() && peekChar() != '-') {
+          configurationBuilder.setSeedFile(parseFileName());
+        }
+      } else if (acceptString("obfuscationdictionary")) {
+        configurationBuilder.setObfuscationDictionary(parseFileName());
+      } else if (acceptString("classobfuscationdictionary")) {
+        configurationBuilder.setClassObfuscationDictionary(parseFileName());
+      } else if (acceptString("packageobfuscationdictionary")) {
+        configurationBuilder.setPackageObfuscationDictionary(parseFileName());
+      } else {
+        throw parseError("Unknown option");
+      }
+      return true;
+    }
+
+    private void parseInclude() throws ProguardRuleParserException {
+      Path included = parseFileName();
+      try {
+        new ProguardFileParser(included).parse();
+      } catch (FileNotFoundException | NoSuchFileException e) {
+        throw parseError("Included file '" + included.toString() + "' not found", e);
+      } catch (IOException e) {
+        throw parseError("Failed to read included file '" + included.toString() + "'", e);
+      }
+    }
+
+    private boolean acceptArobaseInclude() throws ProguardRuleParserException {
+      if (remainingChars() < 2) {
+        return false;
+      }
+      if (!acceptChar('@')) {
+        return false;
+      }
+      parseInclude();
+      return true;
+    }
+
+    private void parseKeepAttributes() throws ProguardRuleParserException {
+      String attributesPattern = acceptPatternList();
+      if (attributesPattern == null) {
+        throw parseError("Expected attribute pattern list");
+      }
+      configurationBuilder.addAttributeRemovalPattern(attributesPattern);
+    }
+
+    private boolean skipFlag(String name) {
+      if (acceptString(name)) {
+        if (Log.ENABLED) {
+          Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` flag", name);
+        }
+        return true;
+      }
+      return false;
+    }
+
+    private boolean skipOptionWithSingleArg(String name) {
+      if (acceptString(name)) {
+        if (Log.ENABLED) {
+          Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` option", name);
+        }
+        skipSingleArgument();
+        return true;
+      }
+      return false;
+    }
+
+    private boolean skipOptionWithClassSpec(String name) {
+      if (acceptString(name)) {
+        if (Log.ENABLED) {
+          Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` option", name);
+        }
+        try {
+          ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+          parseClassFlagsAndAnnotations(keepRuleBuilder);
+          keepRuleBuilder.setClassType(parseClassType());
+          keepRuleBuilder.setClassNames(parseClassNames());
+          parseInheritance(keepRuleBuilder);
+          parseMemberRules(keepRuleBuilder, true);
+          return true;
+        } catch (ProguardRuleParserException e) {
+          System.out.println(e);
+          return false;
+        }
+      }
+      return false;
+
+    }
+
+    private boolean parseOptimizationOption() {
+      if (!acceptString("optimizations")) {
+        return false;
+      }
+      skipWhitespace();
+      do {
+        skipOptimizationName();
+        skipWhitespace();
+      } while (acceptChar(','));
+      return true;
+    }
+
+    private void skipOptimizationName() {
+      if (acceptChar('!')) {
+        skipWhitespace();
+      }
+      for (char next = peekChar();
+          Character.isAlphabetic(next) || next == '/' || next == '*';
+          next = peekChar()) {
+        readChar();
+      }
+    }
+
+    private void skipSingleArgument() {
+      skipWhitespace();
+      while (!eof() && !Character.isWhitespace(peekChar())) {
+        readChar();
+      }
+    }
+
+    private ProguardKeepRule parseKeepRule()
+        throws ProguardRuleParserException {
+      ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+      parseRuleTypeAndModifiers(keepRuleBuilder);
+      parseClassSpec(keepRuleBuilder, false);
+      return keepRuleBuilder.build();
+    }
+
+    private ProguardKeepRule parseWhyAreYouKeepingRule()
+        throws ProguardRuleParserException {
+      ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+      keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
+      keepRuleBuilder.getModifiersBuilder().whyAreYouKeeping = true;
+      keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+      parseClassSpec(keepRuleBuilder, false);
+      return keepRuleBuilder.build();
+    }
+
+    private ProguardKeepRule parseKeepPackageNamesRule()
+        throws ProguardRuleParserException {
+      ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+      keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
+      keepRuleBuilder.getModifiersBuilder().keepPackageNames = true;
+      keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+      keepRuleBuilder.setClassNames(parseClassNames());
+      return keepRuleBuilder.build();
+    }
+
+    private ProguardKeepRule parseCheckDiscardRule()
+        throws ProguardRuleParserException {
+      ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+      keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
+      keepRuleBuilder.getModifiersBuilder().checkDiscarded = true;
+      parseClassSpec(keepRuleBuilder, false);
+      keepRuleBuilder.setType(keepRuleBuilder.getMemberRules().isEmpty() ? ProguardKeepRuleType.KEEP
+          : ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
+      return keepRuleBuilder.build();
+    }
+
+    private void parseClassSpec(
+        ProguardConfigurationRule.Builder builder, boolean allowValueSpecification)
+        throws ProguardRuleParserException {
+      parseClassFlagsAndAnnotations(builder);
+      builder.setClassType(parseClassType());
+      builder.setClassNames(parseClassNames());
+      parseInheritance(builder);
+      parseMemberRules(builder, allowValueSpecification);
+    }
+
+    private void parseRuleTypeAndModifiers(ProguardKeepRule.Builder builder)
+        throws ProguardRuleParserException {
+      if (acceptString("names")) {
+        builder.setType(ProguardKeepRuleType.KEEP);
+        builder.getModifiersBuilder().allowsShrinking = true;
+      } else if (acceptString("class")) {
+        if (acceptString("members")) {
+          builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
+        } else if (acceptString("eswithmembers")) {
+          builder.setType(ProguardKeepRuleType.KEEP_CLASSES_WITH_MEMBERS);
+        } else if (acceptString("membernames")) {
+          builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
+          builder.getModifiersBuilder().allowsShrinking = true;
+        } else if (acceptString("eswithmembernames")) {
+          builder.setType(ProguardKeepRuleType.KEEP_CLASSES_WITH_MEMBERS);
+          builder.getModifiersBuilder().allowsShrinking = true;
+        }
+      } else {
+        builder.setType(ProguardKeepRuleType.KEEP);
+      }
+      parseRuleModifiers(builder);
+    }
+
+    private void parseRuleModifiers(ProguardKeepRule.Builder builder) {
+      while (acceptChar(',')) {
+        if (acceptString("allow")) {
+          if (acceptString("shrinking")) {
+            builder.getModifiersBuilder().allowsShrinking = true;
+          } else if (acceptString("optimization")) {
+            builder.getModifiersBuilder().allowsOptimization = true;
+          } else if (acceptString("obfuscation")) {
+            builder.getModifiersBuilder().allowsObfuscation = true;
+          }
+        } else if (acceptString("includedescriptorclasses")) {
+          builder.getModifiersBuilder().includeDescriptorClasses = true;
+        }
+      }
+    }
+
+    private ProguardTypeMatcher parseAnnotation() throws ProguardRuleParserException {
+      skipWhitespace();
+      int startPosition = position;
+      if (acceptChar('@')) {
+        String className = parseClassName();
+        if (className.equals("interface")) {
+          // Not an annotation after all but a class type. Move position back to start
+          // so this can be dealt with as a class type instead.
+          position = startPosition;
+          return null;
+        }
+        return ProguardTypeMatcher.create(className, ClassOrType.CLASS, dexItemFactory);
+      }
+      return null;
+    }
+
+    private boolean parseNegation() {
+      skipWhitespace();
+      return acceptChar('!');
+    }
+
+    private void parseClassFlagsAndAnnotations(ProguardClassSpecification.Builder builder)
+        throws ProguardRuleParserException {
+      while (true) {
+        skipWhitespace();
+        ProguardTypeMatcher annotation = parseAnnotation();
+        if (annotation != null) {
+          // TODO(ager): Should we only allow one annotation? It looks that way from the
+          // proguard keep rule description, but that seems like a strange restriction?
+          assert builder.getClassAnnotation() == null;
+          builder.setClassAnnotation(annotation);
+        } else {
+          DexAccessFlags flags =
+              parseNegation() ? builder.getNegatedClassAccessFlags() :
+                builder.getClassAccessFlags();
+          skipWhitespace();
+          if (acceptString("public")) {
+            flags.setPublic();
+          } else if (acceptString("final")) {
+            flags.setFinal();
+          } else if (acceptString("abstract")) {
+            flags.setAbstract();
+          } else {
+            break;
+          }
+        }
+      }
+    }
+
+    private ProguardClassType parseClassType() throws ProguardRuleParserException {
+      skipWhitespace();
+      if (acceptString("interface")) {
+        return ProguardClassType.INTERFACE;
+      } else if (acceptString("@interface")) {
+        return ProguardClassType.ANNOTATION_INTERFACE;
+      } else if (acceptString("class")) {
+        return ProguardClassType.CLASS;
+      } else if (acceptString("enum")) {
+        return ProguardClassType.ENUM;
+      } else {
+        throw parseError("Expected interface|class|enum");
+      }
+    }
+
+    private void parseInheritance(ProguardClassSpecification.Builder classSpecificationBuilder)
+        throws ProguardRuleParserException {
+      skipWhitespace();
+      if (acceptString("implements")) {
+        classSpecificationBuilder.setInheritanceIsExtends(false);
+      } else if (acceptString("extends")) {
+        classSpecificationBuilder.setInheritanceIsExtends(true);
+      } else {
+        return;
+      }
+      classSpecificationBuilder.setInheritanceAnnotation(parseAnnotation());
+      classSpecificationBuilder.setInheritanceClassName(ProguardTypeMatcher.create(parseClassName(),
+          ClassOrType.CLASS, dexItemFactory));
+    }
+
+    private void parseMemberRules(ProguardClassSpecification.Builder classSpecificationBuilder,
+        boolean allowValueSpecification)
+        throws ProguardRuleParserException {
+      skipWhitespace();
+      if (!eof() && acceptChar('{')) {
+        ProguardMemberRule rule = null;
+        while ((rule = parseMemberRule(allowValueSpecification)) != null) {
+          classSpecificationBuilder.getMemberRules().add(rule);
+        }
+        skipWhitespace();
+        expectChar('}');
+      } else {
+        // If there are no member rules, a default rule for the parameterless constructor
+        // applies. So we add that here.
+        ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
+        defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+        defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
+        defaultRuleBuilder.setArguments(Collections.emptyList());
+        classSpecificationBuilder.getMemberRules().add(defaultRuleBuilder.build());
+      }
+    }
+
+    private ProguardMemberRule parseMemberRule(boolean allowValueSpecification)
+        throws ProguardRuleParserException {
+      ProguardMemberRule.Builder ruleBuilder = ProguardMemberRule.builder();
+      skipWhitespace();
+      ruleBuilder.setAnnotation(parseAnnotation());
+      parseMemberAccessFlags(ruleBuilder);
+      parseMemberPattern(ruleBuilder, allowValueSpecification);
+      return ruleBuilder.isValid() ? ruleBuilder.build() : null;
+    }
+
+    private void parseMemberAccessFlags(ProguardMemberRule.Builder ruleBuilder) {
+      boolean found = true;
+      while (found && !eof()) {
+        found = false;
+        DexAccessFlags flags =
+            parseNegation() ? ruleBuilder.getNegatedAccessFlags() : ruleBuilder.getAccessFlags();
+        skipWhitespace();
+        switch (peekChar()) {
+          case 'a':
+            if (found = acceptString("abstract")) {
+              flags.setAbstract();
+            }
+            break;
+          case 'f':
+            if (found = acceptString("final")) {
+              flags.setFinal();
+            }
+            break;
+          case 'n':
+            if (found = acceptString("native")) {
+              flags.setNative();
+            }
+            break;
+          case 'p':
+            if (found = acceptString("public")) {
+              flags.setPublic();
+            } else if (found = acceptString("private")) {
+              flags.setPrivate();
+            } else if (found = acceptString("protected")) {
+              flags.setProtected();
+            }
+            break;
+          case 's':
+            if (found = acceptString("synchronized")) {
+              flags.setSynchronized();
+            } else if (found = acceptString("static")) {
+              flags.setStatic();
+            } else if (found = acceptString("strictfp")) {
+              flags.setStrict();
+            }
+            break;
+          case 't':
+            if (found = acceptString("transient")) {
+              flags.setTransient();
+            }
+            break;
+          case 'v':
+            if (found = acceptString("volatile")) {
+              flags.setVolatile();
+            }
+            break;
+        }
+      }
+    }
+
+    private void parseMemberPattern(
+        ProguardMemberRule.Builder ruleBuilder, boolean allowValueSpecification)
+        throws ProguardRuleParserException {
+      skipWhitespace();
+      if (acceptString("<methods>")) {
+        ruleBuilder.setRuleType(ProguardMemberType.ALL_METHODS);
+      } else if (acceptString("<fields>")) {
+        ruleBuilder.setRuleType(ProguardMemberType.ALL_FIELDS);
+      } else if (acceptString("<init>")) {
+        ruleBuilder.setRuleType(ProguardMemberType.INIT);
+        ruleBuilder.setName("<init>");
+        ruleBuilder.setArguments(parseArgumentList());
+      } else {
+        String first = acceptClassName();
+        if (first != null) {
+          skipWhitespace();
+          if (first.equals("*") && hasNextChar(';')) {
+            ruleBuilder.setRuleType(ProguardMemberType.ALL);
+          } else {
+            if (hasNextChar('(')) {
+              ruleBuilder.setRuleType(ProguardMemberType.CONSTRUCTOR);
+              ruleBuilder.setName(first);
+              ruleBuilder.setArguments(parseArgumentList());
+            } else {
+              String second = acceptClassName();
+              if (second != null) {
+                skipWhitespace();
+                if (hasNextChar('(')) {
+                  ruleBuilder.setRuleType(ProguardMemberType.METHOD);
+                  ruleBuilder.setName(second);
+                  ruleBuilder
+                      .setTypeMatcher(
+                          ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory));
+                  ruleBuilder.setArguments(parseArgumentList());
+                } else {
+                  ruleBuilder.setRuleType(ProguardMemberType.FIELD);
+                  ruleBuilder.setName(second);
+                  ruleBuilder
+                      .setTypeMatcher(
+                          ProguardTypeMatcher.create(first, ClassOrType.TYPE, dexItemFactory));
+                }
+                skipWhitespace();
+                // Parse "return ..." if present.
+                if (acceptString("return")) {
+                  skipWhitespace();
+                  if (acceptString("true")) {
+                    ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(true));
+                  } else if (acceptString("false")) {
+                    ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(false));
+                  } else {
+                    String qualifiedFieldName = acceptFieldName();
+                    if (qualifiedFieldName != null) {
+                      if (ruleBuilder.getTypeMatcher() instanceof MatchSpecificType) {
+                        int lastDotIndex = qualifiedFieldName.lastIndexOf(".");
+                        DexType fieldType = ((MatchSpecificType) ruleBuilder.getTypeMatcher()).type;
+                        DexType fieldClass =
+                            dexItemFactory.createType(
+                                DescriptorUtils.javaTypeToDescriptor(
+                                    qualifiedFieldName.substring(0, lastDotIndex)));
+                        DexString fieldName =
+                            dexItemFactory.createString(
+                                qualifiedFieldName.substring(lastDotIndex + 1));
+                        DexField field = dexItemFactory
+                            .createField(fieldClass, fieldType, fieldName);
+                        ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(field));
+                      } else {
+                        throw parseError("Expected specific type");
+                      }
+                    } else {
+                      Integer min = acceptInteger();
+                      Integer max = min;
+                      if (min == null) {
+                        throw parseError("Expected integer value");
+                      }
+                      skipWhitespace();
+                      if (acceptString("..")) {
+                        max = acceptInteger();
+                        if (max == null) {
+                          throw parseError("Expected integer value");
+                        }
+                      }
+                      if (!allowValueSpecification) {
+                        throw parseError("Unexpected value specification");
+                      }
+                      ruleBuilder.setReturnValue(
+                          new ProguardMemberRuleReturnValue(new LongInterval(min, max)));
+                    }
+                  }
+                }
+              } else {
+                throw parseError("Expected field or method name");
+              }
+            }
+          }
+        }
+      }
+      // If we found a member pattern eat the terminating ';'.
+      if (ruleBuilder.isValid()) {
+        skipWhitespace();
+        expectChar(';');
+      }
+    }
+
+    private List<ProguardTypeMatcher> parseArgumentList() throws ProguardRuleParserException {
+      List<ProguardTypeMatcher> arguments = new ArrayList<>();
+      skipWhitespace();
+      expectChar('(');
+      skipWhitespace();
+      if (acceptChar(')')) {
+        return arguments;
+      }
+      if (acceptString("...")) {
+        arguments
+            .add(ProguardTypeMatcher.create("...", ClassOrType.TYPE, dexItemFactory));
+      } else {
+        for (String name = parseClassName(); name != null; name =
+            acceptChar(',') ? parseClassName() : null) {
+          arguments
+              .add(ProguardTypeMatcher.create(name, ClassOrType.TYPE, dexItemFactory));
+          skipWhitespace();
+        }
+      }
+      skipWhitespace();
+      expectChar(')');
+      return arguments;
+    }
+
+    private Path parseFileName() throws ProguardRuleParserException {
+      skipWhitespace();
+      int start = position;
+      int end = position;
+      while (!eof(end)) {
+        char current = contents.charAt(end);
+        if (current != File.pathSeparatorChar && !Character.isWhitespace(current)) {
+          end++;
+        } else {
+          break;
+        }
+      }
+      if (start == end) {
+        throw parseError("File name expected");
+      }
+      position = end;
+      return baseDirectory.resolve(contents.substring(start, end));
+    }
+
+    private List<Path> parseClassPath() throws ProguardRuleParserException {
+      List<Path> classPath = new ArrayList<>();
+      skipWhitespace();
+      Path file = parseFileName();
+      classPath.add(file);
+      while (acceptChar(File.pathSeparatorChar)) {
+        file = parseFileName();
+        classPath.add(file);
+      }
+      return classPath;
+    }
+
+    private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule()
+        throws ProguardRuleParserException {
+      ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder();
+      parseClassSpec(builder, true);
+      return builder.build();
+    }
+
+    private ProguardAssumeValuesRule parseAssumeValuesRule() throws ProguardRuleParserException {
+      ProguardAssumeValuesRule.Builder builder = ProguardAssumeValuesRule.builder();
+      parseClassSpec(builder, true);
+      return builder.build();
+    }
+
+    private void skipWhitespace() {
+      while (!eof() && Character.isWhitespace(contents.charAt(position))) {
+        position++;
+      }
+      skipComment();
+    }
+
+    private void skipComment() {
+      if (eof()) {
+        return;
+      }
+      if (peekChar() == '#') {
+        while (!eof() && readChar() != '\n') {
+          ;
+        }
+        skipWhitespace();
+      }
+    }
+
+    private boolean eof() {
+      return position == contents.length();
+    }
+
+    private boolean eof(int position) {
+      return position == contents.length();
+    }
+
+    private boolean hasNextChar(char c) {
+      if (eof()) {
+        return false;
+      }
+      return peekChar() == c;
+    }
+
+    private boolean acceptChar(char c) {
+      if (hasNextChar(c)) {
+        position++;
+        return true;
+      }
+      return false;
+    }
+
+    private char peekChar() {
+      return contents.charAt(position);
+    }
+
+    private char readChar() {
+      return contents.charAt(position++);
+    }
+
+    private int remainingChars() {
+      return contents.length() - position;
+    }
+
+    private void expectChar(char c) throws ProguardRuleParserException {
+      if (eof() || readChar() != c) {
+        throw parseError("Expected char '" + c + "'");
+      }
+    }
+
+    private void expectString(String expected) throws ProguardRuleParserException {
+      if (remainingChars() < expected.length()) {
+        throw parseError("Expected string '" + expected + "'");
+      }
+      for (int i = 0; i < expected.length(); i++) {
+        if (expected.charAt(i) != readChar()) {
+          throw parseError("Expected string '" + expected + "'");
+        }
+      }
+    }
+
+    private boolean acceptString(String expected) {
+      if (remainingChars() < expected.length()) {
+        return false;
+      }
+      for (int i = 0; i < expected.length(); i++) {
+        if (expected.charAt(i) != contents.charAt(position + i)) {
+          return false;
+        }
+      }
+      position += expected.length();
+      return true;
+    }
+
+    private Integer acceptInteger() {
+      skipWhitespace();
+      int start = position;
+      int end = position;
+      while (!eof(end)) {
+        char current = contents.charAt(end);
+        if (Character.isDigit(current)) {
+          end++;
+        } else {
+          break;
+        }
+      }
+      if (start == end) {
+        return null;
+      }
+      position = end;
+      return Integer.parseInt(contents.substring(start, end));
+    }
+
+    private String acceptClassName() {
+      skipWhitespace();
+      int start = position;
+      int end = position;
+      while (!eof(end)) {
+        char current = contents.charAt(end);
+        if (Character.isJavaIdentifierPart(current) ||
+            current == '.' ||
+            current == '*' ||
+            current == '?' ||
+            current == '%' ||
+            current == '[' ||
+            current == ']') {
+          end++;
+        } else {
+          break;
+        }
+      }
+      if (start == end) {
+        return null;
+      }
+      position = end;
+      return contents.substring(start, end);
+    }
+
+    private String acceptFieldName() {
+      skipWhitespace();
+      int start = position;
+      int end = position;
+      while (!eof(end)) {
+        char current = contents.charAt(end);
+        if ((start == end && Character.isJavaIdentifierStart(current)) ||
+            (start < end) && (Character.isJavaIdentifierPart(current) || current == '.')) {
+          end++;
+        } else {
+          break;
+        }
+      }
+      if (start == end) {
+        return null;
+      }
+      position = end;
+      return contents.substring(start, end);
+    }
+
+    private String acceptPatternList() {
+      skipWhitespace();
+      int start = position;
+      int end = position;
+      while (!eof(end)) {
+        char current = contents.charAt(end);
+        if (Character.isJavaIdentifierPart(current) ||
+            current == '!' ||
+            current == '*' ||
+            current == ',') {
+          end++;
+        } else {
+          break;
+        }
+      }
+      if (start == end) {
+        return null;
+      }
+      position = end;
+      return contents.substring(start, end);
+    }
+
+    private void checkNotNegatedPattern() throws ProguardRuleParserException {
+      skipWhitespace();
+      if (acceptChar('!')) {
+        throw parseError("Negated filters are not supported");
+      }
+    }
+
+    private List<ProguardTypeMatcher> parseClassNames() throws ProguardRuleParserException {
+      List<ProguardTypeMatcher> classNames = new ArrayList<>();
+      checkNotNegatedPattern();
+      classNames
+          .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+      skipWhitespace();
+      while (acceptChar(',')) {
+        checkNotNegatedPattern();
+        classNames
+            .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+        skipWhitespace();
+      }
+      return classNames;
+    }
+
+    private String parsePackageNameOrEmptyString() {
+      String name = acceptClassName();
+      return name == null ? "" : name;
+    }
+
+    private String parseClassName() throws ProguardRuleParserException {
+      String name = acceptClassName();
+      if (name == null) {
+        throw parseError("Class name expected");
+      }
+      return name;
+    }
+
+    private String snippetForPosition() {
+      // TODO(ager): really should deal with \r as well to get column right.
+      String[] lines = contents.split("\n", -1);  // -1 to get trailing empty lines represented.
+      int remaining = position;
+      for (int lineNumber = 0; lineNumber < lines.length; lineNumber++) {
+        String line = lines[lineNumber];
+        if (remaining <= line.length() || lineNumber == lines.length - 1) {
+          return path.toString() + ":" + (lineNumber + 1) + ":" + remaining + "\n" + line;
+        }
+        remaining -= (line.length() + 1); // include newline.
+      }
+      return path.toString();
+    }
+
+    private ProguardRuleParserException parseError(String message) {
+      return new ProguardRuleParserException(message, snippetForPosition());
+    }
+
+    private ProguardRuleParserException parseError(String message, Throwable cause) {
+      return new ProguardRuleParserException(message, snippetForPosition(), cause);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
new file mode 100644
index 0000000..deb11a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import java.util.Set;
+
+public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
+  ProguardConfigurationRule(
+      ProguardTypeMatcher classAnnotation,
+      DexAccessFlags classAccessFlags,
+      DexAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      List<ProguardTypeMatcher> classNames,
+      ProguardTypeMatcher inheritanceAnnotation,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      Set<ProguardMemberRule> memberRules) {
+    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+  }
+
+  abstract String typeString();
+
+  String modifierString() {
+    return null;
+  }
+
+  public boolean applyToLibraryClasses() {
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append(typeString());
+    StringUtils.appendNonEmpty(builder, ",", modifierString(), null);
+    builder.append(' ');
+    builder.append(super.toString());
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
new file mode 100644
index 0000000..fac7d99
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -0,0 +1,129 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardKeepRule extends ProguardConfigurationRule {
+
+  public static class Builder extends ProguardClassSpecification.Builder {
+
+    private ProguardKeepRuleType type;
+    private final ProguardKeepRuleModifiers.Builder modifiersBuilder
+        = ProguardKeepRuleModifiers.builder();
+
+    private Builder() {}
+
+    public void setType(ProguardKeepRuleType type) {
+      this.type = type;
+    }
+
+    public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
+      return modifiersBuilder;
+    }
+
+    public ProguardKeepRule build() {
+      return new ProguardKeepRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
+          classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+          inheritanceIsExtends, memberRules, type, modifiersBuilder.build());
+    }
+  }
+
+  private final ProguardKeepRuleType type;
+  private final ProguardKeepRuleModifiers modifiers;
+
+  private ProguardKeepRule(
+      ProguardTypeMatcher classAnnotation,
+      DexAccessFlags classAccessFlags,
+      DexAccessFlags negatedClassAccessFlags,
+      boolean classTypeNegated,
+      ProguardClassType classType,
+      List<ProguardTypeMatcher> classNames,
+      ProguardTypeMatcher inheritanceAnnotation,
+      ProguardTypeMatcher inheritanceClassName,
+      boolean inheritanceIsExtends,
+      Set<ProguardMemberRule> memberRules,
+      ProguardKeepRuleType type,
+      ProguardKeepRuleModifiers modifiers) {
+    super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+        classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+    this.type = type;
+    this.modifiers = modifiers;
+  }
+
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public ProguardKeepRuleType getType() {
+    return type;
+  }
+
+  public ProguardKeepRuleModifiers getModifiers() {
+    return modifiers;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ProguardKeepRule)) {
+      return false;
+    }
+    ProguardKeepRule that = (ProguardKeepRule) o;
+
+    if (type != that.type) {
+      return false;
+    }
+    if (!modifiers.equals(that.modifiers)) {
+      return false;
+    }
+    return super.equals(that);
+  }
+
+  @Override
+  public int hashCode() {
+    // Used multiplier 3 to avoid too much overflow when computing hashCode.
+    int result = type.hashCode();
+    result = 3 * result + modifiers.hashCode();
+    result = 3 * result + super.hashCode();
+    return result;
+  }
+
+  static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+    if (item == null) {
+      return;
+    }
+    String text = item.toString();
+    if (!text.isEmpty()) {
+      if (pre != null) {
+        builder.append(pre);
+      }
+      builder.append(text);
+      if (post != null) {
+        builder.append(post);
+      }
+    }
+  }
+
+  @Override
+  String typeString() {
+    return type.toString();
+  }
+
+  @Override
+  String modifierString() {
+    return modifiers.toString();
+  }
+
+  public static ProguardKeepRule defaultKeepAllRule() {
+    ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
+    builder.matchAllSpecification();
+    builder.setType(ProguardKeepRuleType.KEEP);
+    return builder.build();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
new file mode 100644
index 0000000..e2f53b5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public class ProguardKeepRuleModifiers {
+  public static class Builder {
+    public boolean allowsShrinking = false;
+    public boolean allowsOptimization = false;
+    public boolean allowsObfuscation = false;
+    public boolean whyAreYouKeeping = false;
+    public boolean includeDescriptorClasses = false;
+    public boolean keepPackageNames = false;
+    public boolean checkDiscarded = false;
+
+    void setFlagsToHaveNoEffect() {
+      allowsShrinking = true;
+      allowsOptimization = true;
+      allowsObfuscation = true;
+      whyAreYouKeeping = false;
+      includeDescriptorClasses = false;
+      keepPackageNames = false;
+    }
+
+    private Builder() {}
+
+    ProguardKeepRuleModifiers build() {
+      return new ProguardKeepRuleModifiers(allowsShrinking, allowsOptimization, allowsObfuscation,
+          whyAreYouKeeping, includeDescriptorClasses, keepPackageNames, checkDiscarded);
+    }
+  }
+
+  public final boolean allowsShrinking;
+  public final boolean allowsOptimization;
+  public final boolean allowsObfuscation;
+  public final boolean whyAreYouKeeping;
+  public final boolean includeDescriptorClasses;
+  public final boolean keepPackageNames;
+  public final boolean checkDiscarded;
+
+  private ProguardKeepRuleModifiers(
+      boolean allowsShrinking,
+      boolean allowsOptimization,
+      boolean allowsObfuscation,
+      boolean whyAreYouKeeping,
+      boolean includeDescriptorClasses,
+      boolean keepPackageNames,
+      boolean checkDiscarded) {
+    this.allowsShrinking = allowsShrinking;
+    this.allowsOptimization = allowsOptimization;
+    this.allowsObfuscation = allowsObfuscation;
+    this.whyAreYouKeeping = whyAreYouKeeping;
+    this.includeDescriptorClasses = includeDescriptorClasses;
+    this.keepPackageNames = keepPackageNames;
+    this.checkDiscarded = checkDiscarded;
+  }
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ProguardKeepRuleModifiers)) {
+      return false;
+    }
+    ProguardKeepRuleModifiers that = (ProguardKeepRuleModifiers) o;
+
+    return allowsShrinking == that.allowsShrinking
+        && allowsOptimization == that.allowsOptimization
+        && allowsObfuscation == that.allowsObfuscation
+        && includeDescriptorClasses == that.includeDescriptorClasses
+        && keepPackageNames == that.keepPackageNames;
+  }
+
+  @Override
+  public int hashCode() {
+    return (allowsShrinking ? 1 : 0)
+        | (allowsOptimization ? 2 : 0)
+        | (allowsObfuscation ? 4 : 0)
+        | (whyAreYouKeeping ? 8 : 0)
+        | (includeDescriptorClasses ? 16 : 0)
+        | (keepPackageNames ? 32 : 0);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    appendWithComma(builder, allowsObfuscation, "allowobfuscation");
+    appendWithComma(builder, allowsShrinking, "allowshrinking");
+    appendWithComma(builder, allowsOptimization, "allowoptimization");
+    appendWithComma(builder, whyAreYouKeeping, "whyareyoukeeping");
+    appendWithComma(builder, includeDescriptorClasses, "includedescriptorclasses");
+    appendWithComma(builder, keepPackageNames, "keeppackagenames");
+    return builder.toString();
+  }
+
+  private void appendWithComma(StringBuilder builder, boolean predicate,
+      String text) {
+    if (!predicate) {
+      return;
+    }
+    if (builder.length() != 0) {
+      builder.append(',');
+    }
+    builder.append(text);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
new file mode 100644
index 0000000..4af299c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleType.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public enum ProguardKeepRuleType {
+  KEEP,
+  KEEP_CLASS_MEMBERS,
+  KEEP_CLASSES_WITH_MEMBERS;
+
+  @Override
+  public String toString() {
+    switch (this) {
+      case KEEP:
+        return "keep";
+      case KEEP_CLASS_MEMBERS:
+        return "keepclassmembers";
+      case KEEP_CLASSES_WITH_MEMBERS:
+        return "keepclasseswithmembers";
+      default:
+        throw new Unreachable("Unknown ProguardKeepRuleType.");
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
new file mode 100644
index 0000000..08c79aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -0,0 +1,351 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class ProguardMemberRule {
+
+  public static class Builder {
+
+    private ProguardTypeMatcher annotation;
+    private DexAccessFlags accessFlags = new DexAccessFlags(0);
+    private DexAccessFlags negatedAccessFlags = new DexAccessFlags(0);
+    private ProguardMemberType ruleType;
+    private ProguardNameMatcher name;
+    private ProguardTypeMatcher type;
+    private List<ProguardTypeMatcher> arguments;
+    private ProguardMemberRuleReturnValue returnValue;
+
+    private Builder() {}
+
+    public void setAnnotation(ProguardTypeMatcher annotation) {
+      this.annotation = annotation;
+    }
+
+    public DexAccessFlags getAccessFlags() {
+      return accessFlags;
+    }
+
+    public void setAccessFlags(DexAccessFlags flags) {
+      accessFlags = flags;
+    }
+
+    public DexAccessFlags getNegatedAccessFlags() {
+      return negatedAccessFlags;
+    }
+
+    public void setNegatedAccessFlags(DexAccessFlags flags) {
+      negatedAccessFlags = flags;
+    }
+
+    public void setRuleType(ProguardMemberType ruleType) {
+      this.ruleType = ruleType;
+    }
+
+    public void setName(String name) {
+      this.name = ProguardNameMatcher.create(name);
+    }
+
+    public ProguardTypeMatcher getTypeMatcher() {
+      return type;
+    }
+
+    public void setTypeMatcher(ProguardTypeMatcher type) {
+      this.type = type;
+    }
+
+    public void setArguments(List<ProguardTypeMatcher> arguments) {
+      this.arguments = arguments;
+    }
+
+    public void setReturnValue(ProguardMemberRuleReturnValue value) {
+      returnValue = value;
+    }
+
+    public boolean isValid() {
+      return ruleType != null;
+    }
+
+    public ProguardMemberRule build() {
+      assert isValid();
+      return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, name,
+          type, arguments, returnValue);
+    }
+  }
+
+  private final ProguardTypeMatcher annotation;
+  private final DexAccessFlags accessFlags;
+  private final DexAccessFlags negatedAccessFlags;
+  private final ProguardMemberType ruleType;
+  private final ProguardNameMatcher name;
+  private final ProguardTypeMatcher type;
+  private final List<ProguardTypeMatcher> arguments;
+  private final ProguardMemberRuleReturnValue returnValue;
+
+  private ProguardMemberRule(
+      ProguardTypeMatcher annotation,
+      DexAccessFlags accessFlags,
+      DexAccessFlags negatedAccessFlags,
+      ProguardMemberType ruleType,
+      ProguardNameMatcher name,
+      ProguardTypeMatcher type,
+      List<ProguardTypeMatcher> arguments,
+      ProguardMemberRuleReturnValue returnValue) {
+    this.annotation = annotation;
+    this.accessFlags = accessFlags;
+    this.negatedAccessFlags = negatedAccessFlags;
+    this.ruleType = ruleType;
+    this.name = name;
+    this.type = type;
+    this.arguments = arguments != null ? ImmutableList.copyOf(arguments) : null;
+    this.returnValue = returnValue;
+  }
+
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public ProguardTypeMatcher getAnnotation() {
+    return annotation;
+  }
+
+  public DexAccessFlags getAccessFlags() {
+    return accessFlags;
+  }
+
+  public DexAccessFlags getNegatedAccessFlags() {
+    return negatedAccessFlags;
+  }
+
+  public ProguardMemberType getRuleType() {
+    return ruleType;
+  }
+
+  public ProguardNameMatcher getName() {
+    return name;
+  }
+
+  public ProguardTypeMatcher getType() {
+    return type;
+  }
+
+  public List<ProguardTypeMatcher> getArguments() {
+    return arguments;
+  }
+
+  public boolean hasReturnValue() {
+    return returnValue != null;
+  }
+
+  public ProguardMemberRuleReturnValue getReturnValue() {
+    return returnValue;
+  }
+
+  public ProguardTypeMatcher getTypeMatcher() {
+    return type;
+  }
+
+  public boolean matches(DexEncodedField field, RootSetBuilder builder) {
+    switch (getRuleType()) {
+      case ALL:
+      case ALL_FIELDS:
+        // Access flags check.
+        if (!field.accessFlags.containsAllOf(getAccessFlags()) ||
+            !field.accessFlags.containsNoneOf(getNegatedAccessFlags())) {
+          break;
+        }
+        // Annotations check.
+        return RootSetBuilder.containsAnnotation(annotation, field.annotations);
+      case FIELD:
+        // Name check.
+        String name = builder.lookupString(field.field.name);
+        if (!getName().matches(name)) {
+          break;
+        }
+        // Access flags check.
+        if (!(field.accessFlags.containsAllOf(getAccessFlags()) &&
+            field.accessFlags.containsNoneOf(getNegatedAccessFlags()))) {
+          break;
+        }
+        // Type check.
+        if (!this.type.matches(field.field.type)) {
+          break;
+        }
+        // Annotations check
+        if (!RootSetBuilder.containsAnnotation(annotation, field.annotations)) {
+          break;
+        }
+        return true;
+      case ALL_METHODS:
+      case INIT:
+      case CONSTRUCTOR:
+      case METHOD:
+        break;
+    }
+    return false;
+  }
+
+  public boolean matches(DexEncodedMethod method, RootSetBuilder builder) {
+    switch (getRuleType()) {
+      case ALL_METHODS:
+        if (method.accessFlags.isConstructor() && method.accessFlags.isStatic()) {
+          break;
+        }
+      case ALL:
+        // Access flags check.
+        if (!method.accessFlags.containsAllOf(getAccessFlags()) ||
+            !method.accessFlags.containsNoneOf(getNegatedAccessFlags())) {
+          break;
+        }
+        // Annotations check.
+        return RootSetBuilder.containsAnnotation(annotation, method.annotations);
+      case METHOD:
+        // Check return type.
+        if (!type.matches(method.method.proto.returnType)) {
+          break;
+        }
+        // Fall through for access flags, name and arguments.
+      case CONSTRUCTOR:
+      case INIT:
+        // Name check.
+        String name = builder.lookupString(method.method.name);
+        if (!getName().matches(name)) {
+          break;
+        }
+        // Access flags check.
+        if (!(method.accessFlags.containsAllOf(getAccessFlags()) &&
+            method.accessFlags.containsNoneOf(getNegatedAccessFlags()))) {
+          break;
+        }
+        // Annotations check.
+        if (!RootSetBuilder.containsAnnotation(annotation, method.annotations)) {
+          break;
+        }
+        // Parameter types check.
+        List<ProguardTypeMatcher> arguments = getArguments();
+        if (arguments.size() == 1 && arguments.get(0).isTripleDotPattern()) {
+          return true;
+        } else {
+          DexType[] parameters = method.method.proto.parameters.values;
+          if (parameters.length != arguments.size()) {
+            break;
+          }
+          int i = 0;
+          for (; i < parameters.length; i++) {
+            if (!arguments.get(i).matches(parameters[i])) {
+              break;
+            }
+          }
+          if (i == parameters.length) {
+            // All parameters matched.
+            return true;
+          }
+        }
+        break;
+      case ALL_FIELDS:
+      case FIELD:
+        break;
+    }
+    return false;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof ProguardMemberRule)) {
+      return false;
+    }
+
+    ProguardMemberRule that = (ProguardMemberRule) o;
+
+    if (annotation != null ? !annotation.equals(that.annotation) : that.annotation != null) {
+      return false;
+    }
+    if (!accessFlags.equals(that.accessFlags)) {
+      return false;
+    }
+    if (!negatedAccessFlags.equals(that.negatedAccessFlags)) {
+      return false;
+    }
+    if (ruleType != that.ruleType) {
+      return false;
+    }
+    if (name != null ? !name.equals(that.name) : that.name != null) {
+      return false;
+    }
+    if (type != null ? !type.equals(that.type) : that.type != null) {
+      return false;
+    }
+    return arguments != null ? arguments.equals(that.arguments) : that.arguments == null;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = annotation != null ? annotation.hashCode() : 0;
+    result = 31 * result + accessFlags.hashCode();
+    result = 31 * result + negatedAccessFlags.hashCode();
+    result = 31 * result + (ruleType != null ? ruleType.hashCode() : 0);
+    result = 31 * result + (name != null ? name.hashCode() : 0);
+    result = 31 * result + (type != null ? type.hashCode() : 0);
+    result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder result = new StringBuilder();
+    ProguardKeepRule.appendNonEmpty(result, "@", annotation, " ");
+    ProguardKeepRule.appendNonEmpty(result, null, accessFlags, " ");
+    ProguardKeepRule
+        .appendNonEmpty(result, null, negatedAccessFlags.toString().replace(" ", " !"), " ");
+    switch (getRuleType()) {
+      case ALL_FIELDS:
+        result.append("<fields>");
+        break;
+      case ALL_METHODS:
+        result.append("<methods>");
+        break;
+      case METHOD:
+        result.append(getType());
+        result.append(' ');
+      case CONSTRUCTOR:
+      case INIT: {
+        result.append(getName());
+        result.append('(');
+        result.append(StringUtils.join(getArguments(), ","));
+        result.append(')');
+        break;
+      }
+      case FIELD: {
+        result.append(getType());
+        result.append(' ');
+        result.append(getName());
+        break;
+      }
+      default:
+        throw new Unreachable("Unknown kind of member rule");
+    }
+    if (hasReturnValue()) {
+      result.append(returnValue.toString());
+    }
+    return result.toString();
+  }
+
+  public static ProguardMemberRule defaultKeepAllRule() {
+    ProguardMemberRule.Builder ruleBuilder = new ProguardMemberRule.Builder();
+    ruleBuilder.setRuleType(ProguardMemberType.ALL);
+    return ruleBuilder.build();
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
new file mode 100644
index 0000000..b4e9596
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.utils.LongInterval;
+
+public class ProguardMemberRuleReturnValue {
+
+  private final boolean booleanValue;
+  private final LongInterval longInterval;
+  private final DexField field;
+
+  ProguardMemberRuleReturnValue(boolean value) {
+    this.booleanValue = value;
+    this.longInterval = null;
+    this.field = null;
+  }
+
+  ProguardMemberRuleReturnValue(LongInterval value) {
+    this.booleanValue = false;
+    this.longInterval = value;
+    this.field = null;
+  }
+
+  ProguardMemberRuleReturnValue(DexField field) {
+    this.booleanValue = false;
+    this.longInterval = null;
+    this.field = field;
+  }
+
+  public boolean isBoolean() {
+    return longInterval == null && field == null;
+  }
+
+  public boolean isValueRange() {
+    return longInterval != null && field == null;
+  }
+
+  public boolean isField() {
+    return field != null;
+  }
+
+  public boolean getBoolean() {
+    assert isBoolean();
+    return booleanValue;
+  }
+
+  /**
+   * Returns if this return value is a single value.
+   *
+   * Boolean values are considered a single value.
+   */
+  public boolean isSingleValue() {
+    return isBoolean() || (isValueRange() && longInterval.isSingleValue());
+  }
+
+  /**
+   * Returns the return value.
+   *
+   * Boolean values are returned as 0 for <code>false</code> and 1 for <code>true</code>.
+   */
+  public long getSingleValue() {
+    assert isSingleValue();
+    if (isBoolean()) {
+      return booleanValue ? 1 : 0;
+    }
+    return longInterval.getSingleValue();
+  }
+
+  public LongInterval getValueRange() {
+    assert isValueRange();
+    return longInterval;
+  }
+
+  public DexField getField() {
+    assert isField();
+    return field;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder result = new StringBuilder();
+    result.append(" return ");
+    if (isBoolean()) {
+      result.append(booleanValue ? "true" : "false");
+    } else if (isValueRange()) {
+      result.append(longInterval.getMin());
+      if (!isSingleValue()) {
+        result.append("..");
+        result.append(longInterval.getMax());
+      }
+    } else {
+      assert isField();
+      result.append(field.clazz.toSourceString() + '.' + field.name);
+    }
+    return result.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberType.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberType.java
new file mode 100644
index 0000000..aa37a2a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberType.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public enum ProguardMemberType {
+  // Please keep the order so fields are before methods
+  FIELD,
+  ALL_FIELDS,
+  ALL,
+  ALL_METHODS,
+  INIT,
+  CONSTRUCTOR,
+  METHOD;
+
+  public boolean includesFields() {
+    return ordinal() <= ALL.ordinal();
+  }
+
+  public boolean includesMethods() {
+    return ordinal() >= ALL.ordinal();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
new file mode 100644
index 0000000..9286d0a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -0,0 +1,139 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public abstract class ProguardNameMatcher {
+
+  private static final ProguardNameMatcher MATCH_ALL_NAMES = new MatchAllNames();
+
+  private ProguardNameMatcher() {
+
+  }
+
+  public static ProguardNameMatcher create(String pattern) {
+    if (pattern.equals("*")) {
+      return MATCH_ALL_NAMES;
+    } else if (pattern.contains("*") || pattern.contains("?")) {
+      return new MatchNamePattern(pattern);
+    } else {
+      return new MatchSpecificName(pattern);
+    }
+  }
+
+  /**
+   * Determines if the proguard name pattern matches the given field or method name.
+   *
+   * @param pattern Proguard name pattern potentially with wildcards: ?, *.
+   * @param name The name to match against.
+   * @return true iff the pattern matches the name.
+   */
+  public static boolean matchFieldOrMethodName(String pattern, String name) {
+    return matchFieldOrMethodNameImpl(pattern, 0, name, 0);
+  }
+
+  private static boolean matchFieldOrMethodNameImpl(
+      String pattern, int patternIndex, String name, int nameIndex) {
+    for (int i = patternIndex; i < pattern.length(); i++) {
+      char patternChar = pattern.charAt(i);
+      switch (patternChar) {
+        case '*':
+          // Match the rest of the pattern against the rest of the name.
+          for (int nextNameIndex = nameIndex; nextNameIndex <= name.length(); nextNameIndex++) {
+            if (matchFieldOrMethodNameImpl(pattern, i + 1, name, nextNameIndex)) {
+              return true;
+            }
+          }
+          return false;
+        case '?':
+          if (nameIndex == name.length()) {
+            return false;
+          }
+          nameIndex++;
+          break;
+        default:
+          if (nameIndex == name.length() || patternChar != name.charAt(nameIndex++)) {
+            return false;
+          }
+          break;
+      }
+    }
+    return nameIndex == name.length();
+  }
+
+  public abstract boolean matches(String name);
+
+  private static class MatchAllNames extends ProguardNameMatcher {
+
+    @Override
+    public boolean matches(String name) {
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      return "*";
+    }
+  }
+
+  private static class MatchNamePattern extends ProguardNameMatcher {
+
+    private final String pattern;
+
+    MatchNamePattern(String pattern) {
+      this.pattern = pattern;
+    }
+
+    @Override
+    public boolean matches(String name) {
+      return matchFieldOrMethodName(pattern, name);
+    }
+
+    @Override
+    public String toString() {
+      return pattern;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      return o instanceof MatchNamePattern && pattern.equals(((MatchNamePattern) o).pattern);
+    }
+
+    @Override
+    public int hashCode() {
+      return pattern.hashCode();
+    }
+  }
+
+  private static class MatchSpecificName extends ProguardNameMatcher {
+
+    private final String name;
+
+    MatchSpecificName(String name) {
+      this.name = name;
+    }
+
+    @Override
+    public boolean matches(String name) {
+      return this.name.equals(name);
+    }
+
+    @Override
+    public String toString() {
+      return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof MatchSpecificName && name.equals(((MatchSpecificName) o).name);
+    }
+
+    @Override
+    public int hashCode() {
+      return name.hashCode();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardRuleParserException.java b/src/main/java/com/android/tools/r8/shaking/ProguardRuleParserException.java
new file mode 100644
index 0000000..0fb2787
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardRuleParserException.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+public class ProguardRuleParserException extends Exception {
+  public ProguardRuleParserException(String message, String snippet) {
+    this(message, snippet, null);
+  }
+
+  public ProguardRuleParserException(String message, String snippet, Throwable cause) {
+    super(message + " at " + snippet, cause);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
new file mode 100644
index 0000000..fd123d4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -0,0 +1,314 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DescriptorUtils;
+
+public abstract class ProguardTypeMatcher {
+
+  private static final String MATCH_ALL_PATTERN = "***";
+  private static final String MATCH_ANY_ARG_SEQUENCE_PATTERN = "...";
+  private static final String LEGACY_MATCH_CLASS_PATTERN = "*";
+  private static final String MATCH_CLASS_PATTERN = "**";
+  private static final String MATCH_BASIC_PATTERN = "%";
+
+  private ProguardTypeMatcher() {
+  }
+
+  enum ClassOrType {
+    CLASS,
+    TYPE
+  }
+
+  public abstract boolean matches(DexType type);
+
+  public abstract String toString();
+
+  public boolean isTripleDotPattern() {
+    return false;
+  }
+
+  public static ProguardTypeMatcher create(String pattern, ClassOrType kind,
+      DexItemFactory dexItemFactory) {
+    if (pattern == null) {
+      return null;
+    }
+    switch (pattern) {
+      case MATCH_ALL_PATTERN:
+        return MatchAllTypes.MATCH_ALL_TYPES;
+      case MATCH_ANY_ARG_SEQUENCE_PATTERN:
+        return MatchAnyArgSequence.MATCH_ANY_ARG_SEQUENCE;
+      case MATCH_CLASS_PATTERN:
+        return MatchClassTypes.MATCH_CLASS_TYPES;
+      case LEGACY_MATCH_CLASS_PATTERN:
+        return MatchClassTypes.LEGACY_MATCH_CLASS_TYPES;
+      case MATCH_BASIC_PATTERN:
+        return MatchBasicTypes.MATCH_BASIC_TYPES;
+    }
+    if (!pattern.contains("*") && !pattern.contains("%") && !pattern.contains("?")) {
+      return new MatchSpecificType(
+          dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(pattern)));
+    }
+    return new MatchTypePattern(pattern, kind);
+  }
+
+  public static ProguardTypeMatcher defaultAllMatcher() {
+    return MatchAllTypes.MATCH_ALL_TYPES;
+  }
+
+  @Override
+  public abstract boolean equals(Object o);
+
+  @Override
+  public abstract int hashCode();
+
+  private static class MatchAllTypes extends ProguardTypeMatcher {
+
+    private static final ProguardTypeMatcher MATCH_ALL_TYPES = new MatchAllTypes();
+
+    @Override
+    public boolean matches(DexType type) {
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      return MATCH_ALL_PATTERN;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof MatchAllTypes;
+    }
+
+    @Override
+    public int hashCode() {
+      return getClass().hashCode();
+    }
+  }
+
+  private static class MatchAnyArgSequence extends ProguardTypeMatcher {
+
+    private static final ProguardTypeMatcher MATCH_ANY_ARG_SEQUENCE = new MatchAnyArgSequence();
+
+    @Override
+    public boolean matches(DexType type) {
+      throw new IllegalStateException();
+    }
+
+    @Override
+    public String toString() {
+      return MATCH_ANY_ARG_SEQUENCE_PATTERN;
+    }
+
+    @Override
+    public boolean isTripleDotPattern() {
+      return true;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof MatchAnyArgSequence;
+    }
+
+    @Override
+    public int hashCode() {
+      return getClass().hashCode();
+    }
+  }
+
+  private static class MatchClassTypes extends ProguardTypeMatcher {
+
+    private static final ProguardTypeMatcher MATCH_CLASS_TYPES = new MatchClassTypes(
+        MATCH_CLASS_PATTERN);
+    private static final ProguardTypeMatcher LEGACY_MATCH_CLASS_TYPES = new MatchClassTypes(
+        LEGACY_MATCH_CLASS_PATTERN);
+
+    private final String pattern;
+
+    private MatchClassTypes(String pattern) {
+      assert pattern.equals(LEGACY_MATCH_CLASS_PATTERN) || pattern.equals(MATCH_CLASS_PATTERN);
+      this.pattern = pattern;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      return type.isClassType();
+    }
+
+    @Override
+    public String toString() {
+      return pattern;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof MatchClassTypes && pattern.equals(((MatchClassTypes) o).pattern);
+    }
+
+    @Override
+    public int hashCode() {
+      return pattern.hashCode();
+    }
+  }
+
+  private static class MatchBasicTypes extends ProguardTypeMatcher {
+
+    private static final ProguardTypeMatcher MATCH_BASIC_TYPES = new MatchBasicTypes();
+
+    @Override
+    public boolean matches(DexType type) {
+      return type.isPrimitiveType();
+    }
+
+    @Override
+    public String toString() {
+      return MATCH_BASIC_PATTERN;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof MatchBasicTypes;
+    }
+
+    @Override
+    public int hashCode() {
+      return getClass().hashCode();
+    }
+  }
+
+  public static class MatchSpecificType extends ProguardTypeMatcher {
+
+    public final DexType type;
+
+    private MatchSpecificType(DexType type) {
+      this.type = type;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      return this.type == type;
+    }
+
+    @Override
+    public String toString() {
+      return type.toSourceString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof MatchSpecificType) {
+        return type.equals(((MatchSpecificType) o).type);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return type.hashCode();
+    }
+  }
+
+  private static class MatchTypePattern extends ProguardTypeMatcher {
+
+    private final String pattern;
+    private final ClassOrType kind;
+
+    private MatchTypePattern(String pattern, ClassOrType kind) {
+      this.pattern = pattern;
+      this.kind = kind;
+    }
+
+    @Override
+    public boolean matches(DexType type) {
+      // TODO(herhut): Translate pattern to work on descriptors instead.
+      String typeName = type.toSourceString();
+      return matchClassOrTypeNameImpl(pattern, 0, typeName, 0, kind);
+    }
+
+    private static boolean matchClassOrTypeNameImpl(
+        String pattern, int patternIndex, String className, int nameIndex, ClassOrType kind) {
+      for (int i = patternIndex; i < pattern.length(); i++) {
+        char patternChar = pattern.charAt(i);
+        switch (patternChar) {
+          case '*':
+            boolean includeSeparators = pattern.length() > (i + 1) && pattern.charAt(i + 1) == '*';
+            int nextPatternIndex = i + (includeSeparators ? 2 : 1);
+            // Fast cases for the common case where a pattern ends with '**' or '*'.
+            if (nextPatternIndex == pattern.length()) {
+              if (includeSeparators) {
+                return kind == ClassOrType.CLASS || !isArrayType(className);
+              }
+              boolean hasSeparators = containsSeparatorsStartingAt(className, nameIndex);
+              return !hasSeparators && (kind == ClassOrType.CLASS || !isArrayType(className));
+            }
+            // Match the rest of the pattern against the (non-empty) rest of the class name.
+            for (int nextNameIndex = nameIndex; nextNameIndex < className.length();
+                nextNameIndex++) {
+              if (!includeSeparators && className.charAt(nextNameIndex) == '.') {
+                return matchClassOrTypeNameImpl(pattern, nextPatternIndex, className, nextNameIndex,
+                    kind);
+              }
+              if (kind == ClassOrType.TYPE && className.charAt(nextNameIndex) == '[') {
+                return matchClassOrTypeNameImpl(pattern, nextPatternIndex, className, nextNameIndex,
+                    kind);
+              }
+              if (matchClassOrTypeNameImpl(pattern, nextPatternIndex, className, nextNameIndex,
+                  kind)) {
+                return true;
+              }
+            }
+            // Finally, check the case where the '*' or '**' eats all of the class name.
+            return matchClassOrTypeNameImpl(pattern, nextPatternIndex, className,
+                className.length(),
+                kind);
+          case '?':
+            if (nameIndex == className.length() || className.charAt(nameIndex++) == '.') {
+              return false;
+            }
+            break;
+          default:
+            if (nameIndex == className.length() || patternChar != className.charAt(nameIndex++)) {
+              return false;
+            }
+            break;
+        }
+      }
+      return nameIndex == className.length();
+    }
+
+    private static boolean containsSeparatorsStartingAt(String className, int nameIndex) {
+      return className.indexOf('.', nameIndex) != -1;
+    }
+
+    private static boolean isArrayType(String type) {
+      int length = type.length();
+      if (length < 2) {
+        return false;
+      }
+      return type.charAt(length - 1) == ']' && type.charAt(length - 2) == '[';
+    }
+
+    @Override
+    public String toString() {
+      return pattern;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o instanceof MatchTypePattern) {
+        MatchTypePattern that = (MatchTypePattern) o;
+        return kind.equals(that.kind) && pattern.equals(that.pattern);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return pattern.hashCode() * 7 + kind.hashCode();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java b/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
new file mode 100644
index 0000000..47893e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
@@ -0,0 +1,243 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.Sets;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Set;
+
+public class ReasonPrinter {
+
+  private final Set<DexItem> itemsQueried;
+  private ReasonFormatter formatter;
+
+  private final Map<DexEncodedField, KeepReason> liveFields;
+  private final Map<DexEncodedMethod, KeepReason> liveMethods;
+  private final Map<DexItem, KeepReason> reachablityReasons;
+  private final Map<DexType, KeepReason> instantiatedTypes;
+
+  ReasonPrinter(Set<DexItem> itemsQueried, Map<DexEncodedField, KeepReason> liveFields,
+      Map<DexEncodedMethod, KeepReason> liveMethods, Map<DexItem, KeepReason> reachablityReasons,
+      Map<DexType, KeepReason> instantiatedTypes) {
+    this.itemsQueried = itemsQueried;
+    this.liveFields = liveFields;
+    this.liveMethods = liveMethods;
+    this.reachablityReasons = reachablityReasons;
+    this.instantiatedTypes = instantiatedTypes;
+  }
+
+  public void run(DexApplication application) {
+    // TODO(herhut): Instead of traversing the app, sort the queried items.
+    formatter = new ReasonFormatter();
+    for (DexClass clazz : application.classes()) {
+      if (itemsQueried.contains(clazz)) {
+        printReasonFor(clazz);
+      }
+      Arrays.stream(clazz.staticFields()).filter(itemsQueried::contains)
+          .forEach(this::printReasonFor);
+      Arrays.stream(clazz.instanceFields()).filter(itemsQueried::contains)
+          .forEach(this::printReasonFor);
+      Arrays.stream(clazz.directMethods()).filter(itemsQueried::contains)
+          .forEach(this::printReasonFor);
+      Arrays.stream(clazz.virtualMethods()).filter(itemsQueried::contains)
+          .forEach(this::printReasonFor);
+    }
+  }
+
+  private void printNoIdeaWhy(DexItem item, ReasonFormatter formatter) {
+    formatter.startItem(item);
+    formatter.pushEmptyPrefix();
+    formatter.addReason("is kept for unknown reason.");
+    formatter.popPrefix();
+    formatter.endItem();
+  }
+
+  private void printOnlyAbstractShell(DexItem item, ReasonFormatter formatter) {
+    formatter.startItem(item);
+    KeepReason reachableReason = reachablityReasons.get(item);
+    if (reachableReason != null) {
+      formatter.pushPrefix(
+          "is not kept, only its abstract declaration is needed because it ");
+      reachableReason.print(formatter);
+      formatter.popPrefix();
+    } else {
+      formatter.pushEmptyPrefix();
+      formatter.addReason("is not kept, only its abstract declaration is.");
+      formatter.popPrefix();
+    }
+    formatter.endItem();
+  }
+
+  private void printReasonFor(DexClass item) {
+    KeepReason reason = instantiatedTypes.get(item.type);
+    if (reason == null) {
+      if (item.accessFlags.isAbstract()) {
+        printOnlyAbstractShell(item, formatter);
+      } else {
+        printNoIdeaWhy(item, formatter);
+      }
+    } else {
+      formatter.startItem(item);
+      formatter.pushIsLivePrefix();
+      reason.print(formatter);
+      formatter.popPrefix();
+      formatter.endItem();
+    }
+  }
+
+  private void printReasonFor(DexEncodedMethod item) {
+    KeepReason reasonLive = liveMethods.get(item);
+    if (reasonLive == null) {
+      if (item.accessFlags.isAbstract()) {
+        printOnlyAbstractShell(item, formatter);
+      } else {
+        printNoIdeaWhy(item.method, formatter);
+      }
+    } else {
+      formatter.addMethodReferenceReason(item);
+    }
+  }
+
+  private void printReasonFor(DexEncodedField item) {
+    KeepReason reason = liveFields.get(item);
+    if (reason == null) {
+      if (item.accessFlags.isAbstract()) {
+        printOnlyAbstractShell(item, formatter);
+      } else {
+        printNoIdeaWhy(item.field, formatter);
+      }
+    } else {
+      formatter.startItem(item.field);
+      formatter.pushIsLivePrefix();
+      reason.print(formatter);
+      formatter.popPrefix();
+      reason = reachablityReasons.get(item);
+      if (reason != null) {
+        formatter.pushIsReachablePrefix();
+        reason.print(formatter);
+        formatter.popPrefix();
+      }
+      formatter.endItem();
+    }
+  }
+
+  class ReasonFormatter {
+
+    private Set<DexItem> seen = Sets.newIdentityHashSet();
+    private Deque<String> prefixes = new ArrayDeque<>();
+
+    private int indentation = -1;
+
+    void pushIsLivePrefix() {
+      prefixes.push("is live because ");
+    }
+
+    void pushIsReachablePrefix() {
+      prefixes.push("is reachable because ");
+    }
+
+    void pushPrefix(String prefix) {
+      prefixes.push(prefix);
+    }
+
+    void pushEmptyPrefix() {
+      prefixes.push("");
+    }
+
+    void popPrefix() {
+      prefixes.pop();
+    }
+
+    void startItem(DexItem item) {
+      indentation++;
+      indent();
+      System.out.println(item.toSourceString());
+    }
+
+    private void indent() {
+      for (int i = 0; i < indentation; i++) {
+        System.out.print("  ");
+      }
+    }
+
+    void addReason(String thing) {
+      indent();
+      System.out.print("|- ");
+      String prefix = prefixes.peek();
+      System.out.print(prefix);
+      System.out.println(thing);
+    }
+
+    void addMessage(String thing) {
+      indent();
+      System.out.print("|  ");
+      System.out.println(thing);
+    }
+
+    void endItem() {
+      indentation--;
+    }
+
+    void addMethodReferenceReason(DexEncodedMethod method) {
+      if (!seen.add(method.method)) {
+        return;
+      }
+      startItem(method);
+      KeepReason reason = reachablityReasons.get(method);
+      if (reason != null) {
+        pushIsReachablePrefix();
+        reason.print(this);
+        popPrefix();
+      }
+      reason = liveMethods.get(method);
+      if (reason != null) {
+        pushIsLivePrefix();
+        reason.print(this);
+        popPrefix();
+      }
+      endItem();
+    }
+
+    void addTypeLivenessReason(DexType type) {
+      if (!seen.add(type)) {
+        return;
+      }
+      startItem(type);
+      pushIsLivePrefix();
+      KeepReason reason = instantiatedTypes.get(type);
+      if (reason != null) {
+        reason.print(this);
+      }
+      popPrefix();
+      endItem();
+    }
+  }
+
+  public static ReasonPrinter getNoOpPrinter() {
+    return new NoOpReasonPrinter();
+  }
+
+  private static class NoOpReasonPrinter extends ReasonPrinter {
+
+    NoOpReasonPrinter() {
+      super(Collections.emptySet(), Collections.emptyMap(), Collections.emptyMap(),
+          Collections.emptyMap(), Collections.emptyMap());
+    }
+
+    @Override
+    public void run(DexApplication application) {
+      // Intentionally left empty.
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
new file mode 100644
index 0000000..848a743
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -0,0 +1,483 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
+import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+public class RootSetBuilder {
+
+  private DexApplication application;
+  private final List<ProguardConfigurationRule> rules;
+  private final Map<DexItem, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
+  private final Set<DexItem> noOptimization = Sets.newIdentityHashSet();
+  private final Set<DexItem> noObfuscation = Sets.newIdentityHashSet();
+  private final Set<DexItem> reasonAsked = Sets.newIdentityHashSet();
+  private final Set<DexItem> keepPackageName = Sets.newIdentityHashSet();
+  private final Set<ProguardConfigurationRule> rulesThatUseExtendsOrImplementsWrong =
+      Sets.newIdentityHashSet();
+  private final Set<DexItem> checkDiscarded = Sets.newIdentityHashSet();
+  private final Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking =
+      new IdentityHashMap<>();
+  private final Map<DexItem, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
+  private final Map<DexItem, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
+
+  public RootSetBuilder(DexApplication application,
+      List<ProguardConfigurationRule> rules) {
+    this.application = application;
+    this.rules = rules;
+  }
+
+  private boolean anySuperTypeMatches(DexType type, ProguardTypeMatcher name,
+      ProguardTypeMatcher annotation) {
+    while (type != null) {
+      DexClass clazz = application.definitionFor(type);
+      if (clazz == null) {
+        // TODO(herhut): Warn about broken supertype chain?
+        return false;
+      }
+      if (name.matches(clazz.type) && containsAnnotation(annotation, clazz.annotations)) {
+        return true;
+      }
+      type = clazz.superType;
+    }
+    return false;
+  }
+
+  private boolean anyImplementedInterfaceMatches(DexClass clazz,
+      ProguardTypeMatcher className, ProguardTypeMatcher annotation) {
+    if (clazz == null) {
+      return false;
+    }
+    for (DexType iface : clazz.interfaces.values) {
+      DexClass ifaceClass = application.definitionFor(iface);
+      if (ifaceClass == null) {
+        // TODO(herhut): Warn about broken supertype chain?
+        return false;
+      }
+      // TODO(herhut): Maybe it would be better to do this breadth first.
+      if (className.matches(iface) && containsAnnotation(annotation, ifaceClass.annotations)
+          || anyImplementedInterfaceMatches(ifaceClass, className, annotation)) {
+        return true;
+      }
+    }
+    if (clazz.superType == null) {
+      return false;
+    }
+    DexClass superClass = application.definitionFor(clazz.superType);
+    if (superClass == null) {
+      // TODO(herhut): Warn about broken supertype chain?
+      return false;
+    }
+    return anyImplementedInterfaceMatches(superClass, className, annotation);
+  }
+
+  // Returns a list of types iff the keep rule only contains specific type matches.
+  // Otherwise, null is returned.
+  private DexType[] specificDexTypes(ProguardConfigurationRule rule) {
+    for (ProguardTypeMatcher matcher : rule.getClassNames()) {
+      if (!(matcher instanceof MatchSpecificType)) {
+        return null;
+      }
+    }
+    final int length = rule.getClassNames().size();
+    DexType[] result = new DexType[length];
+    for (int i = 0; i < length; i++) {
+      result[i] = ((MatchSpecificType) rule.getClassNames().get(i)).type;
+    }
+    return result;
+  }
+
+  // Process a class with the keep rule.
+  private void process(DexClass clazz, ProguardConfigurationRule rule) {
+    if (!clazz.accessFlags.containsAllOf(rule.getClassAccessFlags())) {
+      return;
+    }
+    if (!clazz.accessFlags.containsNoneOf(rule.getNegatedClassAccessFlags())) {
+      return;
+    }
+    if (!containsAnnotation(rule.getClassAnnotation(), clazz.annotations)) {
+      return;
+    }
+
+    // In principle it should make a difference whether the user specified in a class
+    // spec that a class either extends or implements another type. However, proguard
+    // seems not to care, so users have started to use this inconsistently. We are thus
+    // inconsistent, as well, but tell them.
+    // TODO(herhut): One day make this do what it says.
+    if (rule.hasInheritanceClassName()) {
+      boolean extendsExpected =
+          anySuperTypeMatches(clazz.superType, rule.getInheritanceClassName(),
+              rule.getInheritanceAnnotation());
+      boolean implementsExpected = false;
+      if (!extendsExpected) {
+        implementsExpected =
+            anyImplementedInterfaceMatches(clazz, rule.getInheritanceClassName(),
+                rule.getInheritanceAnnotation());
+      }
+      if (!extendsExpected && !implementsExpected) {
+        return;
+      }
+      // Warn if users got it wrong, but only warn once.
+      if (extendsExpected && !rule.getInheritanceIsExtends()) {
+        if (rulesThatUseExtendsOrImplementsWrong.add(rule)) {
+          System.err.println(
+              "The rule `" + rule + "` uses implements but actually matches extends.");
+        }
+      } else if (implementsExpected && rule.getInheritanceIsExtends()) {
+        if (rulesThatUseExtendsOrImplementsWrong.add(rule)) {
+          System.err.println(
+              "The rule `" + rule + "` uses extends but actually matches implements.");
+        }
+      }
+    }
+
+    for (ProguardTypeMatcher className : rule.getClassNames()) {
+      if (className.matches(clazz.type)) {
+        Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
+        if (rule instanceof ProguardKeepRule) {
+          switch (((ProguardKeepRule) rule).getType()) {
+            case KEEP_CLASS_MEMBERS: {
+              markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
+              markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
+              break;
+            }
+            case KEEP_CLASSES_WITH_MEMBERS: {
+              if (!allRulesSatisfied(memberKeepRules, clazz)) {
+                break;
+              }
+              // fallthrough;
+            }
+            case KEEP: {
+              markClass(clazz, rule);
+              markClass(clazz, rule);
+              markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+              markMatchingFields(clazz, memberKeepRules, rule, null);
+              break;
+            }
+          }
+        } else if (rule instanceof ProguardAssumeNoSideEffectRule) {
+          markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+          markMatchingFields(clazz, memberKeepRules, rule, null);
+        } else {
+          assert rule instanceof ProguardAssumeValuesRule;
+          markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+          markMatchingFields(clazz, memberKeepRules, rule, null);
+        }
+      }
+    }
+  }
+
+  public RootSet run(ExecutorService executorService) throws ExecutionException {
+    application.timing.begin("Build root set...");
+    try {
+      List<Future<?>> futures = new ArrayList<>();
+      // Mark all the things explicitly listed in keep rules.
+      if (rules != null) {
+        for (ProguardConfigurationRule rule : rules) {
+          DexType[] specifics = specificDexTypes(rule);
+          if (specifics != null) {
+            // This keep rule only lists specific type matches.
+            // This means there is no need to iterate over all classes.
+            for (DexType type : specifics) {
+              DexClass clazz = application.definitionFor(type);
+              // Ignore keep rule iff it does not reference a class in the app.
+              if (clazz != null) {
+                process(clazz, rule);
+              }
+            }
+          } else {
+            futures.add(executorService.submit(() -> {
+              for (DexProgramClass clazz : application.classes()) {
+                process(clazz, rule);
+              }
+              if (rule.applyToLibraryClasses()) {
+                for (DexLibraryClass clazz : application.libraryClasses()) {
+                  process(clazz, rule);
+                }
+              }
+            }));
+          }
+        }
+        ThreadUtils.awaitFutures(futures);
+      }
+    } finally {
+      application.timing.end();
+    }
+    return new RootSet(noShrinking, noOptimization, noObfuscation, reasonAsked, keepPackageName,
+        checkDiscarded, noSideEffects, assumedValues, dependentNoShrinking);
+  }
+
+  private void markMatchingVisibleMethods(DexClass clazz,
+      Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+      DexType onlyIfClassKept) {
+    Set<Wrapper<DexMethod>> methodsMarked = new HashSet<>();
+    while (clazz != null) {
+      markMethods(clazz.directMethods(), memberKeepRules, rule, methodsMarked, onlyIfClassKept);
+      markMethods(clazz.virtualMethods(), memberKeepRules, rule, methodsMarked, onlyIfClassKept);
+      clazz = application.definitionFor(clazz.superType);
+    }
+  }
+
+  private void markMatchingFields(DexClass clazz,
+      Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+      DexType onlyIfClassKept) {
+    markFields(clazz.staticFields(), memberKeepRules, rule, onlyIfClassKept);
+    markFields(clazz.instanceFields(), memberKeepRules, rule, onlyIfClassKept);
+  }
+
+  public static void writeSeeds(Iterable<DexItem> seeds, PrintStream out) {
+    for (DexItem seed : seeds) {
+      if (seed instanceof DexClass) {
+        out.println(((DexClass) seed).type.toSourceString());
+      } else if (seed instanceof DexEncodedField) {
+        DexField field = ((DexEncodedField) seed).field;
+        out.println(
+            field.clazz.toSourceString() + ": " + field.type.toSourceString() + " " + field.name
+                .toSourceString());
+      } else if (seed instanceof DexEncodedMethod) {
+        DexEncodedMethod encodedMethod = (DexEncodedMethod) seed;
+        DexMethod method = encodedMethod.method;
+        out.print(method.holder.toSourceString() + ": ");
+        if (encodedMethod.accessFlags.isConstructor()) {
+          if (encodedMethod.accessFlags.isStatic()) {
+            out.print("<clinit>(");
+          } else {
+            String holderName = method.holder.toSourceString();
+            String constrName = holderName.substring(holderName.lastIndexOf('.') + 1);
+            out.print(constrName + "(");
+          }
+        } else {
+          out.print(
+              method.proto.returnType.toSourceString() + " " + method.name.toSourceString() + "(");
+        }
+        boolean first = true;
+        for (DexType param : method.proto.parameters.values) {
+          if (!first) {
+            out.print(",");
+          }
+          first = false;
+          out.print(param.toSourceString());
+        }
+        out.println(")");
+      }
+    }
+    out.close();
+  }
+
+  private boolean allRulesSatisfied(Collection<ProguardMemberRule> memberKeepRules,
+      DexClass clazz) {
+    for (ProguardMemberRule rule : memberKeepRules) {
+      if (!ruleSatisfied(rule, clazz)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Checks whether the given rule is satisfied bu this clazz, not taking superclasses into
+   * account.
+   */
+  private boolean ruleSatisfied(ProguardMemberRule rule, DexClass clazz) {
+    if (ruleSatisfiedByMethods(rule, clazz.directMethods()) ||
+        ruleSatisfiedByMethods(rule, clazz.virtualMethods()) ||
+        ruleSatisfiedByFields(rule, clazz.staticFields()) ||
+        ruleSatisfiedByFields(rule, clazz.instanceFields())) {
+      return true;
+    }
+    return false;
+  }
+
+  private boolean ruleSatisfiedByMethods(ProguardMemberRule rule, DexEncodedMethod[] methods) {
+    if (rule.getRuleType().includesMethods()) {
+      for (DexEncodedMethod method : methods) {
+        if (rule.matches(method, this)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private boolean ruleSatisfiedByFields(ProguardMemberRule rule, DexEncodedField[] fields) {
+    if (rule.getRuleType().includesFields()) {
+      for (DexEncodedField field : fields) {
+        if (rule.matches(field, this)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  static boolean containsAnnotation(ProguardTypeMatcher classAnnotation,
+      DexAnnotationSet annotations) {
+    if (classAnnotation == null) {
+      return true;
+    }
+    if (annotations.isEmpty()) {
+      return false;
+    }
+    for (DexAnnotation annotation : annotations.annotations) {
+      if (classAnnotation.matches(annotation.annotation.type)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private final IdentityHashMap<DexString, String> stringCache = new IdentityHashMap<>();
+  private final IdentityHashMap<DexType, String> typeCache = new IdentityHashMap<>();
+
+  public String lookupString(DexString name) {
+    return stringCache.computeIfAbsent(name, DexString::toString);
+  }
+
+  public String lookupType(DexType type) {
+    return typeCache.computeIfAbsent(type, DexType::toSourceString);
+  }
+
+  private void markMethods(DexEncodedMethod[] methods, Collection<ProguardMemberRule> rules,
+      ProguardConfigurationRule context, Set<Wrapper<DexMethod>> methodsMarked,
+      DexType onlyIfClassKept) {
+    for (DexEncodedMethod method : methods) {
+      if (methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
+        continue;
+      }
+      for (ProguardMemberRule rule : rules) {
+        if (rule.matches(method, this)) {
+          if (Log.ENABLED) {
+            Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
+                rule);
+          }
+          addItemToSets(method, context, rule, onlyIfClassKept);
+        }
+      }
+    }
+  }
+
+  private void markFields(DexEncodedField[] fields, Collection<ProguardMemberRule> rules,
+      ProguardConfigurationRule context, DexType onlyIfClassKept) {
+    for (DexEncodedField field : fields) {
+      for (ProguardMemberRule rule : rules) {
+        if (rule.matches(field, this)) {
+          if (Log.ENABLED) {
+            Log.verbose(getClass(), "Marking field `%s` due to `%s { %s }`.", field, context,
+                rule);
+          }
+          addItemToSets(field, context, rule, onlyIfClassKept);
+        }
+      }
+    }
+  }
+
+  private void markClass(DexClass clazz, ProguardConfigurationRule rule) {
+    if (Log.ENABLED) {
+      Log.verbose(getClass(), "Marking class `%s` due to `%s`.", clazz.type, rule);
+    }
+    addItemToSets(clazz, rule, null, null);
+  }
+
+  private synchronized void addItemToSets(DexItem item, ProguardConfigurationRule context,
+      ProguardMemberRule rule, DexType onlyIfClassKept) {
+    if (context instanceof ProguardKeepRule) {
+      ProguardKeepRule keepRule = (ProguardKeepRule) context;
+      ProguardKeepRuleModifiers modifiers = keepRule.getModifiers();
+      if (!modifiers.allowsShrinking) {
+        if (onlyIfClassKept != null) {
+          dependentNoShrinking.computeIfAbsent(onlyIfClassKept, x -> new IdentityHashMap<>())
+              .put(item, keepRule);
+        } else {
+          noShrinking.put(item, keepRule);
+        }
+      }
+      if (!modifiers.allowsOptimization) {
+        noOptimization.add(item);
+      }
+      if (!modifiers.allowsObfuscation) {
+        noObfuscation.add(item);
+      }
+      if (modifiers.whyAreYouKeeping) {
+        assert onlyIfClassKept == null;
+        reasonAsked.add(item);
+      }
+      if (modifiers.keepPackageNames) {
+        assert onlyIfClassKept == null;
+        keepPackageName.add(item);
+      }
+      if (modifiers.checkDiscarded) {
+        checkDiscarded.add(item);
+      }
+    } else if (context instanceof ProguardAssumeNoSideEffectRule) {
+      noSideEffects.put(item, rule);
+    } else if (context instanceof ProguardAssumeValuesRule) {
+      assumedValues.put(item, rule);
+    }
+  }
+
+  public static class RootSet {
+
+    public final Map<DexItem, ProguardKeepRule> noShrinking;
+    public final Set<DexItem> noOptimization;
+    public final Set<DexItem> noObfuscation;
+    public final Set<DexItem> reasonAsked;
+    public final Set<DexItem> keepPackageName;
+    public final Set<DexItem> checkDiscarded;
+    public final Map<DexItem, ProguardMemberRule> noSideEffects;
+    public final Map<DexItem, ProguardMemberRule> assumedValues;
+    private final Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking;
+
+    private RootSet(Map<DexItem, ProguardKeepRule> noShrinking,
+        Set<DexItem> noOptimization, Set<DexItem> noObfuscation, Set<DexItem> reasonAsked,
+        Set<DexItem> keepPackageName, Set<DexItem> checkDiscarded,
+        Map<DexItem, ProguardMemberRule> noSideEffects,
+        Map<DexItem, ProguardMemberRule> assumedValues,
+        Map<DexType, Map<DexItem, ProguardKeepRule>> dependentNoShrinking) {
+      this.noShrinking = Collections.unmodifiableMap(noShrinking);
+      this.noOptimization = Collections.unmodifiableSet(noOptimization);
+      this.noObfuscation = Collections.unmodifiableSet(noObfuscation);
+      this.reasonAsked = Collections.unmodifiableSet(reasonAsked);
+      this.keepPackageName = Collections.unmodifiableSet(keepPackageName);
+      this.checkDiscarded = Collections.unmodifiableSet(checkDiscarded);
+      this.noSideEffects = Collections.unmodifiableMap(noSideEffects);
+      this.assumedValues = Collections.unmodifiableMap(assumedValues);
+      this.dependentNoShrinking = dependentNoShrinking;
+    }
+
+    Map<DexItem, ProguardKeepRule> getDependentItems(DexType type) {
+      return Collections
+          .unmodifiableMap(dependentNoShrinking.getOrDefault(type, Collections.emptyMap()));
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
new file mode 100644
index 0000000..5f2996f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassPromise;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class TreePruner {
+
+  private DexApplication application;
+  private final AppInfoWithLiveness appInfo;
+  private final InternalOptions options;
+
+  public TreePruner(
+      DexApplication application, AppInfoWithLiveness appInfo, InternalOptions options) {
+    this.application = application;
+    this.appInfo = appInfo;
+    this.options = options;
+  }
+
+  public DexApplication run() {
+    application.timing.begin("Pruning application...");
+    if (options.debugKeepRules && !options.skipMinification) {
+      System.out.println(
+          "NOTE: Debugging keep rules on a minified build might yield broken builds, as\n" +
+              "      minifcation also depends on the used keep rules. We recommend using\n" +
+              "      --skip-minification.");
+    }
+    DexApplication result;
+    try {
+      result = removeUnused(application).build();
+    } finally {
+      application.timing.end();
+    }
+    return result;
+  }
+
+  private DexApplication.Builder removeUnused(DexApplication application) {
+    return new DexApplication.Builder(application, removeUnusedClassStructure(application));
+  }
+
+  private Map<DexType, DexClassPromise> removeUnusedClassStructure(DexApplication application) {
+    Map<DexType, DexClassPromise> classMap = new IdentityHashMap<>();
+    for (DexLibraryClass clazz : application.libraryClasses()) {
+      classMap.put(clazz.type, clazz);
+    }
+    for (DexProgramClass clazz : application.classes()) {
+      if (!appInfo.liveTypes.contains(clazz.type) && !options.debugKeepRules) {
+        // The class is completely unused and we can remove it.
+        if (Log.ENABLED) {
+          Log.debug(getClass(), "Removing class: " + clazz);
+        }
+      } else {
+        classMap.put(clazz.type, clazz);
+        if (!appInfo.instantiatedTypes.contains(clazz.type) && !options.debugKeepRules) {
+          // The class is only needed as a type but never instantiated. Make it abstract to reflect
+          // this.
+          if (clazz.accessFlags.isFinal()) {
+            // We cannot mark this class abstract, as it is final (not supported on Android).
+            // However, this might extend an abstract class and we might have removed the
+            // corresponding methods in this class. This might happen if we only keep this
+            // class around for its constants.
+            // TODO(herhut): Find alternative for abstract final classes.
+          } else {
+            clazz.accessFlags.setAbstract();
+          }
+        }
+        // The class is used and must be kept. Remove the unused fields and methods from
+        // the class.
+        clazz.directMethods = reachableMethods(clazz.directMethods(), clazz);
+        clazz.virtualMethods = reachableMethods(clazz.virtualMethods(), clazz);
+        clazz.instanceFields = reachableFields(clazz.instanceFields());
+        clazz.staticFields = reachableFields(clazz.staticFields());
+      }
+    }
+    return classMap;
+  }
+
+  private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
+      T[] items, Set<S> live) {
+    for (int i = 0; i < items.length; i++) {
+      if (!live.contains(items[i].getKey())) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  private DexEncodedMethod[] reachableMethods(DexEncodedMethod[] methods, DexClass clazz) {
+    int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods);
+    // Return the original array if all methods are used.
+    if (firstUnreachable == -1) {
+      return methods;
+    }
+    ArrayList<DexEncodedMethod> reachableMethods = new ArrayList<>(methods.length);
+    for (int i = 0; i < firstUnreachable; i++) {
+      reachableMethods.add(methods[i]);
+    }
+    for (int i = firstUnreachable; i < methods.length; i++) {
+      if (appInfo.liveMethods.contains(methods[i].getKey())) {
+        reachableMethods.add(methods[i]);
+      } else if (options.debugKeepRules) {
+        // Keep the method but rewrite its body, if it has one.
+        reachableMethods.add(methods[i].accessFlags.isAbstract()
+            ? methods[i]
+            : methods[i].toMethodThatLogsError(application.dexItemFactory));
+      } else if (appInfo.targetedMethods.contains(methods[i].getKey())) {
+        if (Log.ENABLED) {
+          Log.debug(getClass(), "Making method %s abstract.", methods[i].method);
+        }
+        DexEncodedMethod method = methods[i];
+        // Final classes cannot be abstract, so we have to keep the method in that case.
+        // Also some other kinds of methods cannot be abstract, so keep them around.
+        boolean allowAbstract = clazz.accessFlags.isAbstract()
+            && !method.accessFlags.isFinal()
+            && !method.accessFlags.isNative()
+            && !method.accessFlags.isStrict()
+            && !method.accessFlags.isSynchronized();
+        // By construction, private and static methods cannot be reachable but non-live.
+        assert !method.accessFlags.isPrivate() && !method.accessFlags.isStatic();
+        reachableMethods.add(allowAbstract
+            ? methods[i].toAbstractMethod()
+            : methods[i].toEmptyThrowingMethod());
+      } else if (Log.ENABLED) {
+        Log.debug(getClass(), "Removing method %s.", methods[i].method);
+      }
+    }
+    return reachableMethods.toArray(new DexEncodedMethod[reachableMethods.size()]);
+  }
+
+  private DexEncodedField[] reachableFields(DexEncodedField[] fields) {
+    int firstUnreachable = firstUnreachableIndex(fields, appInfo.liveFields);
+    if (firstUnreachable == -1) {
+      return fields;
+    }
+    if (Log.ENABLED) {
+      Log.debug(getClass(), "Removing field: " + fields[firstUnreachable]);
+    }
+    ArrayList<DexEncodedField> reachableFields = new ArrayList<>(fields.length);
+    for (int i = 0; i < firstUnreachable; i++) {
+      reachableFields.add(fields[i]);
+    }
+    for (int i = firstUnreachable + 1; i < fields.length; i++) {
+      if (appInfo.liveFields.contains(fields[i].getKey())) {
+        reachableFields.add(fields[i]);
+      } else if (Log.ENABLED) {
+        Log.debug(getClass(), "Removing field: " + fields[i]);
+      }
+    }
+    return reachableFields.toArray(new DexEncodedField[reachableFields.size()]);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
new file mode 100644
index 0000000..53680ee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -0,0 +1,580 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.isArchive;
+import static com.android.tools.r8.utils.FileUtils.isClassFile;
+import static com.android.tools.r8.utils.FileUtils.isDexFile;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.CopyOption;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Collection of program files needed for processing.
+ *
+ * <p>This abstraction is the main input and output container for a given application.
+ */
+public class AndroidApp {
+
+  public static final String DEFAULT_PROGUARD_MAP_FILE = "proguard.map";
+  public static final String DEFAULT_PROGUARD_SEEDS_FILE = "proguard.seeds";
+  public static final String DEFAULT_PACKAGE_DISTRIBUTION_FILE = "package.map";
+
+  private final ImmutableList<InternalResource> dexSources;
+  private final ImmutableList<InternalResource> classSources;
+  private final InternalResource proguardMap;
+  private final InternalResource proguardSeeds;
+  private final InternalResource packageDistribution;
+  private final InternalResource mainDexList;
+
+  // See factory methods and AndroidApp.Builder below.
+  private AndroidApp(
+      ImmutableList<InternalResource> dexSources,
+      ImmutableList<InternalResource> classSources,
+      InternalResource proguardMap,
+      InternalResource proguardSeeds,
+      InternalResource packageDistribution,
+      InternalResource mainDexList) {
+    this.dexSources = dexSources;
+    this.classSources = classSources;
+    this.proguardMap = proguardMap;
+    this.proguardSeeds = proguardSeeds;
+    this.packageDistribution = packageDistribution;
+    this.mainDexList = mainDexList;
+  }
+
+  /**
+   * Create a new empty builder.
+   */
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  /**
+   * Create a new builder initialized with the resources from @code{app}.
+   */
+  public static Builder builder(AndroidApp app) {
+    return new Builder(app);
+  }
+
+  /**
+   * Create an app from program files @code{files}. See also Builder::addProgramFiles.
+   */
+  public static AndroidApp fromProgramFiles(Path... files) throws IOException {
+    return fromProgramFiles(Arrays.asList(files));
+  }
+
+  /**
+   * Create an app from program files @code{files}. See also Builder::addProgramFiles.
+   */
+  public static AndroidApp fromProgramFiles(List<Path> files) throws IOException {
+    return builder().addProgramFiles(files).build();
+  }
+
+  /**
+   * Create an app from files found in @code{directory}. See also Builder::addProgramDirectory.
+   */
+  public static AndroidApp fromProgramDirectory(Path directory) throws IOException {
+    return builder().addProgramDirectory(directory).build();
+  }
+
+  /**
+   * Create an app from dex program data. See also Builder::addDexProgramData.
+   */
+  public static AndroidApp fromDexProgramData(byte[]... data) {
+    return fromDexProgramData(Arrays.asList(data));
+  }
+
+  /**
+   * Create an app from dex program data. See also Builder::addDexProgramData.
+   */
+  public static AndroidApp fromDexProgramData(List<byte[]> data) {
+    return builder().addDexProgramData(data).build();
+  }
+
+  /**
+   * Create an app from Java-bytecode program data. See also Builder::addClassProgramData.
+   */
+  public static AndroidApp fromClassProgramData(byte[]... data) {
+    return fromClassProgramData(Arrays.asList(data));
+  }
+
+  /**
+   * Create an app from Java-bytecode program data. See also Builder::addClassProgramData.
+   */
+  public static AndroidApp fromClassProgramData(List<byte[]> data) {
+    return builder().addClassProgramData(data).build();
+  }
+
+  /** Get input streams for all dex program resources. */
+  public List<InternalResource> getDexProgramResources() {
+    return filter(dexSources, InternalResource.Kind.PROGRAM);
+  }
+
+  /** Get input streams for all Java-bytecode program resources. */
+  public List<InternalResource> getClassProgramResources() {
+    return filter(classSources, InternalResource.Kind.PROGRAM);
+  }
+
+  /** Get input streams for all dex program classpath resources. */
+  public List<InternalResource> getDexClasspathResources() {
+    return filter(dexSources, InternalResource.Kind.CLASSPATH);
+  }
+
+  /** Get input streams for all Java-bytecode classpath resources. */
+  public List<InternalResource> getClassClasspathResources() {
+    return filter(classSources, InternalResource.Kind.CLASSPATH);
+  }
+
+  /** Get input streams for all dex library resources. */
+  public List<InternalResource> getDexLibraryResources() {
+    return filter(dexSources, InternalResource.Kind.LIBRARY);
+  }
+
+  /** Get input streams for all Java-bytecode library resources. */
+  public List<InternalResource> getClassLibraryResources() {
+    return filter(classSources, InternalResource.Kind.LIBRARY);
+  }
+
+  private List<InternalResource> filter(
+      List<InternalResource> resources, InternalResource.Kind kind) {
+    List<InternalResource> out = new ArrayList<>(resources.size());
+    for (InternalResource resource : resources) {
+      if (kind == resource.kind) {
+        out.add(resource);
+      }
+    }
+    return out;
+  }
+
+  /**
+   * True if the proguard-map resource exists.
+   */
+  public boolean hasProguardMap() {
+    return proguardMap != null;
+  }
+
+  /**
+   * Get the input stream of the proguard-map resource if it exists.
+   */
+  public InputStream getProguardMap(Closer closer) throws IOException {
+    return proguardMap == null ? null : proguardMap.getStream(closer);
+  }
+
+  /**
+   * True if the proguard-seeds resource exists.
+   */
+  public boolean hasProguardSeeds() {
+    return proguardSeeds != null;
+  }
+
+  /**
+   * Get the input stream of the proguard-seeds resource if it exists.
+   */
+  public InputStream getProguardSeeds(Closer closer) throws IOException {
+    return proguardSeeds == null ? null : proguardSeeds.getStream(closer);
+  }
+
+  /** True if the package distribution resource exists. */
+  public boolean hasPackageDistribution() {
+    return packageDistribution != null;
+  }
+
+  /** Get the input stream of the package distribution resource if it exists. */
+  public InputStream getPackageDistribution(Closer closer) throws IOException {
+    return packageDistribution == null ? null : packageDistribution.getStream(closer);
+  }
+
+  /**
+   * True if the main dex list resource exists.
+   */
+  public boolean hasMainDexList() {
+    return mainDexList != null;
+  }
+
+  /**
+   * Get the input stream of the main dex list resource if it exists.
+   */
+  public InputStream getMainDexList(Closer closer) throws IOException {
+    return mainDexList == null ? null : mainDexList.getStream(closer);
+  }
+
+  /**
+   * Write the dex program resources and proguard resource to @code{output}.
+   */
+  public void write(Path output) throws IOException {
+    write(output, false);
+  }
+
+  /**
+   * Write the dex program resources and proguard resource to @code{output}.
+   */
+  public void write(Path output, boolean overwrite) throws IOException {
+    if (isArchive(output)) {
+      writeToZip(output);
+    } else {
+      writeToDirectory(output);
+    }
+  }
+
+  /**
+   * Write the dex program resources and proguard resource to @code{directory}.
+   */
+  public void writeToDirectory(Path directory) throws IOException {
+    writeToDirectory(directory, false);
+  }
+
+  /**
+   * Write the dex program resources and proguard resource to @code{directory}.
+   */
+  public void writeToDirectory(Path directory, boolean overwrite) throws IOException {
+    CopyOption[] options = copyOptions(overwrite);
+    try (Closer closer = Closer.create()) {
+      List<InternalResource> dexProgramSources = getDexProgramResources();
+      for (int i = 0; i < dexProgramSources.size(); i++) {
+        Files.copy(dexProgramSources.get(i).getStream(closer),
+            directory.resolve(getDexFileName(i)), options);
+      }
+      writeProguardMap(closer, directory, overwrite);
+      writeProguardSeeds(closer, directory, overwrite);
+    }
+  }
+
+  /**
+   * Write the dex program resources to @code{archive} and the proguard resource as its sibling.
+   */
+  public void writeToZip(Path archive) throws IOException {
+    writeToZip(archive, false);
+  }
+
+  public List<byte[]> writeToMemory() throws IOException {
+    List<byte[]> dex = new ArrayList<>();
+    try (Closer closer = Closer.create()) {
+      List<InternalResource> dexProgramSources = getDexProgramResources();
+      for (int i = 0; i < dexProgramSources.size(); i++) {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ByteStreams.copy(dexProgramSources.get(i).getStream(closer), out);
+        dex.add(out.toByteArray());
+      }
+      // TODO(sgjesse): Add Proguard map and seeds.
+    }
+    return dex;
+  }
+
+  /**
+   * Write the dex program resources to @code{archive} and the proguard resource as its sibling.
+   */
+  public void writeToZip(Path archive, boolean overwrite) throws IOException {
+    OpenOption[] options = openOptions(overwrite);
+    try (Closer closer = Closer.create()) {
+      try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(archive, options))) {
+        List<InternalResource> dexProgramSources = getDexProgramResources();
+        for (int i = 0; i < dexProgramSources.size(); i++) {
+          ZipEntry zipEntry = new ZipEntry(getDexFileName(i));
+          byte[] bytes = ByteStreams.toByteArray(dexProgramSources.get(i).getStream(closer));
+          zipEntry.setSize(bytes.length);
+          out.putNextEntry(zipEntry);
+          out.write(bytes);
+          out.closeEntry();
+        }
+      }
+      // Write the proguard map to the archives containing directory.
+      // TODO(zerny): How do we want to determine the output location for the extra resources?
+      writeProguardMap(closer, archive.getParent(), overwrite);
+      writeProguardSeeds(closer, archive.getParent(), overwrite);
+    }
+  }
+
+  private void writeProguardMap(Closer closer, Path parent, boolean overwrite) throws IOException {
+    InputStream input = getProguardMap(closer);
+    if (input != null) {
+      Files.copy(input, parent.resolve(DEFAULT_PROGUARD_MAP_FILE), copyOptions(overwrite));
+    }
+  }
+
+  public void writeProguardMap(Closer closer, OutputStream out) throws IOException {
+    InputStream input = getProguardMap(closer);
+    assert input != null;
+    out.write(ByteStreams.toByteArray(input));
+  }
+
+  private void writeProguardSeeds(Closer closer, Path parent, boolean overwrite)
+      throws IOException {
+    InputStream input = getProguardSeeds(closer);
+    if (input != null) {
+      Files.copy(input, parent.resolve(DEFAULT_PROGUARD_SEEDS_FILE), copyOptions(overwrite));
+    }
+  }
+
+  private String getDexFileName(int index) {
+    return index == 0 ? "classes.dex" : ("classes" + (index + 1) + ".dex");
+  }
+
+  private OpenOption[] openOptions(boolean overwrite) {
+    return new OpenOption[]{
+        overwrite ? StandardOpenOption.CREATE : StandardOpenOption.CREATE_NEW,
+        StandardOpenOption.TRUNCATE_EXISTING
+    };
+  }
+
+  private CopyOption[] copyOptions(boolean overwrite) {
+    return overwrite
+        ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING}
+        : new CopyOption[]{};
+  }
+
+  /**
+   * Builder interface for constructing an AndroidApp.
+   */
+  public static class Builder {
+
+    private final List<InternalResource> dexSources = new ArrayList<>();
+    private final List<InternalResource> classSources = new ArrayList<>();
+    private InternalResource proguardMap;
+    private InternalResource proguardSeeds;
+    private InternalResource packageDistribution;
+    private InternalResource mainDexList;
+
+    // See AndroidApp::builder().
+    private Builder() {
+    }
+
+    // See AndroidApp::builder(AndroidApp).
+    private Builder(AndroidApp app) {
+      dexSources.addAll(app.dexSources);
+      classSources.addAll(app.classSources);
+      proguardMap = app.proguardMap;
+      proguardSeeds = app.proguardSeeds;
+      packageDistribution = app.packageDistribution;
+      mainDexList = app.mainDexList;
+    }
+
+    /**
+     * Add dex program files and proguard-map file located in @code{directory}.
+     *
+     * <p>The program files included are the top-level files ending in .dex and the proguard-map
+     * file should it exist (see @code{DEFAULT_PROGUARD_MAP_FILE} for its assumed name).
+     *
+     * <p>This method is mostly a utility for reading in the file content produces by some external
+     * tool, eg, dx.
+     *
+     * @param directory Directory containing dex program files and optional proguard-map file.
+     */
+    public Builder addProgramDirectory(Path directory) throws IOException {
+      File[] resources = directory.toFile().listFiles(file -> isDexFile(file.toPath()));
+      for (File source : resources) {
+        addFile(source.toPath(), InternalResource.Kind.PROGRAM);
+      }
+      File mapFile = new File(directory.toFile(), DEFAULT_PROGUARD_MAP_FILE);
+      if (mapFile.exists()) {
+        setProguardMapFile(mapFile.toPath());
+      }
+      return this;
+    }
+
+    /**
+     * Add program file resources.
+     */
+    public Builder addProgramFiles(Path... files) throws IOException {
+      return addProgramFiles(Arrays.asList(files));
+    }
+
+    /** Add program file resources. */
+    public Builder addProgramFiles(Collection<Path> files) throws IOException {
+      for (Path file : files) {
+        addFile(file, InternalResource.Kind.PROGRAM);
+      }
+      return this;
+    }
+
+    /**
+     * Add classpath file resources.
+     */
+    public Builder addClasspathFiles(Path... files) throws IOException {
+      return addClasspathFiles(Arrays.asList(files));
+    }
+
+    /** Add classpath file resources. */
+    public Builder addClasspathFiles(Collection<Path> files) throws IOException {
+      for (Path file : files) {
+        addFile(file, InternalResource.Kind.CLASSPATH);
+      }
+      return this;
+    }
+
+    /**
+     * Add library file resources.
+     */
+    public Builder addLibraryFiles(Path... files) throws IOException {
+      return addLibraryFiles(Arrays.asList(files));
+    }
+
+    /** Add library file resources. */
+    public Builder addLibraryFiles(Collection<Path> files) throws IOException {
+      for (Path file : files) {
+        addFile(file, InternalResource.Kind.LIBRARY);
+      }
+      return this;
+    }
+
+    /**
+     * Add dex program-data.
+     */
+    public Builder addDexProgramData(byte[]... data) {
+      return addDexProgramData(Arrays.asList(data));
+    }
+
+    /** Add dex program-data. */
+    public Builder addDexProgramData(Collection<byte[]> data) {
+      for (byte[] datum : data) {
+        dexSources.add(InternalResource.fromBytes(InternalResource.Kind.PROGRAM, datum));
+      }
+      return this;
+    }
+
+    /**
+     * Add Java-bytecode program data.
+     */
+    public Builder addClassProgramData(byte[]... data) {
+      return addClassProgramData(Arrays.asList(data));
+    }
+
+    /** Add Java-bytecode program data. */
+    public Builder addClassProgramData(Collection<byte[]> data) {
+      for (byte[] datum : data) {
+        classSources.add(InternalResource.fromBytes(InternalResource.Kind.PROGRAM, datum));
+      }
+      return this;
+    }
+
+    /** Set proguard-map file. */
+    public Builder setProguardMapFile(Path file) {
+      proguardMap = file == null ? null : InternalResource.fromFile(null, file);
+      return this;
+    }
+
+    /** Set proguard-map data. */
+    public Builder setProguardMapData(String content) {
+      return setProguardMapData(content == null ? null : content.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /** Set proguard-map data. */
+    public Builder setProguardMapData(byte[] content) {
+      proguardMap = content == null ? null : InternalResource.fromBytes(null, content);
+      return this;
+    }
+
+    /** Set proguard-seeds data. */
+    public Builder setProguardSeedsData(byte[] content) {
+      proguardSeeds = content == null ? null : InternalResource.fromBytes(null, content);
+      return this;
+    }
+
+    /** Set the package-distribution file. */
+    public Builder setPackageDistributionFile(Path file) {
+      packageDistribution = file == null ? null : InternalResource.fromFile(null, file);
+      return this;
+    }
+
+    /** Set the main-dex list file. */
+    public Builder setMainDexListFile(Path file) {
+      mainDexList = file == null ? null : InternalResource.fromFile(null, file);
+      return this;
+    }
+
+    /**
+     * Build final AndroidApp.
+     */
+    public AndroidApp build() {
+      return new AndroidApp(
+          ImmutableList.copyOf(dexSources),
+          ImmutableList.copyOf(classSources),
+          proguardMap,
+          proguardSeeds,
+          packageDistribution,
+          mainDexList);
+    }
+
+    private void addFile(Path file, InternalResource.Kind kind) throws IOException {
+      if (!Files.exists(file)) {
+        throw new FileNotFoundException("Non-existent input file: " + file);
+      }
+      if (isDexFile(file)) {
+        dexSources.add(InternalResource.fromFile(kind, file));
+      } else if (isClassFile(file)) {
+        classSources.add(InternalResource.fromFile(kind, file));
+      } else if (isArchive(file)) {
+        addArchive(file, kind);
+      } else {
+        throw new CompilationError("Unsupported source file type for file: " + file);
+      }
+    }
+
+    private void addArchive(Path archive, InternalResource.Kind kind) throws IOException {
+      assert isArchive(archive);
+      boolean containsDexData = false;
+      boolean containsClassData = false;
+      try (ZipInputStream stream = new ZipInputStream(new FileInputStream(archive.toFile()))) {
+        ZipEntry entry;
+        while ((entry = stream.getNextEntry()) != null) {
+          Path name = Paths.get(entry.getName());
+          if (isDexFile(name)) {
+            containsDexData = true;
+            dexSources.add(InternalResource.fromBytes(kind, ByteStreams.toByteArray(stream)));
+          } else if (isClassFile(name)) {
+            containsClassData = true;
+            classSources.add(InternalResource.fromBytes(
+                kind, ByteStreams.toByteArray(stream), guessTypeDescriptor(name)));
+          }
+        }
+      } catch (ZipException e) {
+        throw new CompilationError(
+            "Zip error while reading '" + archive + "': " + e.getMessage(), e);
+      }
+      if (containsDexData && containsClassData) {
+        throw new CompilationError(
+            "Cannot create android app from an archive '" + archive
+                + "' containing both DEX and Java-bytecode content");
+      }
+    }
+  }
+
+  // Guess class descriptor from location of the class file in the archive.
+  private static String guessTypeDescriptor(Path name) {
+    String fileName = name.toString();
+    assert fileName != null;
+    assert fileName.endsWith(CLASS_EXTENSION);
+    String descriptor = fileName.substring(0, fileName.length() - CLASS_EXTENSION.length());
+    if (descriptor.contains(".")) {
+      throw new CompilationError("Unexpected file name in the jar: " + name);
+    }
+    return 'L' + descriptor + ';';
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/CfgPrinter.java b/src/main/java/com/android/tools/r8/utils/CfgPrinter.java
new file mode 100644
index 0000000..0c0aa66
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CfgPrinter.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.Stack;
+
+/**
+ * Printer abstraction for creating flow-graphs for the C1 visualizer.
+ */
+public class CfgPrinter {
+
+  private final StringBuilder builder = new StringBuilder();
+  private final Stack<String> opened = new Stack<>();
+  private final int indentSpacing = 2;
+
+  public int nextUnusedValue = 0;
+
+  public String makeUnusedValue() {
+    return "_" + nextUnusedValue++;
+  }
+
+  public void resetUnusedValue() {
+    nextUnusedValue = 0;
+  }
+
+  public CfgPrinter begin(String title) {
+    print("begin_");
+    append(title).ln();
+    opened.push(title);
+    return this;
+  }
+
+  public CfgPrinter end(String title) {
+    String top = opened.pop();
+    assert title.equals(top);
+    print("end_");
+    append(title).ln();
+    return this;
+  }
+
+  public CfgPrinter print(int i) {
+    printIndent();
+    builder.append(i);
+    return this;
+  }
+
+  public CfgPrinter print(String string) {
+    printIndent();
+    builder.append(string);
+    return this;
+  }
+
+  public CfgPrinter append(int i) {
+    builder.append(i);
+    return this;
+  }
+
+  public CfgPrinter append(String string) {
+    builder.append(string);
+    return this;
+  }
+
+  public CfgPrinter sp() {
+    builder.append(" ");
+    return this;
+  }
+
+  public CfgPrinter ln() {
+    builder.append("\n");
+    return this;
+  }
+
+  private void printIndent() {
+    for (int i = 0; i < opened.size() * indentSpacing; i++) {
+      builder.append(" ");
+    }
+  }
+
+  @Override
+  public String toString() {
+    return builder.toString();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
new file mode 100644
index 0000000..e038f6a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -0,0 +1,224 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class DescriptorUtils {
+
+  public static final char DESCRIPTOR_PACKAGE_SEPARATOR = '/';
+  public static final char JAVA_PACKAGE_SEPARATOR = '.';
+  private static final Map<String, String> typeNameToLetterMap =
+      ImmutableMap.<String, String>builder()
+          .put("void", "V")
+          .put("boolean", "Z")
+          .put("byte", "B")
+          .put("short", "S")
+          .put("char", "C")
+          .put("int", "I")
+          .put("long", "J")
+          .put("float", "F")
+          .put("double", "D")
+          .build();
+
+  private static String internalToDescriptor(String typeName, boolean shorty) {
+    String descriptor = typeNameToLetterMap.get(typeName);
+    if (descriptor != null) {
+      return descriptor;
+    }
+    // Must be some array or object type.
+    if (shorty) {
+      return "L";
+    }
+    if (typeName.endsWith("[]")) {
+      return "[" + internalToDescriptor(typeName.substring(0, typeName.length() - 2), shorty);
+    }
+    // Must be an object type.
+    return "L" + typeName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR) + ";";
+  }
+
+  /**
+   * Convert a Java type name to a descriptor string.
+   *
+   * @param typeName the java type name
+   * @return the descriptor string
+   */
+  public static String javaTypeToDescriptor(String typeName) {
+    assert typeName.indexOf(DESCRIPTOR_PACKAGE_SEPARATOR) == -1;
+    return internalToDescriptor(typeName, false);
+  }
+
+  /**
+   * Convert a Java type name to a shorty descriptor string.
+   *
+   * @param typeName the java type name
+   * @return the shorty descriptor string
+   */
+  public static String javaTypeToShorty(String typeName) {
+    return internalToDescriptor(typeName, true);
+  }
+
+  /**
+   * Convert a type descriptor to a Java type name.
+   *
+   * @param descriptor type descriptor
+   * @return Java type name
+   */
+  public static String descriptorToJavaType(String descriptor) {
+    return descriptorToJavaType(descriptor, null);
+  }
+
+  /**
+   * Convert a type descriptor to a Java type name. Will also deobfuscate class names if a
+   * class mapper is provided.
+   *
+   * @param descriptor type descriptor
+   * @param classNameMapper class name mapper for mapping obfuscated class names
+   * @return Java type name
+   */
+  public static String descriptorToJavaType(String descriptor, ClassNameMapper classNameMapper) {
+    switch (descriptor.charAt(0)) {
+      case 'L':
+        assert descriptor.charAt(descriptor.length() - 1) == ';';
+        String clazz = descriptor.substring(1, descriptor.length() - 1)
+            .replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
+        String originalName =
+            classNameMapper == null ? null : classNameMapper.deobfuscateClassName(clazz);
+        if (originalName == null) {
+          // Class was not renamed, reconstruct the java name.
+          return clazz;
+        }
+        return originalName;
+      case '[':
+        return descriptorToJavaType(descriptor.substring(1, descriptor.length()), classNameMapper)
+            + "[]";
+      case 'V':
+        return "void";
+      case 'Z':
+        return "boolean";
+      case 'B':
+        return "byte";
+      case 'S':
+        return "short";
+      case 'C':
+        return "char";
+      case 'I':
+        return "int";
+      case 'J':
+        return "long";
+      case 'F':
+        return "float";
+      case 'D':
+        return "double";
+      default:
+        throw new Unreachable("Unknown type " + descriptor);
+    }
+  }
+
+  /**
+   * Get class name from its descriptor.
+   *
+   * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+   * @return class name i.e. "Object"
+   */
+  public static String getSimpleClassNameFromDescriptor(String classDescriptor) {
+    return getSimpleClassNameFromBinaryName(getClassBinaryNameFromDescriptor(classDescriptor));
+  }
+
+  /**
+   * Get package java name from a class descriptor.
+   *
+   * @param descriptor a class descriptor i.e. "Ljava/lang/Object;"
+   * @return java package name i.e. "java.lang"
+   */
+  public static String getPackageNameFromDescriptor(String descriptor) {
+    return getPackageNameFromBinaryName(getClassBinaryNameFromDescriptor(descriptor));
+  }
+
+  /**
+   * Convert class descriptor to a binary name.
+   *
+   * @param classDescriptor a class descriptor i.e. "Ljava/lang/Object;"
+   * @return class binary name i.e. "java/lang/Object"
+   */
+  public static String getClassBinaryNameFromDescriptor(String classDescriptor) {
+    assert isClassDescriptor(classDescriptor) : "Invalid class descriptor "
+        + classDescriptor;
+    return classDescriptor.substring(1, classDescriptor.length() - 1);
+  }
+
+  /**
+   * Get class name from its binary name.
+   *
+   * @param classBinaryName a class binary name i.e. "java/lang/Object"
+   * @return class name i.e. "Object"
+   */
+  public static String getSimpleClassNameFromBinaryName(String classBinaryName) {
+    int simpleNameIndex = classBinaryName.lastIndexOf(DESCRIPTOR_PACKAGE_SEPARATOR);
+    return (simpleNameIndex < 0) ? classBinaryName : classBinaryName.substring(simpleNameIndex + 1);
+  }
+
+  public static boolean isClassDescriptor(String descriptor) {
+    char[] buffer = descriptor.toCharArray();
+    int length = buffer.length;
+    if (length < 3 || buffer[0] != 'L') {
+      return false;
+    }
+
+    int pos = 1;
+    char ch;
+    do {
+      // First letter of an Ident (an Ident can't be empty)
+      if (pos >= length) {
+        return false;
+      }
+
+      ch = buffer[pos++];
+      if (isInvalidChar(ch) || ch == DESCRIPTOR_PACKAGE_SEPARATOR || ch == ';') {
+        return false;
+      }
+
+      // Next letters of an Ident
+      do {
+        if (pos >= length) {
+          return false;
+        }
+
+        ch = buffer[pos++];
+        if (isInvalidChar(ch)) {
+          return false;
+        }
+      } while (ch != DESCRIPTOR_PACKAGE_SEPARATOR && ch != ';');
+
+    } while (ch != ';');
+
+    return pos == length;
+  }
+
+  /**
+   * Get package java name from a class binary name
+   *
+   * @param classBinaryName a class binary name i.e. "java/lang/Object"
+   * @return java package name i.e. "java.lang"
+   */
+  public static String getPackageNameFromBinaryName(String classBinaryName) {
+    int nameIndex = classBinaryName.lastIndexOf(DESCRIPTOR_PACKAGE_SEPARATOR);
+    return (nameIndex < 0) ? "" : classBinaryName.substring(0, nameIndex)
+        .replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR);
+  }
+
+  private static boolean isInvalidChar(char ch) {
+    switch (ch) {
+      case JAVA_PACKAGE_SEPARATOR:
+      case '[':
+        return true;
+      default:
+        return false;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/EncodedValueUtils.java b/src/main/java/com/android/tools/r8/utils/EncodedValueUtils.java
new file mode 100644
index 0000000..8903525
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/EncodedValueUtils.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.DexFile;
+import com.android.tools.r8.dex.DexOutputBuffer;
+
+public class EncodedValueUtils {
+
+  public static long parseSigned(DexFile file, int numberOfBytes) {
+    assert numberOfBytes > 0;
+    long result = 0;
+    int shift = 0;
+    for (int i = 1; i < numberOfBytes; i++) {
+      result |= ((long) ((file.get() & 0xFF))) << shift;
+      shift += 8;
+    }
+    return result | (file.get() << shift);
+  }
+
+  // Inspired by com.android.dex.EncodedValueCodec
+  public static int putSigned(DexOutputBuffer outputBuffer, long value, int expectedSize) {
+    int bit_size = Long.SIZE + 1 - Long.numberOfLeadingZeros(value ^ (value >> (Long.SIZE - 1)));
+    int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+    assert size > 0 && size <= expectedSize;
+    for (int i = 0; i < size; i++) {
+      outputBuffer.putByte((byte) value);
+      value >>= Byte.SIZE;
+    }
+    return size;
+  }
+
+  // Inspired by com.android.dex.EncodedValueCodec
+  public static byte[] encodeSigned(long value) {
+    int bit_size = Long.SIZE + 1 - Long.numberOfLeadingZeros(value ^ (value >> (Long.SIZE - 1)));
+    int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+    byte[] result = new byte[size];
+    for (int i = 0; i < size; i++) {
+      result[i] = (byte) value;
+      value >>= Byte.SIZE;
+    }
+    return result;
+  }
+
+  public static long parseUnsigned(DexFile file, int numberOfBytes) {
+    assert numberOfBytes > 0;
+    long result = 0;
+    int shift = 0;
+    for (int i = 0; i < numberOfBytes; i++) {
+      result |= ((long) (file.get() & 0xFF)) << shift;
+      shift += 8;
+    }
+    return result;
+  }
+
+  // Inspired by com.android.dex.EncodedValueCodec
+  public static int putUnsigned(DexOutputBuffer outputBuffer, long value,
+      int expectedSize) {
+    int bit_size = Long.SIZE - Long.numberOfLeadingZeros(value);
+    int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+    assert size > 0 && size <= expectedSize;
+    for (int i = 0; i < size; i++) {
+      outputBuffer.putByte((byte) value);
+      value >>= Byte.SIZE;
+    }
+    return size;
+  }
+
+  public static byte[] encodeUnsigned(long value) {
+    int bit_size = Long.SIZE - Long.numberOfLeadingZeros(value);
+    int size = Math.max((bit_size + Byte.SIZE - 1) / Byte.SIZE, 1);
+    byte[] result = new byte[size];
+    for (int i = 0; i < size; i++) {
+      result[i] = (byte) value;
+      value >>= Byte.SIZE;
+    }
+    return result;
+  }
+
+  public static int putBitsFromRightZeroExtended(DexOutputBuffer outputBuffer, long value,
+      int expectedSize) {
+    int bit_size = Long.SIZE - Long.numberOfTrailingZeros(value);
+    int size = (bit_size - 1) / Byte.SIZE + 1;
+    assert size > 0 && size <= expectedSize;
+    value >>= Long.SIZE - (size * Byte.SIZE); // shift trailing zeros.
+    for (int i = 0; i < size; i++) {
+      outputBuffer.putByte((byte) value);
+      value >>= Byte.SIZE;
+    }
+    return size;
+  }
+
+  private static byte[] encodeBitsFromRightZeroExtended(long value) {
+    int bit_size = Long.SIZE - Long.numberOfTrailingZeros(value);
+    int size = (bit_size - 1) / Byte.SIZE + 1;
+    value >>= Long.SIZE - (size * Byte.SIZE); // shift trailing zeros.
+    byte[] result = new byte[size];
+    for (int i = 0; i < size; i++) {
+      result[i] = (byte) value;
+      value >>= Byte.SIZE;
+    }
+    return result;
+  }
+
+  public static float parseFloat(DexFile file, int numberOfBytes) {
+    long bits = parseUnsigned(file, numberOfBytes) << ((Float.BYTES - numberOfBytes) * Byte.SIZE);
+    return Float.intBitsToFloat((int) bits);
+  }
+
+  public static int putFloat(DexOutputBuffer outputBuffer, float value) {
+    long bits = ((long) Float.floatToIntBits(value)) << 32;
+    return EncodedValueUtils.putBitsFromRightZeroExtended(outputBuffer, bits, Float.BYTES);
+  }
+
+  public static byte[] encodeFloat(float value) {
+    long tmp = ((long) Float.floatToIntBits(value)) << 32;
+    byte[] result = EncodedValueUtils.encodeBitsFromRightZeroExtended(tmp);
+    assert result.length <= Float.BYTES;
+    return result;
+  }
+
+  public static double parseDouble(DexFile file, int numberOfBytes) {
+    long bits = parseUnsigned(file, numberOfBytes) << ((Double.BYTES - numberOfBytes) * Byte.SIZE);
+    return Double.longBitsToDouble(bits);
+  }
+
+  public static int putDouble(DexOutputBuffer outputBuffer, double value) {
+    long bits = Double.doubleToLongBits(value);
+    return EncodedValueUtils.putBitsFromRightZeroExtended(outputBuffer, bits, Double.BYTES);
+  }
+
+  public static byte[] encodeDouble(double value) {
+    long tmp = Double.doubleToLongBits(value);
+    byte[] result = EncodedValueUtils.encodeBitsFromRightZeroExtended(tmp);
+    assert result.length <= Double.BYTES;
+    return result;
+  }
+
+}
+
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
new file mode 100644
index 0000000..9a2b9f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.CompilationException;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FileUtils {
+
+  public static final String APK_EXTENSION = ".apk";
+  public static final String CLASS_EXTENSION = ".class";
+  public static final String DEX_EXTENSION = ".dex";
+  public static final String JAR_EXTENSION = ".jar";
+  public static final String ZIP_EXTENSION = ".zip";
+  public static final String DEFAULT_DEX_FILENAME = "classes.dex";
+
+  public static boolean isDexFile(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(DEX_EXTENSION);
+  }
+
+  public static boolean isClassFile(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(CLASS_EXTENSION);
+  }
+
+  public static boolean isJarFile(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(JAR_EXTENSION);
+  }
+
+  public static boolean isZipFile(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(ZIP_EXTENSION);
+  }
+
+  public static boolean isApkFile(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(APK_EXTENSION);
+  }
+
+  public static boolean isArchive(Path path) {
+    String name = path.getFileName().toString().toLowerCase();
+    return name.endsWith(APK_EXTENSION)
+        || name.endsWith(JAR_EXTENSION)
+        || name.endsWith(ZIP_EXTENSION);
+  }
+
+  public static List<String> readTextFile(Path file) throws IOException {
+    try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
+      List<String> result = new ArrayList<>();
+      String line;
+      while ((line = reader.readLine()) != null) {
+        result.add(line);
+      }
+      return result;
+    }
+  }
+
+  public static void writeTextFile(Path file, List<String> lines) throws IOException {
+    try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
+      for (String line : lines) {
+        writer.write(line);
+        writer.write("\n");
+      }
+    }
+  }
+
+  public static Path validateOutputFile(Path path) throws CompilationException {
+    if (path != null) {
+      if (isZipFile(path)) {
+        if (Files.exists(path)) {
+          throw new CompilationException("Cannot write to existing output file: " + path);
+        }
+      } else if (!(Files.exists(path) && Files.isDirectory(path))) {
+        throw new CompilationException(
+            "Invalid output: "
+                + path
+                + "\nOutput must be a non-existing zip archive or an existing directory");
+      }
+    }
+    return path;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/HashMapInt.java b/src/main/java/com/android/tools/r8/utils/HashMapInt.java
new file mode 100644
index 0000000..7e91644
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/HashMapInt.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Hash map based on open addressing where keys are Objects and values are basic ints.
+// Provides: put, get, and size.
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Objects;
+
+public class HashMapInt<T> extends SimpleHashMap {
+
+  private Object[] keys;
+  private int[] values;
+
+  public HashMapInt() {
+    super();
+  }
+
+  public HashMapInt(int initialCapacity) {
+    super(initialCapacity);
+  }
+
+  public HashMapInt(int initialCapacity, double loadFactor) {
+    super(initialCapacity, loadFactor);
+  }
+
+  public void put(final T key, final int value) {
+    if (key == null) {
+      throw new RuntimeException("HashMapInt does not support null as key.");
+    }
+    ensureCapacity();
+    basePut(key, value);
+  }
+
+  boolean equals(Object one, Object other) {
+    return one.equals(other);
+  }
+
+  public int get(final T key) {
+    if (key == null) {
+      throw new RuntimeException("HashMapInt does not support null as key.");
+    }
+    int count = 1;
+    int index = firstProbe(key);
+    while (!equals(key, keys[index])) {
+      if (keys[index] == null) {
+        throw new RuntimeException("HashMapInt get only works if key is present.");
+      }
+      index = nextProbe(index, count++);
+    }
+    return values[index];
+  }
+
+  int firstProbe(T key) {
+    return firstProbe(key.hashCode());
+  }
+
+  public boolean containsKey(final T key) {
+    if (key == null) {
+      throw new RuntimeException("HashMapInt does not support null as key.");
+    }
+    int count = 1;
+    for (int index = firstProbe(key); ; index = nextProbe(index, count++)) {
+      Object k = keys[index];
+      if (k == null) {
+        return false;
+      }
+      if (equals(k, key)) {
+        return true;
+      }
+    }
+  }
+
+  private void basePut(final T key, final int value) {
+    int count = 1;
+    int index = firstProbe(key);
+    while ((keys[index] != null) && (keys[index] != key)) {
+      index = nextProbe(index, count++);
+    }
+    if (keys[index] == null) {
+      keys[index] = key;
+      incrementSize();
+    }
+    values[index] = value;
+  }
+
+  @SuppressWarnings("unchecked")
+  public Iterable<Integer> values() {
+    return () -> Arrays.stream(keys).filter(Objects::nonNull).map((key) -> get((T) key)).iterator();
+  }
+
+  @SuppressWarnings("unchecked")
+  public Iterable<T> keys() {
+    return () -> (Iterator<T>) Arrays.stream(keys).filter(Objects::nonNull).iterator();
+  }
+
+  @SuppressWarnings("unchecked")
+  void resize() {
+    final Object[] oldKeys = keys;
+    final int[] oldValues = values;
+    final int oldLength = length();
+    super.resize();
+    // Repopulate.
+    for (int index = 0; index < oldLength; index++) {
+      T key = (T) oldKeys[index];
+      if (key != null) {
+        basePut(key, oldValues[index]);
+      }
+    }
+  }
+
+  void initialize(final int length, final int limit) {
+    super.initialize(length, limit);
+    keys = new Object[length];
+    values = new int[length];
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IdentityHashMapInt.java b/src/main/java/com/android/tools/r8/utils/IdentityHashMapInt.java
new file mode 100644
index 0000000..66e62ac
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IdentityHashMapInt.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+public class IdentityHashMapInt<T> extends HashMapInt<T> {
+
+  public IdentityHashMapInt() {
+    super();
+  }
+
+  public IdentityHashMapInt(int initialCapacity) {
+    super(initialCapacity);
+  }
+
+  public IdentityHashMapInt(int initialCapacity, double loadFactor) {
+    super(initialCapacity, loadFactor);
+  }
+
+  @Override
+  int firstProbe(T key) {
+    return firstProbe(System.identityHashCode(key));
+  }
+
+  @Override
+  boolean equals(Object one, Object other) {
+    return one == other;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IntHashMap.java b/src/main/java/com/android/tools/r8/utils/IntHashMap.java
new file mode 100644
index 0000000..6a92316
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntHashMap.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Hash map based on open addressing where keys are basic ints and values are Objects.
+// Provides: put, get, and size.
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+public class IntHashMap<T> extends SimpleHashMap {
+
+  private int[] keys;
+  private Object[] values;
+
+  public IntHashMap() {
+    super();
+  }
+
+  public IntHashMap(int initialCapacity) {
+    super(initialCapacity);
+  }
+
+  public IntHashMap(int initialCapacity, double loadFactor) {
+    super(initialCapacity, loadFactor);
+  }
+
+  public void put(final int key, final T value) {
+    if (value == null) {
+      throw new RuntimeException("IntHashMap does not support null as value.");
+    }
+    ensureCapacity();
+    basePut(key, value);
+  }
+
+  public T get(final int key) {
+    int count = 1;
+    int index = firstProbe(computeHash(key));
+    // Note that unused entries in keys is 0.
+    // That means we only need to check for value != null when key == 0.
+    while ((keys[index] != key) && (values[index] != null)) {
+      index = nextProbe(index, count++);
+    }
+    assert (keys[index] == key) || (values[index] == null);
+    @SuppressWarnings("unchecked")
+    T result = (T) values[index];
+    return result;
+  }
+
+  private void basePut(final int key, final Object value) {
+    assert value != null;
+    int count = 1;
+    int index = firstProbe(computeHash(key));
+    while ((values[index] != null) && (keys[index] != key)) {
+      index = nextProbe(index, count++);
+    }
+    if (values[index] == null) {
+      keys[index] = key;
+      incrementSize();
+    }
+    values[index] = value;
+    assert value.equals(get(key));
+  }
+
+  void resize() {
+    final int[] oldKeys = keys;
+    final Object[] oldValues = values;
+    final int oldLength = length();
+    super.resize();
+    // Repopulate.
+    for (int index = 0; index < oldLength; index++) {
+      Object value = oldValues[index];
+      if (value != null) {
+        basePut(oldKeys[index], value);
+      }
+    }
+  }
+
+  void initialize(final int length, final int limit) {
+    super.initialize(length, limit);
+    keys = new int[length];
+    values = new Object[length];
+  }
+
+  @SuppressWarnings("unchecked")
+  public Iterable<T> values() {
+    return () -> (Iterator<T>) Arrays.stream(values).filter(Objects::nonNull).iterator();
+  }
+
+  public Iterable<Integer> keys() {
+    if (get(0) != null) {
+      return () -> IntStream.concat(IntStream.of(0), Arrays.stream(keys).filter(i -> i != 0))
+          .iterator();
+    }
+    return () -> Arrays.stream(keys).filter(i -> i != 0 || get(i) != null).distinct().iterator();
+  }
+
+  // Thomas Wang, Integer Hash Functions.
+  // http://www.concentric.net/~Ttwang/tech/inthash.htm
+  private static int computeHash(final int key) {
+    int hash = key;
+    hash = ~hash + (hash << 15);  // hash = (hash << 15) - hash - 1;
+    hash = hash ^ (hash >> 12);
+    hash = hash + (hash << 2);
+    hash = hash ^ (hash >> 4);
+    hash = hash * 2057;  // hash = (hash + (hash << 3)) + (hash << 11);
+    hash = hash ^ (hash >> 16);
+    return hash & 0x3fffffff;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IntIntHashMap.java b/src/main/java/com/android/tools/r8/utils/IntIntHashMap.java
new file mode 100644
index 0000000..03d4911
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntIntHashMap.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Hash map based on open addressing where keys are positive basic ints and values are basic ints.
+// Provides: put, get, and size.
+
+import java.util.Arrays;
+
+public class IntIntHashMap extends SimpleHashMap {
+
+  private final int EMPTY_KEY = -1;
+
+  private int[] keys;
+  private int[] values;
+
+  public IntIntHashMap() {
+    super();
+  }
+
+  public IntIntHashMap(int initialCapacity) {
+    super(initialCapacity);
+  }
+
+  public IntIntHashMap(int initialCapacity, double loadFactor) {
+    super(initialCapacity, loadFactor);
+  }
+
+  public void put(final int key, final int value) {
+    if (key < 0) {
+      throw new RuntimeException("IntIntHashMap does not support negative ints as key.");
+    }
+    ensureCapacity();
+    basePut(key, value);
+  }
+
+  public int get(final int key) {
+    if (key < 0) {
+      throw new RuntimeException("IntIntHashMap does not support negative ints as key.");
+    }
+    int count = 1;
+    int index = firstProbe(key);
+    while (key != keys[index]) {
+      if (keys[index] == EMPTY_KEY) {
+        throw new RuntimeException("IntIntHashMap get only works if key is present.");
+      }
+      index = nextProbe(index, count++);
+    }
+    return values[index];
+  }
+
+  public boolean containsKey(final int key) {
+    if (key < 0) {
+      throw new RuntimeException("IntIntHashMap does not support negative ints as key.");
+    }
+    int count = 1;
+    for (int index = firstProbe(key); ; index = nextProbe(index, count++)) {
+      int k = keys[index];
+      if (k == EMPTY_KEY) {
+        return false;
+      }
+      if (k == key) {
+        return true;
+      }
+    }
+  }
+
+  private void basePut(final int key, final int value) {
+    int count = 1;
+    int index = firstProbe(key);
+    while ((keys[index] != EMPTY_KEY) && (keys[index] != key)) {
+      index = nextProbe(index, count++);
+    }
+    if (keys[index] != key) {
+      incrementSize();
+      keys[index] = key;
+    }
+    values[index] = value;
+  }
+
+  void resize() {
+    final int[] oldKeys = keys;
+    final int[] oldValues = values;
+    final int oldLength = length();
+    super.resize();
+    // Repopulate.
+    for (int index = 0; index < oldLength; index++) {
+      int key = oldKeys[index];
+      if (key != EMPTY_KEY) {
+        basePut(key, oldValues[index]);
+      }
+    }
+  }
+
+  void initialize(final int length, final int limit) {
+    super.initialize(length, limit);
+    keys = new int[length];
+    Arrays.fill(keys, EMPTY_KEY);
+    values = new int[length];
+  }
+
+
+  @SuppressWarnings("unchecked")
+  public Iterable<Integer> values() {
+    return () -> Arrays.stream(keys).filter(k -> k > EMPTY_KEY).map(this::get).iterator();
+  }
+
+  @SuppressWarnings("unchecked")
+  public Iterable<Integer> keys() {
+    return () -> Arrays.stream(keys).filter(k -> k > EMPTY_KEY).iterator();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
new file mode 100644
index 0000000..d3b80e3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -0,0 +1,260 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.shaking.ProguardTypeMatcher;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.List;
+
+public class InternalOptions {
+
+  public final DexItemFactory itemFactory;
+
+  public InternalOptions() {
+    itemFactory = new DexItemFactory();
+  }
+
+  public InternalOptions(DexItemFactory factory) {
+    assert factory != null;
+    itemFactory = factory;
+  }
+
+  public final int NOT_SPECIFIED = -1;
+
+  public boolean printTimes = false;
+  // Skipping optimizations.
+  public boolean skipDebugInfoOpt = false;
+  public boolean skipDebugLineNumberOpt = false;
+
+  public boolean lazyClasspathLoading = false;
+  public boolean lazyLibraryLoading = false;
+
+  // Number of threads to use while processing the dex files.
+  public int numberOfThreads = NOT_SPECIFIED;
+  // Print smali disassembly.
+  public boolean useSmaliSyntax = false;
+  // Verbose output.
+  public boolean verbose = false;
+  // Silencing output.
+  public boolean quiet = false;
+  // Eagerly fill dex files as much as possible.
+  public boolean fillDexFiles = false;
+
+  public List<String> methodsFilter = ImmutableList.of();
+  public int minApiLevel = Constants.DEFAULT_ANDROID_API;
+
+  // Defines interface method rewriter behavior.
+  public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off;
+
+  public boolean useTreeShaking = true;
+
+  public boolean printCfg = false;
+  public String printCfgFile;
+  public boolean printSeeds;
+  public Path seedsFile;
+  public boolean printMapping;
+  public Path printMappingFile;
+  public boolean ignoreMissingClasses = false;
+  public boolean skipMinification = false;
+  public String packagePrefix = "";
+  public boolean allowAccessModification = true;
+  public boolean inlineAccessors = true;
+  public final OutlineOptions outline = new OutlineOptions();
+  public boolean debugKeepRules = false;
+  public final AttributeRemovalOptions attributeRemoval = new AttributeRemovalOptions();
+
+  public boolean debug = false;
+  public final TestingOptions testing = new TestingOptions();
+
+  // TODO(zerny): These stateful dictionaries do not belong here.
+  public List<String> classObfuscationDictionary = ImmutableList.of();
+  public List<String> obfuscationDictionary = ImmutableList.of();
+
+  public ImmutableList<ProguardConfigurationRule> keepRules = ImmutableList.of();
+  public ImmutableSet<ProguardTypeMatcher> dontWarnPatterns = ImmutableSet.of();
+
+  public String warningInvalidParameterAnnotations = null;
+
+  public boolean printWarnings() {
+    boolean printed = false;
+    if (warningInvalidParameterAnnotations != null) {
+      System.out.println("Warning: " + warningInvalidParameterAnnotations);
+      printed = true;
+    }
+    return printed;
+  }
+
+  public boolean methodMatchesFilter(DexEncodedMethod method) {
+    // Not specifying a filter matches all methods.
+    if (methodsFilter.size() == 0) {
+      return true;
+    }
+    // Currently the filter is simple string equality on the qualified name.
+    String qualifiedName = method.qualifiedName();
+    return methodsFilter.indexOf(qualifiedName) >= 0;
+  }
+
+  public static class OutlineOptions {
+
+    public boolean enabled = true;
+    public static final String className = "r8.GeneratedOutlineSupport";
+    public String methodPrefix = "outline";
+    public int minSize = 3;
+    public int maxSize = 99;
+    public int threshold = 20;
+  }
+
+  public static class TestingOptions {
+
+    public boolean randomizeCallGraphLeaves = false;
+  }
+
+  public static class AttributeRemovalOptions {
+
+    public static final String INNER_CLASSES = "InnerClasses";
+    public static final String ENCLOSING_METHOD = "EnclosingMethod";
+    public static final String SIGNATURE = "Signature";
+    public static final String EXCEPTIONS = "Exceptions";
+    public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
+    public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
+    public static final String RUNTIME_INVISBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
+    public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS =
+        "RuntimeVisibleParameterAnnotations";
+    public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
+        "RuntimeInvisibleParameterAnnotations";
+    public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
+    public static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS =
+        "RuntimeInvisibleTypeAnnotations";
+    public static final String ANNOTATION_DEFAULT = "AnnotationDefault";
+
+    public boolean innerClasses = false;
+    public boolean enclosingMethod = false;
+    public boolean signature = false;
+    public boolean exceptions = false;
+    public boolean sourceDebugExtension = false;
+    public boolean runtimeVisibleAnnotations = false;
+    public boolean runtimeInvisibleAnnotations = false;
+    public boolean runtimeVisibleParameterAnnotations = false;
+    public boolean runtimeInvisibleParamterAnnotations = false;
+    public boolean runtimeVisibleTypeAnnotations = false;
+    public boolean runtimeInvisibleTypeAnnotations = false;
+    public boolean annotationDefault = false;
+
+    private AttributeRemovalOptions() {
+
+    }
+
+    public static AttributeRemovalOptions filterOnlySignatures() {
+      AttributeRemovalOptions result = new AttributeRemovalOptions();
+      result.applyPattern("*");
+      result.signature = false;
+      return result;
+    }
+
+    /**
+     * Implements ProGuards attribute matching rules.
+     *
+     * @see <a href="https://www.guardsquare.com/en/proguard/manual/attributes">ProGuard manual</a>.
+     */
+    private boolean update(boolean previous, String text, String[] patterns) {
+      for (String pattern : patterns) {
+        if (previous) {
+          return true;
+        }
+        if (pattern.charAt(0) == '!') {
+          if (matches(pattern, 1, text, 0)) {
+            break;
+          }
+        } else {
+          previous = matches(pattern, 0, text, 0);
+        }
+      }
+      return previous;
+    }
+
+    private boolean matches(String pattern, int patternPos, String text, int textPos) {
+      while (patternPos < pattern.length()) {
+        char next = pattern.charAt(patternPos++);
+        if (next == '*') {
+          while (textPos < text.length()) {
+            if (matches(pattern, patternPos, text, textPos++)) {
+              return true;
+            }
+          }
+          return patternPos >= pattern.length();
+        } else {
+          if (textPos >= text.length() || text.charAt(textPos) != next) {
+            return false;
+          }
+          textPos++;
+        }
+      }
+      return textPos == text.length();
+    }
+
+    public void applyPattern(String pattern) {
+      String[] patterns = pattern.split(",");
+      innerClasses = update(innerClasses, INNER_CLASSES, patterns);
+      enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns);
+      signature = update(signature, SIGNATURE, patterns);
+      exceptions = update(exceptions, EXCEPTIONS, patterns);
+      sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns);
+      runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS,
+          patterns);
+      runtimeInvisibleAnnotations = update(runtimeInvisibleAnnotations,
+          RUNTIME_INVISBLE_ANNOTATIONS, patterns);
+      runtimeVisibleParameterAnnotations = update(runtimeVisibleParameterAnnotations,
+          RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, patterns);
+      runtimeInvisibleParamterAnnotations = update(runtimeInvisibleParamterAnnotations,
+          RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, patterns);
+      runtimeVisibleTypeAnnotations = update(runtimeVisibleTypeAnnotations,
+          RUNTIME_VISIBLE_TYPE_ANNOTATIONS, patterns);
+      runtimeInvisibleTypeAnnotations = update(runtimeInvisibleTypeAnnotations,
+          RUNTIME_INVISIBLE_TYPE_ANNOTATIONS, patterns);
+      annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns);
+    }
+
+    public void ensureValid(boolean isMinifying) {
+      if (innerClasses && !enclosingMethod) {
+        throw new CompilationError("Attribute InnerClasses implies EnclosingMethod attribute. " +
+            "Check -keepattributes directive.");
+      } else if (!innerClasses && enclosingMethod) {
+        throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. " +
+            "Check -keepattributes directive.");
+      } else if (signature && isMinifying) {
+        // TODO(38188583): Allow this once we can minify signatures.
+        throw new CompilationError("Attribute Signature cannot be kept when minifying. " +
+            "Check -keepattributes directive.");
+      }
+    }
+  }
+
+  public boolean canUseInvokePolymorphic() {
+    return minApiLevel >= Constants.ANDROID_O_API;
+  }
+
+  public boolean canUseInvokeCustom() {
+    return minApiLevel >= Constants.ANDROID_O_API;
+  }
+
+  public boolean canUseDefaultAndStaticInterfaceMethods() {
+    return minApiLevel >= Constants.ANDROID_N_API;
+  }
+
+  public boolean canUseObjectsNonNull() {
+    return minApiLevel >= Constants.ANDROID_K_API;
+  }
+
+  public boolean canUsePrivateInterfaceMethods() {
+    return minApiLevel >= Constants.ANDROID_N_API;
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalResource.java b/src/main/java/com/android/tools/r8/utils/InternalResource.java
new file mode 100644
index 0000000..14656e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/InternalResource.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.Resource;
+import com.google.common.io.Closer;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+
+/** Internal resource class that is not intended for use from the outside.
+ *
+ * <p>This is only here to hide the creation and class descriptor methods
+ * from the javadoc for the D8 API. If we decide to expose those methods
+ * later the split between Resource and InternalResource can be removed.
+ */
+public abstract class InternalResource extends Resource {
+
+  private InternalResource(Kind kind) {
+    super(kind);
+  }
+
+  /** Returns class descriptor if known, otherwise returns null. */
+  public abstract String getClassDescriptor();
+
+  /** Create an application resource for a given file and kind. */
+  public static InternalResource fromFile(Kind kind, Path file) {
+    return new FileResource(kind, file);
+  }
+
+  /** Create an application resource for a given content and kind. */
+  public static InternalResource fromBytes(Kind kind, byte[] bytes) {
+    return fromBytes(kind, bytes, null);
+  }
+
+  /** Create an application resource for a given content, kind and type descriptor. */
+  public static InternalResource fromBytes(Kind kind, byte[] bytes, String typeDescriptor) {
+    return new ByteResource(kind, bytes, typeDescriptor);
+  }
+
+  /** File based application resource. */
+  private static class FileResource extends InternalResource {
+    final Path file;
+
+    FileResource(Kind kind, Path file) {
+      super(kind);
+      assert file != null;
+      this.file = file;
+    }
+
+    @Override
+    public String getClassDescriptor() {
+      return null;
+    }
+
+    @Override
+    public InputStream getStream(Closer closer) throws IOException {
+      return closer.register(new FileInputStream(file.toFile()));
+    }
+  }
+
+  /** Byte content based application resource. */
+  private static class ByteResource extends InternalResource {
+    final String classDescriptor;
+    final byte[] bytes;
+
+    ByteResource(Kind kind, byte[] bytes, String classDescriptor) {
+      super(kind);
+      assert bytes != null;
+      this.classDescriptor = classDescriptor;
+      this.bytes = bytes;
+    }
+
+    @Override
+    public String getClassDescriptor() {
+      return classDescriptor;
+    }
+
+    @Override
+    public InputStream getStream(Closer closer) throws IOException {
+      // Note: closing a byte-array input stream is a no-op.
+      return new ByteArrayInputStream(bytes);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/LebUtils.java b/src/main/java/com/android/tools/r8/utils/LebUtils.java
new file mode 100644
index 0000000..d4d3b1f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/LebUtils.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.DexFile;
+import com.android.tools.r8.dex.DexOutputBuffer;
+import java.util.Arrays;
+
+public class LebUtils {
+  private static final int BITS_PER_ENCODED_BYTE = 7;
+  private static final int PAYLOAD_MASK = 0x7f;
+  private static final int MORE_DATA_TAG_BIT = 0x80;
+  private static final int MAX_BYTES_PER_VALUE = 5;
+
+  public static int parseUleb128(DexFile file) {
+    int result = 0;
+    byte b;
+    int shift = 0;
+    do {
+      b = file.get();
+      result |= (b & (byte) PAYLOAD_MASK) << shift;
+      shift += BITS_PER_ENCODED_BYTE;
+    } while ((b & ~(byte) PAYLOAD_MASK) == ~(byte) PAYLOAD_MASK);
+    assert shift <= MAX_BYTES_PER_VALUE * BITS_PER_ENCODED_BYTE;  // At most five bytes are used.
+    assert result >= 0;  // Ensure the java int didn't overflow.
+    return result;
+  }
+
+  // Inspired by com.android.dex.Leb128.java
+  public static byte[] encodeUleb128(int value) {
+    byte result[] = new byte[MAX_BYTES_PER_VALUE];
+    int remaining = value >>> BITS_PER_ENCODED_BYTE;
+    int bytes = 0;
+    while (remaining != 0) {
+      result[bytes++] = (byte) ((value & PAYLOAD_MASK) | MORE_DATA_TAG_BIT);
+      value = remaining;
+      remaining >>>= BITS_PER_ENCODED_BYTE;
+    }
+    result[bytes++] = (byte) (value & PAYLOAD_MASK);
+    return Arrays.copyOf(result, bytes);
+  }
+
+  // Inspired by com.android.dex.Leb128.java
+  public static void putUleb128(DexOutputBuffer outputBuffer, int value) {
+    int remaining = value >>> BITS_PER_ENCODED_BYTE;
+    while (remaining != 0) {
+      outputBuffer.putByte((byte) ((value & PAYLOAD_MASK) | MORE_DATA_TAG_BIT));
+      value = remaining;
+      remaining >>>= BITS_PER_ENCODED_BYTE;
+    }
+    outputBuffer.putByte((byte) (value & PAYLOAD_MASK));
+  }
+
+  public static int sizeAsUleb128(int value) {
+    return Math
+        .max(1, (Integer.SIZE - Integer.numberOfLeadingZeros(value) + 6) / BITS_PER_ENCODED_BYTE);
+  }
+
+  public static int parseSleb128(DexFile file) {
+    int result = 0;
+    byte b;
+    int shift = 0;
+    do {
+      b = file.get();
+      result |= (b & (byte) PAYLOAD_MASK) << shift;
+      shift += BITS_PER_ENCODED_BYTE;
+    } while ((b & ~(byte) PAYLOAD_MASK) == ~(byte) PAYLOAD_MASK);
+    int mask = 1 << (shift - 1);
+    assert shift <= MAX_BYTES_PER_VALUE * BITS_PER_ENCODED_BYTE;  // At most five bytes are used.
+    return (result ^ mask) - mask;
+  }
+
+  // Inspired by com.android.dex.Leb128.java
+  public static byte[] encodeSleb128(int value) {
+    byte result[] = new byte[MAX_BYTES_PER_VALUE];
+    int remaining = value >> BITS_PER_ENCODED_BYTE;
+    boolean hasMore = true;
+    int end = value >= 0 ? 0 : -1;
+    int bytes = 0;
+    while (hasMore) {
+      hasMore = (remaining != end)
+          || ((remaining & 1) != ((value >> 6) & 1));
+      result[bytes++] = (byte) ((value & PAYLOAD_MASK) | (hasMore ? MORE_DATA_TAG_BIT : 0));
+      value = remaining;
+      remaining >>= BITS_PER_ENCODED_BYTE;
+    }
+    return Arrays.copyOf(result, bytes);
+  }
+
+  // Inspired by com.android.dex.Leb128.java
+  public static void putSleb128(DexOutputBuffer outputBuffer, int value) {
+    int remaining = value >> BITS_PER_ENCODED_BYTE;
+    boolean hasMore = true;
+    int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
+    while (hasMore) {
+      hasMore = (remaining != end)
+          || ((remaining & 1) != ((value >> 6) & 1));
+      outputBuffer.putByte((byte) ((value & PAYLOAD_MASK) | (hasMore ? MORE_DATA_TAG_BIT : 0)));
+      value = remaining;
+      remaining >>= BITS_PER_ENCODED_BYTE;
+    }
+  }
+
+  public static int sizeAsSleb128(int value) {
+    if (value < 0) {
+      value = ~value;
+    }
+    // Note the + 7 to account for the extra bit on 7-bit boundaries.
+    return (Integer.SIZE - Integer.numberOfLeadingZeros(value) + 7) / BITS_PER_ENCODED_BYTE;
+  }
+
+  public static byte[] encodeUleb128p1(int value) {
+    return encodeUleb128(value + 1);
+  }
+
+  public static byte[][] encodeUleb128p1(int[] values) {
+    byte[][] result = new byte[values.length][];
+    for (int i = 0; i < result.length; i++) {
+      result[i] = encodeUleb128p1(values[i]);
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
new file mode 100644
index 0000000..a2ea508
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+
+public class ListUtils {
+
+  public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
+    List<T> result = new ArrayList<>(list.size());
+    for (S element : list) {
+      result.add(fn.apply(element));
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/LongInterval.java b/src/main/java/com/android/tools/r8/utils/LongInterval.java
new file mode 100644
index 0000000..9c987fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/LongInterval.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+/**
+ * Closed interval of two longs.
+ */
+public class LongInterval {
+
+  private final long min;
+  private final long max;
+
+  public LongInterval(int min, int max) {
+    assert min <= max;
+    this.min = min;
+    this.max = max;
+  }
+
+  public LongInterval(long min, long max) {
+    assert min <= max;
+    this.min = min;
+    this.max = max;
+  }
+
+  public long getMin() {
+    return min;
+  }
+
+  public long getMax() {
+    return max;
+  }
+
+  public boolean isSingleValue() {
+    return min == max;
+  }
+
+  public long getSingleValue() {
+    assert isSingleValue();
+    return min;
+  }
+
+  public boolean containsValue(long value) {
+    return min <= value && value <= max;
+  }
+
+  public boolean doesntOverlapWith(LongInterval other) {
+    return other.max < min || max < other.min;
+  }
+
+  public boolean overlapsWith(LongInterval other) {
+    return other.max >= min && max >= other.min;
+  }
+
+  @Override
+  public String toString() {
+    return "[" + min + ", " + max + "]";
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/MainDexList.java b/src/main/java/com/android/tools/r8/utils/MainDexList.java
new file mode 100644
index 0000000..b219314
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MainDexList.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR;
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.Sets;
+import com.google.common.io.Closer;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+
+public class MainDexList {
+
+  public static Set<DexType> parse(Path path, DexItemFactory itemFactory) throws IOException {
+    try (Closer closer = Closer.create()) {
+      return parse(closer.register(Files.newInputStream(path)), itemFactory);
+    }
+  }
+
+  public static Set<DexType> parse(InputStream input, DexItemFactory itemFactory) {
+    Set<DexType> result = Sets.newIdentityHashSet();
+    try {
+      BufferedReader file =
+          new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
+      String line;
+      while ((line = file.readLine()) != null) {
+        if (!line.endsWith(CLASS_EXTENSION)) {
+          throw new CompilationError("Illegal main-dex-list entry '" + line + "'.");
+        }
+        String name = line.substring(0, line.length() - CLASS_EXTENSION.length());
+        if (name.contains("" + JAVA_PACKAGE_SEPARATOR)) {
+          throw new CompilationError("Illegal main-dex-list entry '" + line + "'.");
+        }
+        String descriptor = "L" + name + ";";
+        result.add(itemFactory.createType(descriptor));
+      }
+    } catch (IOException e) {
+      throw new CompilationError("Cannot load main-dex-list.");
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java b/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java
new file mode 100644
index 0000000..86ce165
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/MethodSignatureEquivalence.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.base.Equivalence;
+
+/**
+ * Implements an equivalence on {@link DexEncodedMethod} that does not take the holder into
+ * account.
+ *
+ * <p>Useful when comparing method implementations by their signature only.
+ */
+public class MethodSignatureEquivalence extends Equivalence<DexMethod> {
+
+  private static final MethodSignatureEquivalence THEINSTANCE = new MethodSignatureEquivalence();
+
+  private MethodSignatureEquivalence() {
+  }
+
+  public static MethodSignatureEquivalence get() {
+    return THEINSTANCE;
+  }
+
+  @Override
+  protected boolean doEquivalent(DexMethod a, DexMethod b) {
+    return a.name.equals(b.name) && a.proto.equals(b.proto);
+  }
+
+  @Override
+  protected int doHash(DexMethod method) {
+    return method.name.hashCode() * 31 + method.proto.hashCode();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/NumberUtils.java b/src/main/java/com/android/tools/r8/utils/NumberUtils.java
new file mode 100644
index 0000000..ca5dc22
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/NumberUtils.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.dex.Constants;
+
+public class NumberUtils {
+
+  public static boolean is4Bit(long value) {
+    return Constants.S4BIT_MIN <= value && value <= Constants.S4BIT_MAX;
+  }
+
+  public static boolean is8Bit(long value) {
+    return Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE;
+  }
+
+  public static boolean negativeIs8Bit(long value) {
+    return Byte.MIN_VALUE <= -value && -value <= Byte.MAX_VALUE;
+  }
+
+  public static boolean is16Bit(long value) {
+    return Short.MIN_VALUE <= value && value <= Short.MAX_VALUE;
+  }
+
+  public static boolean negativeIs16Bit(long value) {
+    return Short.MIN_VALUE <= -value && -value <= Short.MAX_VALUE;
+  }
+
+  public static boolean is32Bit(long value) {
+    return Integer.MIN_VALUE <= value && value <= Integer.MAX_VALUE;
+  }
+
+  public static boolean isIntHigh16Bit(long value) {
+    return (value & ~(0xffff << 16)) == 0;
+  }
+
+  public static boolean isLongHigh16Bit(long value) {
+    return (value & ~(0xffffL << 48)) == 0;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OffOrAuto.java b/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
new file mode 100644
index 0000000..83d6244
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+
+public enum OffOrAuto {
+  Off, Auto;
+
+  static final ValueConverter<OffOrAuto> CONVERTER = new ValueConverter<OffOrAuto>() {
+    @Override
+    public OffOrAuto convert(String input) {
+      try {
+        input = Character.toUpperCase(input.charAt(0)) + input.substring(1).toLowerCase();
+        return Enum.valueOf(OffOrAuto.class, input);
+      } catch (Exception e) {
+        throw new ValueConversionException("Value must be one of: " + valuePattern());
+      }
+    }
+
+    @Override
+    public Class<OffOrAuto> valueType() {
+      return OffOrAuto.class;
+    }
+
+    @Override
+    public String valuePattern() {
+      return "off|auto";
+    }
+  };
+}
diff --git a/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java b/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java
new file mode 100644
index 0000000..dd4c73a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OrderedMergingIterator.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.InternalCompilerError;
+import com.android.tools.r8.graph.KeyedDexItem;
+import com.android.tools.r8.graph.PresortedComparable;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class OrderedMergingIterator<T extends KeyedDexItem<S>, S extends PresortedComparable<S>>
+    implements Iterator<T> {
+
+  private final T[] one;
+  private final T[] other;
+  private int oneIndex = 0;
+  private int otherIndex = 0;
+
+  public OrderedMergingIterator(T[] one, T[] other) {
+    this.one = one;
+    this.other = other;
+  }
+
+  private static <T> T getNextChecked(T[] array, int position) {
+    if (position >= array.length) {
+      throw new NoSuchElementException();
+    }
+    return array[position];
+  }
+
+  @Override
+  public boolean hasNext() {
+    return oneIndex < one.length || otherIndex < other.length;
+  }
+
+  @Override
+  public T next() {
+    if (oneIndex >= one.length) {
+      return getNextChecked(other, otherIndex++);
+    }
+    if (otherIndex >= other.length) {
+      return getNextChecked(one, oneIndex++);
+    }
+    int comparison = one[oneIndex].getKey().compareTo(other[otherIndex].getKey());
+    if (comparison < 0) {
+      return one[oneIndex++];
+    }
+    if (comparison == 0) {
+      throw new InternalCompilerError("Source arrays are not disjoint.");
+    }
+    return other[otherIndex++];
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/PackageDistribution.java b/src/main/java/com/android/tools/r8/utils/PackageDistribution.java
new file mode 100644
index 0000000..6d36bc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/PackageDistribution.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class PackageDistribution {
+
+  private static final String OLDFILE_PREFIX_TEXT =
+      "\n"
+          + "# Below follow the original package to file mapping rules. These have not been\n"
+          + "# changed by R8.\n"
+          + "\n";
+
+  private static final String APPENDED_PREFIX_TEXT =
+      "# The following packages had no mapping in the supplied package file. The\n"
+          + "# mapping rules provided below reflect the mapping that was used by R8. Please\n"
+          + "# use this updated map moving forward to ensure stability of package placement\n"
+          + "# in DEX files (and thus minimize patch size).\n"
+          + "#\n"
+          + "# Note that the updated package placement might not be optimal. Shifting the new\n"
+          + "# packages to DEX files that contain related packages might yield smaller DEX\n"
+          + "# file sizes.\n"
+          + "\n";
+
+  private static final String NEW_PACKAGE_MAP_PREFIX_TEXT =
+      "# This file provides a mapping of classes to DEX files in an Android multi-dex\n"
+          +"# application. It is used in conjunction with the R8 DEX file optimizer\n"
+          + "# to enforce a fixed distribution of classes to DEX files.\n"
+          + "#\n"
+          + "# Fixing the class distribution serves two purposes:\n"
+          + "#\n"
+          + "# 1. Keeping classes in the same DEX file reduces the size of patches between\n"
+          + "#    two versions of an application.\n"
+          + "# 2. Co-locating classes with their uses can reduce DEX file size. For example,\n"
+          + "#    one might want to place the helper classes for credit card processing in\n"
+          + "#    the same DEX file that contains the payment related logic.\n"
+          + "#\n"
+          + "# Entries in this file have the following form:\n"
+          + "#\n"
+          + "# <packageSpec>:<file number>\n"
+          + "#\n"
+          + "# Where packageSpec is either the name of a package, e.g., 'com.google.foo', or\n"
+          + "# a package wildcard of the form 'com.google.bar.*'. The former matches exactly\n"
+          + "# the classes in the given package, whereas the latter also matches classes in\n"
+          + "# subpackages. PackageSpec entries may not overlap.\n"
+          + "#\n"
+          + "# Empty lines and lines starting with a '#' are ignored.\n"
+          + "\n";
+
+  private static final String NO_PACKAGE_MAP_REQUIRED_TEXT =
+      "\n"
+          +  "# Intentionally empty, as the output only has a single DEX file.\n"
+          + "\n";
+
+  private final Map<String, Integer> map;
+
+  private PackageDistribution(Map<String, Integer> map) {
+    this.map = map;
+  }
+
+  public static PackageDistribution load(InputStream input) throws IOException {
+    return read(new BufferedReader(new InputStreamReader(input)));
+  }
+
+  public static PackageDistribution load(Path path) {
+    try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
+      return read(reader);
+    } catch (IOException e) {
+      throw new RuntimeException("Error reading file " + path, e);
+    }
+  }
+
+  private static PackageDistribution read(BufferedReader reader) throws IOException {
+    String line = null;
+    try {
+      Map<String, Integer> result = new HashMap<>();
+      while ((line = reader.readLine()) != null) {
+        if (line.length() == 0 || line.startsWith("#")) {
+          continue;
+        }
+        String[] parts = line.split(":");
+        if (parts.length != 2) {
+          throw new RuntimeException("Error parsing package map line " + line);
+        }
+        String prefix = parts[0];
+        if (result.containsKey(prefix)) {
+          throw new RuntimeException("Prefix is assigned twice: " + prefix);
+        }
+        int file = Integer.parseInt(parts[1]);
+        result.put(prefix, file);
+      }
+      return new PackageDistribution(result);
+    } catch (NumberFormatException e) {
+      throw new RuntimeException("Error parsing package map line " + line, e);
+    }
+  }
+
+  public static void formatEntry(Entry<String, Integer> entry, Writer writer) throws IOException {
+    writer.write(entry.getKey());
+    writer.write(":");
+    writer.write(entry.getValue().toString());
+  }
+
+  public static void writePackageToFileMap(
+      Path target, Map<String, Integer> mappings, PackageDistribution original) throws IOException {
+    BufferedWriter writer = Files.newBufferedWriter(target, StandardCharsets.UTF_8);
+    if (mappings.isEmpty()) {
+      if (original != null) {
+        copyOriginalPackageMap(original, writer);
+      } else {
+        writer.write(NEW_PACKAGE_MAP_PREFIX_TEXT);
+        writer.write(NO_PACKAGE_MAP_REQUIRED_TEXT);
+      }
+      writer.close();
+      return;
+    }
+    if (original == null) {
+      writer.write(NEW_PACKAGE_MAP_PREFIX_TEXT);
+    } else {
+      writer.write(APPENDED_PREFIX_TEXT);
+    }
+    for (Entry<String, Integer> entry : mappings.entrySet()) {
+      formatEntry(entry, writer);
+      writer.newLine();
+    }
+    if (original != null) {
+      // Copy the original
+      writer.write(OLDFILE_PREFIX_TEXT);
+      copyOriginalPackageMap(original, writer);
+    }
+    writer.close();
+  }
+
+  private static void copyOriginalPackageMap(PackageDistribution original, BufferedWriter writer)
+      throws IOException {
+    for (Entry<String, Integer> entry : original.map.entrySet()) {
+      formatEntry(entry, writer);
+      writer.newLine();
+    }
+  }
+
+  public int maxReferencedIndex() {
+    return map.values().stream().max(Integer::compare).orElseGet(() -> 0);
+  }
+
+  public Set<String> getFiles() {
+    return map.keySet();
+  }
+
+  public int get(String file) {
+    return map.getOrDefault(file, -1);
+  }
+
+  public boolean containsFile(String file) {
+    return map.containsKey(file);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java b/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java
new file mode 100644
index 0000000..05481c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SimpleHashMap.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+abstract class SimpleHashMap {
+
+  private int size = 0;  // number of elements in this hash map.
+  private int limit;  // resize when size reaches limit.
+  private int mask;  // length - 1.
+
+  // Constants for capacity.
+  static final int MIN_CAPACITY = 2;
+  static final int DEFAULT_CAPACITY = 50;
+
+  // Constant for loadfactor.
+  static final double MIN_LOAD_FACTOR = 0.2;
+  static final double MAX_LOAD_FACTOR = 0.8;
+  static final double DEFAULT_LOAD_FACTOR = 0.6;
+
+  SimpleHashMap() {
+    this(DEFAULT_CAPACITY);
+  }
+
+  SimpleHashMap(int initialCapacity) {
+    this(initialCapacity, DEFAULT_LOAD_FACTOR);
+  }
+
+  SimpleHashMap(int initialCapacity, double loadFactor) {
+    initialCapacity = Math.max(MIN_CAPACITY, initialCapacity);
+    loadFactor = Math.min(MAX_LOAD_FACTOR, Math.max(MIN_LOAD_FACTOR, loadFactor));
+    final int initialLength = roundToPow2((int) ((double) initialCapacity / loadFactor));
+    initialize(initialLength, (int) ((double) initialLength * loadFactor));
+  }
+
+  public int size() {
+    return size;
+  }
+
+  public String toString() {
+    return this.getClass().getName() + ", " + size + "(length " + length() + ")";
+  }
+
+  int length() {
+    return mask + 1;
+  }
+
+  void initialize(final int length, final int limit) {
+    size = 0;
+    mask = length - 1;
+    this.limit = limit;
+  }
+
+  void resize() {
+    // Double length and limit.
+    initialize(length() << 1, limit << 1);
+  }
+
+  void ensureCapacity() {
+    if (size >= limit) {
+      resize();
+    }
+  }
+
+  void incrementSize() {
+    size++;
+  }
+
+  private int roundToPow2(int number) {
+    number--;
+    number |= number >> 1;
+    number |= number >> 2;
+    number |= number >> 4;
+    number |= number >> 8;
+    number |= number >> 16;
+    return number + 1;
+  }
+
+  int firstProbe(final int hash) {
+    return hash & mask;
+  }
+
+  int nextProbe(final int last, final int index) {
+    return (last + index) & mask;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
new file mode 100644
index 0000000..268228f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -0,0 +1,205 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.errors.Unreachable;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Function;
+
+public class StringUtils {
+
+  private final static char[] IDENTIFIER_LETTERS
+      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".toCharArray();
+  private final static int NUMBER_OF_LETTERS = IDENTIFIER_LETTERS.length;
+
+  public enum BraceType {
+    PARENS,
+    SQUARE,
+    TUBORG,
+    NONE;
+
+    public String left() {
+      switch (this) {
+        case PARENS: return "(";
+        case SQUARE: return "[";
+        case TUBORG: return "{";
+        case NONE: return "";
+        default: throw new Unreachable("Invalid brace type: " + this);
+      }
+    }
+
+    public String right() {
+      switch (this) {
+        case PARENS: return ")";
+        case SQUARE: return "]";
+        case TUBORG: return "}";
+        case NONE: return "";
+        default: throw new Unreachable("Invalid brace type: " + this);
+      }
+    }
+  }
+
+  public static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+    if (item == null) {
+      return;
+    }
+    String text = item.toString();
+    if (!text.isEmpty()) {
+      if (pre != null) {
+        builder.append(pre);
+      }
+      builder.append(text);
+      if (post != null) {
+        builder.append(post);
+      }
+    }
+  }
+
+  public static StringBuilder appendIndent(StringBuilder builder, String subject, int indent) {
+    for (int i = 0; i < indent; i++) {
+      builder.append(" ");
+    }
+    builder.append(subject);
+    return builder;
+  }
+
+  public static StringBuilder appendLeftPadded(StringBuilder builder, String subject, int width) {
+    for (int i = subject.length(); i < width; i++) {
+      builder.append(" ");
+    }
+    builder.append(subject);
+    return builder;
+  }
+
+  public static StringBuilder appendRightPadded(StringBuilder builder, String subject, int width) {
+    builder.append(subject);
+    for (int i = subject.length(); i < width; i++) {
+      builder.append(" ");
+    }
+    return builder;
+  }
+
+  public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection) {
+    return append(builder, collection, ", ", BraceType.PARENS);
+  }
+
+  public static <T> StringBuilder append(StringBuilder builder, Collection<T> collection,
+      String seperator, BraceType brace) {
+    builder.append(brace.left());
+    boolean first = true;
+    for (T element : collection) {
+      if (first) {
+        first = false;
+      } else {
+        builder.append(seperator);
+      }
+      builder.append(element);
+    }
+    builder.append(brace.right());
+    return builder;
+  }
+
+  public static <T> String join(Collection<T> collection, String separator) {
+    return join(collection, separator, BraceType.NONE);
+  }
+
+  public static String join(String separator, String... strings) {
+    return join(Arrays.asList(strings), separator, BraceType.NONE);
+  }
+
+  public static <T> String join(Collection<T> collection, String separator, BraceType brace) {
+    return join(collection, separator, brace, Object::toString);
+  }
+
+  public static <T> String join(Collection<T> collection, String separator, BraceType brace,
+      Function<T, String> fn) {
+    StringBuilder builder = new StringBuilder();
+    append(builder, ListUtils.map(collection, fn), separator, brace);
+    return builder.toString();
+  }
+
+  public static String zeroPrefix(int i, int width) {
+    return zeroPrefixString(Integer.toString(i), width);
+  }
+
+  private static String zeroPrefixString(String s, int width) {
+    String prefix = "0000000000000000";
+    assert(width <= prefix.length());
+    int prefixLength = width - s.length();
+    if (prefixLength > 0) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(prefix, 0, prefixLength);
+      builder.append(s);
+      return builder.toString();
+    } else {
+      return s;
+    }
+  }
+
+  public static String hexString(int value, int width) {
+    assert(0 <= width && width <= 8);
+    String hex = Integer.toHexString(value);
+    if (value >= 0) {
+      return zeroPrefixString(hex, width);
+    } else {
+      // Negative ints are always formatted as 8 characters.
+      assert(hex.length() == 8);
+      return hex;
+    }
+  }
+
+  public static String hexString(long value, int width) {
+    assert(0 <= width && width <= 16);
+    String hex = Long.toHexString(value);
+    if (value >= 0) {
+      return zeroPrefixString(hex, width);
+    } else {
+      // Negative longs are always formatted as 16 characters.
+      assert(hex.length() == 16);
+      return hex;
+    }
+  }
+
+  public static String computeMD5Hash(String name) {
+    byte[] digest = null;
+    try {
+      MessageDigest m = MessageDigest.getInstance("MD5");
+      m.reset();
+      m.update(name.getBytes());
+      digest = m.digest();
+    } catch (NoSuchAlgorithmException e) {
+      throw new RuntimeException(e);
+    }
+    return Arrays.toString(digest);
+  }
+
+  public static String numberToIdentifier(char[] prefix, int nameCount, boolean addSemicolon) {
+    // TODO(herhut): Add support for using numbers.
+    int size = addSemicolon ? 1 : 0;
+    int number = nameCount;
+    while (number >= NUMBER_OF_LETTERS) {
+      number /= NUMBER_OF_LETTERS;
+      size++;
+    }
+    size++;
+    char characters[] = Arrays.copyOfRange(prefix, 0, prefix.length + size);
+    number = nameCount;
+
+    int i = prefix.length;
+    while (number >= NUMBER_OF_LETTERS) {
+      characters[i++] = IDENTIFIER_LETTERS[number % NUMBER_OF_LETTERS];
+      number /= NUMBER_OF_LETTERS;
+    }
+    characters[i++] = IDENTIFIER_LETTERS[number - 1];
+    if (addSemicolon) {
+      characters[i++] = ';';
+    }
+    assert i == characters.length;
+
+    return new String(characters);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
new file mode 100644
index 0000000..06a87aa
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class ThreadUtils {
+
+  public static <T> List<T> awaitFutures(List<? extends Future<? extends T>> futures)
+      throws ExecutionException {
+    ArrayList<T> result = new ArrayList<>(futures.size());
+    for (Future<? extends T> f : futures) {
+      try {
+        result.add(f.get());
+      } catch (InterruptedException e) {
+        throw new RuntimeException("Interrupted while waiting for future.", e);
+      }
+    }
+    return result;
+  }
+
+  public static ExecutorService getExecutorService(int threads) {
+    if (threads == 1) {
+      return Executors.newSingleThreadExecutor();
+    } else {
+      return Executors.newWorkStealingPool(threads);
+    }
+  }
+
+  public static ExecutorService getExecutorService(InternalOptions options) {
+    if (options.numberOfThreads == options.NOT_SPECIFIED) {
+      // This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
+      int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
+      return Executors.newWorkStealingPool(threads);
+    } else if (options.numberOfThreads == 1) {
+      return Executors.newSingleThreadExecutor();
+    } else {
+      return Executors.newWorkStealingPool(options.numberOfThreads);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/Timing.java b/src/main/java/com/android/tools/r8/utils/Timing.java
new file mode 100644
index 0000000..0fa316e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/Timing.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+// Helper for collecting timing information during execution.
+// Timing t = new Timing("R8");
+// A timing tree is collected by calling the following pair (nesting will create the tree):
+//     t.begin("My task);
+//     try { ... } finally { t.end(); }
+// or alternatively:
+//     t.scope("My task", () -> { ... });
+// Finally a report is printed by:
+//     t.report();
+
+import java.util.Stack;
+
+public class Timing {
+
+  private final Stack<Node> stack;
+
+  public Timing(String title) {
+    stack = new Stack<>();
+    stack.push(new Node("Recorded timings for " + title));
+  }
+
+  static class Node {
+    final String title;
+
+    final Stack<Node> sons = new Stack<>();
+    final long start_time;
+    long stop_time;
+
+    Node(String title) {
+      this.title = title;
+      this.start_time = System.nanoTime();
+      this.stop_time = -1;
+    }
+
+    void end() {
+      stop_time = System.nanoTime();
+      assert duration() >= 0;
+    }
+
+    long duration() {
+      return stop_time - start_time;
+    }
+
+    public String toString() {
+      return title + ": " + (duration() / 1000000) + "ms.";
+    }
+
+    public String toString(Node top) {
+      if (this == top) return toString();
+      long percentage = duration() * 100 / top.duration();
+      return toString() + " (" + percentage + "%)";
+    }
+
+    public void report(int depth, Node top) {
+      assert duration() >= 0;
+      if (depth > 0) {
+        for (int i = 0; i < depth; i++) {
+          System.out.print("  ");
+        }
+        System.out.print("- ");
+      }
+      System.out.println(toString(top));
+      sons.forEach(p -> { p.report(depth + 1, top); });
+    }
+  }
+
+
+  public void begin(String title) {
+    Node n = new Node(title);
+    stack.peek().sons.add(n);
+    stack.push(n);
+  }
+
+  public void end() {
+    stack.peek().end();  // record time.
+    stack.pop();
+  }
+
+  public void report() {
+    Node top = stack.peek();
+    top.end();
+    System.out.println();
+    top.report(0, top);
+  }
+
+  public void scope(String title, TimingScope fn) {
+    begin(title);
+    try {
+      fn.apply();
+    } finally {
+      end();
+    }
+  }
+
+  public interface TimingScope {
+    void apply();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
new file mode 100644
index 0000000..59f0702
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.io.ByteStreams;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class ZipUtils {
+
+  public interface OnEntryHandler {
+    void onEntry(ZipEntry entry, ZipInputStream input) throws IOException;
+  }
+
+  public static void iter(String zipFile, OnEntryHandler handler) throws IOException {
+    try (ZipInputStream input = new ZipInputStream(new FileInputStream(zipFile))){
+      ZipEntry entry;
+      while ((entry = input.getNextEntry()) != null) {
+        handler.onEntry(entry, input);
+      }
+    }
+  }
+
+  public static List<File> unzip(String zipFile, File outDirectory) throws IOException {
+    return unzip(zipFile, outDirectory, (entry) -> true);
+  }
+
+  public static List<File> unzip(String zipFile, File outDirectory, Predicate<ZipEntry> filter)
+      throws IOException {
+    final List<File> outFiles = new ArrayList<>();
+      iter(zipFile, (entry, input) -> {
+        String name = entry.getName();
+        if (filter.test(entry)) {
+          File outFile = outDirectory.toPath().resolve(name).toFile();
+          FileOutputStream output = new FileOutputStream(outFile);
+          ByteStreams.copy(input, output);
+          outFiles.add(outFile);
+        }
+      });
+    return outFiles;
+  }
+}
diff --git a/src/test/debugTestResources/Arithmetic.java b/src/test/debugTestResources/Arithmetic.java
new file mode 100644
index 0000000..29ec2f2
--- /dev/null
+++ b/src/test/debugTestResources/Arithmetic.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Arithmetic {
+
+  public static void main(String[] args) {
+    bitwiseInts(12345, 54321);
+  }
+
+  public static void bitwiseInts(int x, int y) {
+    System.out.println(x & y);
+    System.out.println(x | y);
+    System.out.println(x ^ y);
+    System.out.println(~x);
+  }
+
+}
diff --git a/src/test/debugTestResources/Exceptions.java b/src/test/debugTestResources/Exceptions.java
new file mode 100644
index 0000000..a056c13
--- /dev/null
+++ b/src/test/debugTestResources/Exceptions.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Exceptions {
+
+  private static void catchException() {
+    try {
+      throwNullPointerException();
+    } catch (NullPointerException e) {
+      System.out.println("Caught NPE");
+    }
+  }
+
+  private static void throwNullPointerException() {
+    throw new NullPointerException();
+  }
+
+  public static void main(String[] args) {
+    catchException();
+  }
+}
diff --git a/src/test/debugTestResources/InnerAccessors.java b/src/test/debugTestResources/InnerAccessors.java
new file mode 100644
index 0000000..8507e79
--- /dev/null
+++ b/src/test/debugTestResources/InnerAccessors.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class InnerAccessors {
+
+  private static void privateMethod() {
+    System.out.println("I'm a private method");
+  }
+
+  static class Inner {
+    public void callPrivateMethodInOuterClass() {
+      privateMethod();
+    }
+  }
+
+  public static void main(String[] args) {
+    new Inner().callPrivateMethodInOuterClass();
+  }
+
+}
diff --git a/src/test/debugTestResources/Locals.java b/src/test/debugTestResources/Locals.java
new file mode 100644
index 0000000..21d08c1
--- /dev/null
+++ b/src/test/debugTestResources/Locals.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Locals {
+
+  private static void noLocals() {
+    System.out.println("There's no local here");
+  }
+
+  private static void unusedLocals() {
+    int i = Integer.MAX_VALUE;
+    System.out.println("Not using local variable");
+  }
+
+  private static void constantLocals(int p) {
+    int c = 5;
+    int v = c + p;
+    System.out.println("c=" + c + ", v=" + v);
+  }
+
+  private static void zeroLocals() {
+    int i = 0;
+    float f = 0.0f;
+    System.out.println("zeroLocals");
+  }
+
+  private static void noFlowOptimization() {
+    int i = 0;
+    if (i == 0) {
+      System.out.println("i == 0");
+    } else {
+      System.out.println("i != 0");
+    }
+  }
+
+  public static void main(String[] args) {
+    noLocals();
+    unusedLocals();
+    constantLocals(10);
+    zeroLocals();
+    noFlowOptimization();
+  }
+
+}
diff --git a/src/test/debugTestResources/MultipleReturns.java b/src/test/debugTestResources/MultipleReturns.java
new file mode 100644
index 0000000..7d6dc80
--- /dev/null
+++ b/src/test/debugTestResources/MultipleReturns.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class MultipleReturns {
+
+  public static void main(String[] args) {
+    int resultIfTrue = multipleReturns(true);
+    int resultIfFalse = multipleReturns(false);
+    System.out.println("resultIfTrue=" + resultIfTrue);
+    System.out.println("resultIfFalse=" + resultIfFalse);
+  }
+
+  public static int multipleReturns(boolean condition) {
+    if (condition) {
+      return Integer.MAX_VALUE;
+    } else {
+      return Integer.MIN_VALUE;
+    }
+  }
+
+
+}
diff --git a/src/test/examples/annotationremoval/Annotationremoval.java b/src/test/examples/annotationremoval/Annotationremoval.java
new file mode 100644
index 0000000..520c637
--- /dev/null
+++ b/src/test/examples/annotationremoval/Annotationremoval.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package annotationremoval;
+
+public class Annotationremoval {
+
+  public static void main(String... args) {
+    OuterClass instance = new OuterClass();
+    System.out.print(instance.getValueFromInner(123));
+  }
+}
diff --git a/src/test/examples/annotationremoval/OuterClass.java b/src/test/examples/annotationremoval/OuterClass.java
new file mode 100644
index 0000000..d783fc0
--- /dev/null
+++ b/src/test/examples/annotationremoval/OuterClass.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package annotationremoval;
+
+public class OuterClass {
+  public class InnerClass {
+    private int value;
+
+    public InnerClass(int x) {
+      this.value = x;
+    }
+
+    int computeAResult(int y) {
+      int result = 1;
+      for (int i = value; i < y; i++) {
+        result++;
+        if (result == 1) {
+          return result;
+        }
+      }
+      return value * y;
+    }
+  }
+
+  public abstract class MagicClass {
+    public abstract int returnAnInt();
+  }
+
+  public int getValueFromInner(int x) {
+    class LocalMagic extends MagicClass {
+
+      @Override
+      public int returnAnInt() {
+        return 123;
+      }
+    }
+
+    InnerClass inner = new InnerClass(x);
+    MagicClass magic = new MagicClass() {
+
+      @Override
+      public int returnAnInt() {
+        return 124;
+      }
+    };
+    MagicClass localMagic = new LocalMagic();
+    return inner.computeAResult(42) + magic.returnAnInt() + localMagic.returnAnInt();
+  }
+}
diff --git a/src/test/examples/annotationremoval/keep-rules-keep-innerannotation.txt b/src/test/examples/annotationremoval/keep-rules-keep-innerannotation.txt
new file mode 100644
index 0000000..94d5a60
--- /dev/null
+++ b/src/test/examples/annotationremoval/keep-rules-keep-innerannotation.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class annotationremoval.Annotationremoval {
+  public static void main(...);
+}
+
+-keepclassmembers class annotationremoval.OuterClass {
+  public int getValueFromInner(...);
+}
+
+-keepattributes InnerClasses,EnclosingMethod
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/annotationremoval/keep-rules.txt b/src/test/examples/annotationremoval/keep-rules.txt
new file mode 100644
index 0000000..7865eee
--- /dev/null
+++ b/src/test/examples/annotationremoval/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class annotationremoval.Annotationremoval {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/arithmetic/Arithmetic.java b/src/test/examples/arithmetic/Arithmetic.java
new file mode 100644
index 0000000..315aca4
--- /dev/null
+++ b/src/test/examples/arithmetic/Arithmetic.java
@@ -0,0 +1,166 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package arithmetic;
+
+import java.util.Arrays;
+
+public class Arithmetic {
+  static void addInts(int[] ints) {
+    int result = 0;
+    for (int i : ints) {
+      result += i;
+    }
+    System.out.println("addInts: " + Arrays.toString(ints) + " = " + result);
+  }
+
+  static void addDoubles(double[] doubles) {
+    double result = 0;
+    for (double d : doubles) {
+      result += d;
+    }
+    System.out.println("addDoubles: " + Arrays.toString(doubles) + " = " + result);
+  }
+
+  static void addLongs(long[] longs) {
+    long result = 0;
+    for (long l : longs) {
+      result += l;
+    }
+    System.out.println("addLongs: " + Arrays.toString(longs) + " = " + result);
+  }
+
+  static void binaryOps() {
+    int i = 0;
+    System.out.println("i values:");
+    i = i + 1;
+    System.out.println(i);
+    i = 1 + i;
+    System.out.println(i);
+    i = i * 4;
+    System.out.println(i);
+    i = i * i;
+    System.out.println(i);
+    i = 4 * i;
+    System.out.println(i);
+    i = i / 4;
+    System.out.println(i);
+    i = i / i;
+    System.out.println(i);
+    i = i % i;
+    System.out.println(i);
+
+    long l = 0;
+    System.out.println("l values:");
+    l = l + 1;
+    System.out.println(l);
+    l = 1 + l;
+    System.out.println(l);
+    l = l * 4;
+    System.out.println(l);
+    l = l * l;
+    System.out.println(l);
+    l = 4 * l;
+    System.out.println(l);
+    l = l / 4;
+    System.out.println(l);
+    l = l / l;
+    System.out.println(l);
+    l = l % l;
+    System.out.println(l);
+
+    double d = 0.0;
+    System.out.println("d values: ");
+    d = d + 1.0;
+    System.out.println(d);
+    d = 1.0 + d;
+    System.out.println(d);
+    d = d * 4.0;
+    System.out.println(d);
+    d = d * d;
+    System.out.println(d);
+    d = 4.0 * d;
+    System.out.println(d);
+    d = d / 4.0;
+    System.out.println(d);
+    d = d / d;
+    System.out.println(d);
+    d = d % d;
+    System.out.println(d);
+
+    float f = 0.0f;
+    System.out.println("f values: ");
+    f = f + 1.0f;
+    System.out.println(f);
+    f = 1.0f + f;
+    System.out.println(f);
+    f = f * 4.0f;
+    System.out.println(f);
+    f = f * f;
+    System.out.println(f);
+    f = 4.0f * f;
+    System.out.println(f);
+    f = f / 4.0f;
+    System.out.println(f);
+    f = f / f;
+    System.out.println(f);
+    f = f % f;
+    System.out.println(f);
+  }
+
+  public static void moreOps() {
+    int a = 42;
+    int b = -a;
+    int shiftLeftA = a << 5;
+    int shiftRightA = a >> 5;
+    int uShiftRightA = -a >>> 5;
+    System.out.println(a + b + shiftLeftA + shiftRightA + uShiftRightA);
+    float c = 42.42f;
+    float d = -c;
+    System.out.println(c + d);
+    double e = 43.43;
+    double f  = -e;
+    System.out.println(e + f);
+    long g = 5000000000L;
+    long h = -g;
+    long shiftLeftG = g << 8;
+    long shiftRightG = g >> 8;
+    long uShiftRightG = -g >>> 8;
+    System.out.println(g + h + shiftLeftG + shiftRightG + uShiftRightG);
+  }
+
+  public static void bitwiseInts(int x, int y) {
+    System.out.println(x & y);
+    System.out.println(x | y);
+    System.out.println(x ^ y);
+    System.out.println(~x);
+  }
+
+  public static void bitwiseLongs(long x, long y) {
+    System.out.println(x & y);
+    System.out.println(x | y);
+    System.out.println(x ^ y);
+    System.out.println(~x);
+  }
+
+  public static void main(String[] args) {
+    addInts(new int[] { });
+    addInts(new int[] { 1 });
+    addInts(new int[] { 0, 1, 2, 3 });
+    addDoubles(new double[] { 0.0 });
+    addDoubles(new double[] { 0.0, 1.0, 2.0 });
+    addDoubles(new double[] { 0.0, 1.0, 2.0, 3.0 });
+    long l = 0x0000000100000000L;
+    addLongs(new long[] { });
+    addLongs(new long[] { l });
+    addLongs(new long[] { l, l + 1, l + 2 });
+    binaryOps();
+    moreOps();
+    bitwiseInts(12345, 54321);
+    bitwiseLongs(54321, 12345);
+  }
+}
diff --git a/src/test/examples/arrayaccess/ArrayAccess.java b/src/test/examples/arrayaccess/ArrayAccess.java
new file mode 100644
index 0000000..f73fd91
--- /dev/null
+++ b/src/test/examples/arrayaccess/ArrayAccess.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package arrayaccess;
+
+public class ArrayAccess {
+
+  public static int loadStoreBoolean(int i, boolean b) {
+    boolean[] array = new boolean[i + 2];
+    array[i] = b;
+    array[i + 1] = !array[i];
+    return (array[i] ? 1 : 0) + (array[i + 1] ? 1 : 0);
+  }
+
+  public static int loadStoreByte(int i) {
+    byte[] array = new byte[i + 2];
+    array[i] = 1;
+    array[i + 1] = (byte) (array[i] + (byte) 1);
+    return array[i] + array[i + 1];
+  }
+
+  public static int loadStoreChar(int i) {
+    char[] array = new char[i + 2];
+    array[i] = 1;
+    array[i + 1] = (char) (array[i] + (char) 1);
+    return array[i] + array[i + 1];
+  }
+
+  public static int loadStoreShort(int i) {
+    short[] array = new short[i + 2];
+    array[i] = 1;
+    array[i + 1] = (short) (array[i] + (short) 1);
+    return array[i] + array[i + 1];
+  }
+
+  public static float loadStoreFloat(int i) {
+    float[] array = new float[i + 2];
+    array[i] = 1.0f;
+    array[i + 1] = array[i] + 1.0f;
+    return array[i] + array[i + 1];
+  }
+
+  public static double loadStoreDouble(int i) {
+    double[] array = new double[i + 2];
+    array[i] = 1.0;
+    array[i + 1] = array[i] + 1.0;
+    return array[i] + array[i + 1];
+  }
+
+  public static int loadStoreObject(int i, Object o) {
+    Object[] array = new Object[i + 2];
+    array[i] = o;
+    array[i + 1] = o;
+    return 1 + (array[i].hashCode() - array[i + 1].hashCode());
+  }
+
+  public static int loadStoreArray(int i, Object[] os) {
+    Object[][] array = new Object[i + 2][];
+    array[i] = os;
+    array[i + 1] = os;
+    return array[i].length + array[i + 1].length;
+  }
+
+  public static void main(String[] args) {
+    int i = 0;
+    i += loadStoreBoolean(1, true);
+    i += loadStoreByte(0);
+    i += loadStoreChar(1);
+    i += loadStoreShort(2);
+    i += loadStoreFloat(3);
+    i += loadStoreDouble(4);
+    i += loadStoreObject(1, "foo");
+    i += loadStoreArray(1, new Object[10]);
+    System.out.println("37=" + i);
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/assumenosideeffects1/Assumenosideeffects.java b/src/test/examples/assumenosideeffects1/Assumenosideeffects.java
new file mode 100644
index 0000000..24b85a0
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/Assumenosideeffects.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects1;
+
+public class Assumenosideeffects {
+
+  public static void main(String[] args) {
+    noSideEffectVoid();
+    noSideEffectInt();
+  }
+
+  @CheckDiscarded
+  public static void noSideEffectVoid() {
+    System.out.println("noSideEffectVoid");
+  }
+
+  @CheckDiscarded
+  public static int noSideEffectInt() {
+    System.out.println("noSideEffectInt");
+    return 0;
+  }
+}
diff --git a/src/test/examples/assumenosideeffects1/CheckDiscarded.java b/src/test/examples/assumenosideeffects1/CheckDiscarded.java
new file mode 100644
index 0000000..c34c95c
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects1;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects1/keep-rules-discard.txt b/src/test/examples/assumenosideeffects1/keep-rules-discard.txt
new file mode 100644
index 0000000..9c6b6f7
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects1.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects1.Assumenosideeffects {
+  public static void noSideEffectVoid(...);
+  public static int noSideEffectInt(...);
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+  @assumenosideeffects1.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects1/keep-rules.txt b/src/test/examples/assumenosideeffects1/keep-rules.txt
new file mode 100644
index 0000000..d82c218
--- /dev/null
+++ b/src/test/examples/assumenosideeffects1/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects1.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects1.Assumenosideeffects {
+  public static void noSideEffectVoid(...);
+  public static int noSideEffectInt(...);
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects2/Assumenosideeffects.java b/src/test/examples/assumenosideeffects2/Assumenosideeffects.java
new file mode 100644
index 0000000..ab49244
--- /dev/null
+++ b/src/test/examples/assumenosideeffects2/Assumenosideeffects.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects2;
+
+public class Assumenosideeffects {
+
+  public static void main(String[] args) {
+    System.out.println("Hello, world!");
+  }
+}
diff --git a/src/test/examples/assumenosideeffects2/keep-rules.txt b/src/test/examples/assumenosideeffects2/keep-rules.txt
new file mode 100644
index 0000000..68ad8e8
--- /dev/null
+++ b/src/test/examples/assumenosideeffects2/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects2.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark the println on java.io.PrintStream to have no side effects using a
+# wildcard (non-specific) rule.
+-assumenosideeffects public class java.io.PrintStr* {
+  public void println(...);
+}
+
+# Allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects3/Assumenosideeffects.java b/src/test/examples/assumenosideeffects3/Assumenosideeffects.java
new file mode 100644
index 0000000..b7aed82
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/Assumenosideeffects.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects3;
+
+public class Assumenosideeffects {
+
+  public static void main(String[] args) {
+    System.out.println(method0());
+    System.out.println(method1());
+    System.out.println(method0L() + "L");
+    System.out.println(method1L() + "L");
+  }
+
+  @CheckDiscarded
+  public static int method0() {
+    return 0;
+  }
+
+  @CheckDiscarded
+  public static int method1() {
+    return 1;
+  }
+
+  @CheckDiscarded
+  public static long method0L() {
+    return 0;
+  }
+
+  @CheckDiscarded
+  public static long method1L() {
+    return 1;
+  }
+}
diff --git a/src/test/examples/assumenosideeffects3/CheckDiscarded.java b/src/test/examples/assumenosideeffects3/CheckDiscarded.java
new file mode 100644
index 0000000..9880dc6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects3;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects3/keep-rules-discard.txt b/src/test/examples/assumenosideeffects3/keep-rules-discard.txt
new file mode 100644
index 0000000..cdd4b86
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/keep-rules-discard.txt
@@ -0,0 +1,25 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects3.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects3.Assumenosideeffects {
+  public static int method0(...) return 1;
+  public static int method1(...) return 0;
+  public static long method0L(...) return 1;
+  public static long method1L(...) return 0;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+  @assumenosideeffects3.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects3/keep-rules.txt b/src/test/examples/assumenosideeffects3/keep-rules.txt
new file mode 100644
index 0000000..cb12eb1
--- /dev/null
+++ b/src/test/examples/assumenosideeffects3/keep-rules.txt
@@ -0,0 +1,20 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects3.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects3.Assumenosideeffects {
+  public static int method0(...) return 1;
+  public static int method1(...) return 0;
+  public static long method0L(...) return 1;
+  public static long method1L(...) return 0;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects4/Assumenosideeffects.java b/src/test/examples/assumenosideeffects4/Assumenosideeffects.java
new file mode 100644
index 0000000..cfeff6e
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/Assumenosideeffects.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects4;
+
+public class Assumenosideeffects {
+
+  public static final int ASSUMED_VALUE_0 = 0;
+  public static final int ASSUMED_VALUE_1 = 1;
+  public static final long ASSUMED_VALUE_0L = 0;
+  public static final long ASSUMED_VALUE_1L = 1;
+
+  public static void main(String[] args) {
+    System.out.println(method0());
+    System.out.println(method1());
+    System.out.println(method0L() + "L");
+    System.out.println(method1L() + "L");
+  }
+
+  @CheckDiscarded
+  public static int method0() {
+    System.out.println("method0");
+    return ASSUMED_VALUE_0;
+  }
+
+  @CheckDiscarded
+  public static int method1() {
+    System.out.println("method1");
+    return ASSUMED_VALUE_1;
+  }
+
+  @CheckDiscarded
+  public static long method0L() {
+    System.out.println("method0L");
+    return ASSUMED_VALUE_0L;
+  }
+
+  @CheckDiscarded
+  public static long method1L() {
+    System.out.println("method1L");
+    return ASSUMED_VALUE_1L;
+  }
+}
diff --git a/src/test/examples/assumenosideeffects4/CheckDiscarded.java b/src/test/examples/assumenosideeffects4/CheckDiscarded.java
new file mode 100644
index 0000000..43398ab
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects4;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects4/keep-rules-discard.txt b/src/test/examples/assumenosideeffects4/keep-rules-discard.txt
new file mode 100644
index 0000000..a6cfcd6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/keep-rules-discard.txt
@@ -0,0 +1,29 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects4.Assumenosideeffects {
+  public static final int ASSUMED_VALUE_0;
+  public static final int ASSUMED_VALUE_1;
+  public static final long ASSUMED_VALUE_0L;
+  public static final long ASSUMED_VALUE_1L;
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects4.Assumenosideeffects {
+  public static int method0(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1;
+  public static int method1(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0;
+  public static long method0L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1L;
+  public static long method1L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0L;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+  @assumenosideeffects4.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects4/keep-rules.txt b/src/test/examples/assumenosideeffects4/keep-rules.txt
new file mode 100644
index 0000000..1c66b38
--- /dev/null
+++ b/src/test/examples/assumenosideeffects4/keep-rules.txt
@@ -0,0 +1,24 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects4.Assumenosideeffects {
+  public static final int ASSUMED_VALUE_0;
+  public static final int ASSUMED_VALUE_1;
+  public static final long ASSUMED_VALUE_0L;
+  public static final long ASSUMED_VALUE_1L;
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects4.Assumenosideeffects {
+  public static int method0(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1;
+  public static int method1(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0;
+  public static long method0L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_1L;
+  public static long method1L(...) return assumenosideeffects4.Assumenosideeffects.ASSUMED_VALUE_0L;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumenosideeffects5/Assumenosideeffects.java b/src/test/examples/assumenosideeffects5/Assumenosideeffects.java
new file mode 100644
index 0000000..91a1a0d
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/Assumenosideeffects.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects5;
+
+public class Assumenosideeffects {
+
+  public static void main(String[] args) {
+    System.out.println(methodTrue());
+    System.out.println(methodFalse());
+  }
+
+  @CheckDiscarded
+  public static boolean methodTrue() {
+    System.out.println("methodTrue");
+    return true;
+  }
+
+  @CheckDiscarded
+  public static boolean methodFalse() {
+    System.out.println("methodFalse");
+    return false;
+  }
+}
diff --git a/src/test/examples/assumenosideeffects5/CheckDiscarded.java b/src/test/examples/assumenosideeffects5/CheckDiscarded.java
new file mode 100644
index 0000000..1c5f479
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects5;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects5/keep-rules-discard.txt b/src/test/examples/assumenosideeffects5/keep-rules-discard.txt
new file mode 100644
index 0000000..2383b2c
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects5.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects5.Assumenosideeffects {
+  public static boolean methodTrue(...) return false;
+  public static boolean methodFalse(...) return true;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+  @assumenosideeffects5.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects5/keep-rules.txt b/src/test/examples/assumenosideeffects5/keep-rules.txt
new file mode 100644
index 0000000..ac8cb71
--- /dev/null
+++ b/src/test/examples/assumenosideeffects5/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects5.Assumenosideeffects {
+  public static void main(...);
+}
+
+# Mark some methods to have no side effects.
+-assumenosideeffects public class assumenosideeffects5.Assumenosideeffects {
+  public static boolean methodTrue(...) return false;
+  public static boolean methodFalse(...) return true;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues1/Assumevalues.java b/src/test/examples/assumevalues1/Assumevalues.java
new file mode 100644
index 0000000..24e753c
--- /dev/null
+++ b/src/test/examples/assumevalues1/Assumevalues.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumevalues1;
+
+public class Assumevalues {
+
+  public static int value = 2;
+  public static long valueL = 2;
+
+  public static void main(String[] args) {
+    value = 3;
+    if (value == 1) {
+      System.out.println("1");
+    }
+    if (value == 2) {
+      System.out.println("2");
+    }
+    if (value == 3) {
+      System.out.println("3");
+    }
+
+    valueL = 3;
+    if (valueL == 1) {
+      System.out.println("1L");
+    }
+    if (valueL == 2) {
+      System.out.println("2L");
+    }
+    if (valueL == 3) {
+      System.out.println("3L");
+    }
+  }
+}
diff --git a/src/test/examples/assumevalues1/keep-rules.txt b/src/test/examples/assumevalues1/keep-rules.txt
new file mode 100644
index 0000000..53f4c15
--- /dev/null
+++ b/src/test/examples/assumevalues1/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues1.Assumevalues {
+  public static void main(...);
+}
+
+# Assume values for fields.
+-assumevalues public class assumevalues1.Assumevalues {
+  public static int value return 1;
+  public static long valueL return 1;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues2/Assumevalues.java b/src/test/examples/assumevalues2/Assumevalues.java
new file mode 100644
index 0000000..fc44a82
--- /dev/null
+++ b/src/test/examples/assumevalues2/Assumevalues.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumevalues2;
+
+public class Assumevalues {
+
+  public static int value = 2;
+  public static long valueL = 2;
+
+  public static void main(String[] args) {
+    value = 1;
+    if (value == 1) {
+      method1();
+    }
+    value = 2;
+    if (value == 2) {
+      method2();
+    }
+    value = 3;
+    if (value == 3) {
+      method3();
+    }
+    value = 4;
+    if (value == 4) {
+      method4();
+    }
+
+    valueL = 1;
+    if (valueL == 1) {
+      method1L();
+    }
+    valueL = 2;
+    if (valueL == 2) {
+      method2L();
+    }
+    valueL = 3;
+    if (valueL == 3) {
+      method3L();
+    }
+    valueL = 4;
+    if (valueL == 4) {
+      method4L();
+    }
+  }
+
+  @CheckDiscarded
+  public static void method1() {
+    System.out.println("1");
+  }
+
+  public static void method2() {
+    System.out.println("2");
+  }
+
+  public static void method3() {
+    System.out.println("3");
+  }
+
+  @CheckDiscarded
+  public static void method4() {
+    System.out.println("4");
+  }
+
+  @CheckDiscarded
+  public static void method1L() {
+    System.out.println("1L");
+  }
+
+  public static void method2L() {
+    System.out.println("2L");
+  }
+
+  public static void method3L() {
+    System.out.println("3L");
+  }
+
+  @CheckDiscarded
+  public static void method4L() {
+    System.out.println("4L");
+  }
+}
diff --git a/src/test/examples/assumevalues2/CheckDiscarded.java b/src/test/examples/assumevalues2/CheckDiscarded.java
new file mode 100644
index 0000000..252ac6b
--- /dev/null
+++ b/src/test/examples/assumevalues2/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumevalues2;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumevalues2/keep-rules-discard.txt b/src/test/examples/assumevalues2/keep-rules-discard.txt
new file mode 100644
index 0000000..7817d71
--- /dev/null
+++ b/src/test/examples/assumevalues2/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues2.Assumevalues {
+  public static void main(...);
+}
+
+# Assume values for a field.
+-assumevalues public class assumevalues2.Assumevalues {
+  public static int value return 2..3;
+  public static long valueL return 2..3;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class * {
+  @assumevalues2.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumevalues2/keep-rules.txt b/src/test/examples/assumevalues2/keep-rules.txt
new file mode 100644
index 0000000..c1fead1
--- /dev/null
+++ b/src/test/examples/assumevalues2/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues2.Assumevalues {
+  public static void main(...);
+}
+
+# Assume values for fields.
+-assumevalues public class assumevalues2.Assumevalues {
+  public static int value return 2..3;
+  public static long valueL return 2..3;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues3/Assumevalues.java b/src/test/examples/assumevalues3/Assumevalues.java
new file mode 100644
index 0000000..594f1cb
--- /dev/null
+++ b/src/test/examples/assumevalues3/Assumevalues.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumevalues3;
+
+public class Assumevalues {
+
+  public static final int ASSUMED_VALUE = 1;
+  public static final long ASSUMED_VALUEL = 1;
+
+  public static int value = 2;
+  public static long valueL = 2;
+
+  public static void main(String[] args) {
+    value = 3;
+    if (value == 1) {
+      System.out.println("1");
+    }
+    if (value == 2) {
+      System.out.println("2");
+    }
+    if (value == 3) {
+      System.out.println("3");
+    }
+
+    valueL = 3;
+    if (valueL == 1) {
+      System.out.println("1L");
+    }
+    if (valueL == 2) {
+      System.out.println("2L");
+    }
+    if (valueL == 3) {
+      System.out.println("3L");
+    }
+  }
+}
diff --git a/src/test/examples/assumevalues3/keep-rules.txt b/src/test/examples/assumevalues3/keep-rules.txt
new file mode 100644
index 0000000..020b564
--- /dev/null
+++ b/src/test/examples/assumevalues3/keep-rules.txt
@@ -0,0 +1,20 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues3.Assumevalues {
+  public static final int ASSUMED_VALUE;
+  public static final long ASSUMED_VALUEL;
+  public static void main(...);
+}
+
+# Assume values for fields.
+-assumevalues public class assumevalues3.Assumevalues {
+  public static int value return assumevalues3.Assumevalues.ASSUMED_VALUE;
+  public static long valueL return assumevalues3.Assumevalues.ASSUMED_VALUEL;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues4/Assumevalues.java b/src/test/examples/assumevalues4/Assumevalues.java
new file mode 100644
index 0000000..f401c81
--- /dev/null
+++ b/src/test/examples/assumevalues4/Assumevalues.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumevalues4;
+
+public class Assumevalues {
+
+  public static final int ASSUMED_VALUE_0 = 0;
+  public static final int ASSUMED_VALUE_1 = 1;
+  public static final long ASSUMED_VALUE_0L = 0;
+  public static final long ASSUMED_VALUE_1L = 1;
+
+  public static void main(String[] args) {
+    System.out.println(method0());
+    System.out.println(method1());
+    System.out.println(method0L() + "L");
+    System.out.println(method1L() + "L");
+  }
+
+  public static int method0() {
+    System.out.println("method0");
+    return ASSUMED_VALUE_0;
+  }
+
+  public static int method1() {
+    System.out.println("method1");
+    return ASSUMED_VALUE_1;
+  }
+
+  public static long method0L() {
+    System.out.println("method0L");
+    return ASSUMED_VALUE_0L;
+  }
+
+  public static long method1L() {
+    System.out.println("method1L");
+    return ASSUMED_VALUE_1L;
+  }
+}
diff --git a/src/test/examples/assumevalues4/keep-rules.txt b/src/test/examples/assumevalues4/keep-rules.txt
new file mode 100644
index 0000000..3f6e9f6
--- /dev/null
+++ b/src/test/examples/assumevalues4/keep-rules.txt
@@ -0,0 +1,24 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues4.Assumevalues {
+  public static final int ASSUMED_VALUE_0;
+  public static final int ASSUMED_VALUE_1;
+  public static final long ASSUMED_VALUE_0L;
+  public static final long ASSUMED_VALUE_1L;
+  public static void main(...);
+}
+
+# Mark some methods to have known return value.
+-assumevalues public class assumevalues4.Assumevalues {
+  public static int method0(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_1;
+  public static int method1(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_0;
+  public static long method0L(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_1L;
+  public static long method1L(...) return assumevalues4.Assumevalues.ASSUMED_VALUE_0L;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues5/Assumevalues.java b/src/test/examples/assumevalues5/Assumevalues.java
new file mode 100644
index 0000000..714e144
--- /dev/null
+++ b/src/test/examples/assumevalues5/Assumevalues.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumevalues5;
+
+public class Assumevalues {
+
+  public static void main(String[] args) {
+    System.out.println(methodTrue());
+    System.out.println(methodFalse());
+  }
+
+  public static boolean methodTrue() {
+    System.out.println("methodTrue");
+    return true;
+  }
+
+  public static boolean methodFalse() {
+    System.out.println("methodFalse");
+    return false;
+  }
+}
diff --git a/src/test/examples/assumevalues5/keep-rules.txt b/src/test/examples/assumevalues5/keep-rules.txt
new file mode 100644
index 0000000..416aa36
--- /dev/null
+++ b/src/test/examples/assumevalues5/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumevalues5.Assumevalues {
+  public static void main(...);
+}
+
+# Mark some methods to have known return value.
+-assumevalues public class assumevalues5.Assumevalues {
+  public static boolean methodTrue(...) return false;
+  public static boolean methodFalse(...) return true;
+}
+
+# Allow access modification to enable minifcation.
+-allowaccessmodification
diff --git a/src/test/examples/barray/BArray.java b/src/test/examples/barray/BArray.java
new file mode 100644
index 0000000..0ca6505
--- /dev/null
+++ b/src/test/examples/barray/BArray.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package barray;
+
+public class BArray {
+
+  public static void main(String[] args) {
+    boolean[] boolArray = null;
+    byte[] byteArray = null;
+    boolean bool;
+    byte bits;
+    try {
+      bool = boolArray[0] || boolArray[1];
+    } catch (Throwable e) {
+      bool = true;
+    }
+    try {
+      bits = byteArray[0];
+    } catch (Throwable e) {
+      bits = 42;
+    }
+    System.out.println("bits " + bits + " and bool " + bool);
+  }
+}
diff --git a/src/test/examples/bridge/BridgeMethod.java b/src/test/examples/bridge/BridgeMethod.java
new file mode 100644
index 0000000..9e82a29
--- /dev/null
+++ b/src/test/examples/bridge/BridgeMethod.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package bridge;
+
+abstract class Super<T> {
+  public abstract int method(T t0, T t1);
+  public abstract int rangeMethod(T t0, T t1, T t2, T t3, T t4, T t5);
+}
+
+public class BridgeMethod extends Super<Integer> {
+
+  @Override
+  public int method(Integer t0, Integer t1) {
+    if (t0 > t1) {
+      return t0;
+    }
+    return t1;
+  }
+
+  @Override
+  public int rangeMethod(Integer t0, Integer t1, Integer t2, Integer t3, Integer t4, Integer t5) {
+    if (t0 > t1) {
+      return t0;
+    }
+    return t1 + t2 + t3 + t4 + t5;
+  }
+
+  public static void main(String[] args) {
+    Super<Integer> instance = new BridgeMethod();
+    instance.method(1, 2);
+    instance.method(2, 1);
+    instance.rangeMethod(1, 2, 3, 4, 5, 6);
+    instance.rangeMethod(2, 1, 3, 4, 5, 6);
+  }
+}
diff --git a/src/test/examples/constants/Constants.java b/src/test/examples/constants/Constants.java
new file mode 100644
index 0000000..6349749
--- /dev/null
+++ b/src/test/examples/constants/Constants.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'constants.dex' is what is run.
+package constants;
+
+class Constants {
+
+  public static void printInt(int x) {
+    System.out.print(x);
+  }
+
+  public static void printLong(long x) {
+    System.out.print(x);
+  }
+
+  public static void testConst4() {
+    printInt(-8);
+    printInt(-1);
+    printInt(0);
+    printInt(1);
+    printInt(7);
+  }
+
+  public static void testConst16() {
+    printInt(Short.MIN_VALUE);
+    printInt(-9);
+    printInt(8);
+    printInt(Short.MAX_VALUE);
+  }
+
+  public static void testConstHigh16() {
+    printInt(0xffff0000);
+    printInt(0xf0000000);
+    printInt(0x0f000000);
+    printInt(0x00f00000);
+    printInt(0x000f0000);
+    printInt(0x80000000);
+    printInt(0x00010000);
+  }
+
+  public static void testConst() {
+    printInt(Short.MIN_VALUE - 1);
+    printInt(Short.MAX_VALUE + 1);
+
+    printInt(0xffff0001);
+    printInt(0xf0000001);
+    printInt(0x0f000001);
+    printInt(0x00f00001);
+    printInt(0x000f0001);
+    printInt(0x80000001);
+    printInt(0x00010001);
+  }
+
+  public static void testConstWide16() {
+    printLong((long) Short.MIN_VALUE);
+    printLong(-1L);
+    printLong(0L);
+    printLong(1L);
+    printLong((long) Short.MAX_VALUE);
+  }
+
+  public static void testConstWide32() {
+    printLong((long) Short.MIN_VALUE - 1);
+    printLong((long) Integer.MIN_VALUE);
+    printLong((long) Integer.MAX_VALUE);
+    printLong((long) Short.MAX_VALUE + 1);
+  }
+
+  public static void testConstWideHigh16() {
+    printLong(0xffff000000000000L);
+    printLong(0xf000000000000000L);
+    printLong(0x0f00000000000000L);
+    printLong(0x00f0000000000000L);
+    printLong(0x000f000000000000L);
+    printLong(0x8000000000000000L);
+    printLong(0x0001000000000000L);
+    printLong(0x7fff000000000000L);
+  }
+
+  public static void testConstWide() {
+    printLong((long) Integer.MIN_VALUE - 1);
+    printLong((long) Integer.MAX_VALUE + 1);
+
+    printLong(0xffff7fffffffffffL);
+    printLong(0xffff000000000001L);
+    printLong(0xf000000000000001L);
+    printLong(0x0f00000000000001L);
+    printLong(0x00f0000000000001L);
+    printLong(0x000f000000000001L);
+    printLong(0x8000000000000001L);
+    printLong(0x0001000000000001L);
+    printLong(0x7fffffffffffffffL);
+    printLong(0x7fff000000000001L);
+  }
+
+  public static void main(String[] args) {
+    testConst4();
+    testConst16();
+    testConstHigh16();
+    testConst();
+
+    testConstWide16();
+    testConstWide32();
+    testConstWideHigh16();
+    testConstWide();
+  }
+}
diff --git a/src/test/examples/controlflow/ControlFlow.java b/src/test/examples/controlflow/ControlFlow.java
new file mode 100644
index 0000000..ffdabe0
--- /dev/null
+++ b/src/test/examples/controlflow/ControlFlow.java
@@ -0,0 +1,100 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'controlflow.dex' is what is run.
+
+package controlflow;
+
+public class ControlFlow {
+
+  public static void simpleIf(boolean b) {
+    String s = "Hep!";
+    if (b) {
+      s = "Fisk";
+    } else {
+      s = "Hest";
+    }
+    System.out.println(s);
+  }
+
+  public static void simpleIfMoreValues(boolean b) {
+    int i = 0;
+    double d = 0.0;
+    String s = "Hep!";
+    if (b) {
+      i = 1;
+      d = 1.1;
+      s = "Fisk";
+      b = false;
+    } else {
+      i = 2;
+      d = 2.2;
+      s = "Hest";
+    }
+    if (i == 1) {
+      b = true;
+    }
+    System.out.println(s + " " + i + " " + d + " " + b);
+  }
+
+  public static void simpleIfFallthrough(boolean b) {
+    String s = "Hep!";
+    if (b) {
+      s = "Fisk";
+    }
+    System.out.println(s);
+  }
+
+  public static void sequenceOfIfs(int i) {
+    if (i < 10) {
+      System.out.println("10");
+    }
+    if (i < 5) {
+      System.out.println("5");
+    }
+    if (i < 2) {
+      System.out.println("2");
+    }
+  }
+
+  public static void nestedIfs(int i) {
+    if (i < 10) {
+      System.out.println("10");
+      if (i < 5) {
+        System.out.println("5");
+        if (i < 2) {
+          System.out.println("2");
+        }
+      }
+    }
+  }
+
+  public static void simpleLoop(int count) {
+    System.out.println("simpleLoop");
+    for (int i = 0; i < count; i++) {
+      System.out.println("count: " + i);
+    }
+  }
+
+  public static void main(String[] args) {
+    simpleIf(true);
+    simpleIf(false);
+    simpleIfMoreValues(true);
+    simpleIfMoreValues(false);
+    simpleIfFallthrough(true);
+    simpleIfFallthrough(false);
+    sequenceOfIfs(10);
+    sequenceOfIfs(9);
+    sequenceOfIfs(4);
+    sequenceOfIfs(1);
+    nestedIfs(10);
+    nestedIfs(9);
+    nestedIfs(4);
+    nestedIfs(1);
+    simpleLoop(0);
+    simpleLoop(1);
+    simpleLoop(10);
+  }
+}
diff --git a/src/test/examples/conversions/Conversions.java b/src/test/examples/conversions/Conversions.java
new file mode 100644
index 0000000..456972d
--- /dev/null
+++ b/src/test/examples/conversions/Conversions.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package conversions;
+
+public class Conversions {
+
+  public static byte b(byte b) {
+    return b;
+  }
+
+  public static char c(char c) {
+    return c;
+  }
+
+  public static short s(short s) {
+    return s;
+  }
+
+  public static int i() {
+    return 1;
+  }
+
+  public static int i(int i) {
+    return i;
+  }
+
+  public static long l() {
+    return 1;
+  }
+
+  public static long l(long l) {
+    return l;
+  }
+
+  public static double d() {
+    return 1;
+  }
+
+  public static double d(double d) {
+    return d;
+  }
+
+  public static float f() {
+    return 1;
+  }
+
+  public static float f(float f) {
+    return f;
+  }
+
+  public static void main(String[] args) {
+    // I2L, I2F, I2D
+    System.out.println(l(i()));
+    System.out.println(f(i()));
+    System.out.println(d(i()));
+    // L2I, L2F, L2D
+    System.out.println(i((int) l()));
+    System.out.println(f(l()));
+    System.out.println(d(l()));
+    // F2I, F2L, F2D
+    System.out.println(i((int) f()));
+    System.out.println(l((long) f()));
+    System.out.println(d(f()));
+    // D2I, D2L, D2F
+    System.out.println(i((int) d()));
+    System.out.println(l((long) d()));
+    System.out.println(f((float) d()));
+    // I2B, I2C, I2S
+    System.out.println(b((byte) i()));
+    System.out.println(c((char) i()));
+    System.out.println(s((short) i()));
+  }
+}
diff --git a/src/test/examples/cse/CommonSubexpressionElimination.java b/src/test/examples/cse/CommonSubexpressionElimination.java
new file mode 100644
index 0000000..3c64a1d
--- /dev/null
+++ b/src/test/examples/cse/CommonSubexpressionElimination.java
@@ -0,0 +1,115 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package cse;
+
+public class CommonSubexpressionElimination {
+
+  public static int divNoCatch(int a, int b, int c) {
+    int d = c / (a - b);
+    System.out.println(d);
+    return c / (a - b);
+  }
+
+  public static int divNoCatch2(int a, int b, int c) {
+    int d = c / (a - b);
+    int e = c / (a - b);
+    System.out.println(d + " " + e);
+    return c / (a - b);
+  }
+
+  public static int divCatch(int a, int b, int c) {
+    try {
+      int d = c / (a - b);
+      System.out.println(d);
+      return d;
+    } catch (Throwable t) {
+      return c / (a - b);
+    }
+  }
+
+  public static int divCatch2(int a, int b, int c) {
+    try {
+      int d = c / (a - b);
+      int e = c / (a - b);
+      System.out.println(d + " " + e);
+      return d;
+    } catch (Throwable t) {
+      return c / (a - b);
+    }
+  }
+
+  public static String divCatchCatch(int a, int b, int c) {
+    try {
+      int d = c / (a - b);
+      System.out.println(d);
+      return "X";
+    } catch (Throwable t) {
+      try {
+        return "" + c / (a - b);
+      } catch (Throwable t2) {
+        return "A";
+      }
+    }
+  }
+
+  public static String divCatchSharedCatchHandler(int a, int b, int c) {
+    try {
+      int d = c / (a - b);
+      System.out.println(d);
+      if (a == 0) {
+        int e = c / (a - b);
+        System.out.println(e);
+      } else {
+        int f = c / (a - b);
+        System.out.println(f);
+      }
+      return "X";
+    } catch (Throwable t) {
+      return "B";
+    }
+  }
+
+
+  public static void main(String[] args) {
+    System.out.println(divNoCatch(1, 0, 1));
+    System.out.println(divNoCatch2(1, 0, 2));
+    System.out.println(divCatch(1, 0, 3));
+    System.out.println(divCatch2(1, 0, 4));
+
+    System.out.println(divCatchCatch(0, 0, 1));
+    System.out.println(divCatchSharedCatchHandler(0, 0, 1));
+
+    try {
+      divNoCatch(0, 0, 1);
+      throw new RuntimeException("UNEXPECTED");
+    } catch (ArithmeticException e) {
+      // Expected "divide by zero".
+    }
+
+    try {
+      divNoCatch2(0, 0, 1);
+      throw new RuntimeException("UNEXPECTED");
+    } catch (ArithmeticException e) {
+      // Expected "divide by zero".
+    }
+
+    try {
+      divCatch(0, 0, 1);
+      throw new RuntimeException("UNEXPECTED");
+    } catch (ArithmeticException e) {
+      // Expected "divide by zero".
+    }
+
+    try {
+      divCatch2(0, 0, 1);
+      throw new RuntimeException("UNEXPECTED");
+    } catch (ArithmeticException e) {
+      // Expected "divide by zero".
+    }
+  }
+}
diff --git a/src/test/examples/enclosingmethod/AbstractClass.java b/src/test/examples/enclosingmethod/AbstractClass.java
new file mode 100644
index 0000000..90dfe27
--- /dev/null
+++ b/src/test/examples/enclosingmethod/AbstractClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package enclosingmethod;
+
+public abstract class AbstractClass {
+  public abstract int anInt();
+}
diff --git a/src/test/examples/enclosingmethod/Main.java b/src/test/examples/enclosingmethod/Main.java
new file mode 100644
index 0000000..c1c0b75
--- /dev/null
+++ b/src/test/examples/enclosingmethod/Main.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package enclosingmethod;
+
+public class Main {
+  public static void main(String... args) {
+    OuterClass anInstance = new OuterClass();
+    anInstance.aMethod();
+    final Class[] classes = OuterClass.class.getDeclaredClasses();
+    for (Class clazz : classes) {
+      System.out.println("InnerClass " + clazz.getName());
+    }
+  }
+}
diff --git a/src/test/examples/enclosingmethod/OuterClass.java b/src/test/examples/enclosingmethod/OuterClass.java
new file mode 100644
index 0000000..d9ab4ba
--- /dev/null
+++ b/src/test/examples/enclosingmethod/OuterClass.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package enclosingmethod;
+
+public class OuterClass {
+  public class AClass {
+
+  }
+
+  public void aMethod() {
+    class AnotherClass extends AbstractClass {
+
+      @Override
+      public int anInt() {
+        return 48;
+      }
+    }
+
+    print(new AbstractClass() {
+      @Override
+      public int anInt() {
+        return 42;
+      }
+    });
+    print(new AnotherClass());
+  }
+
+  private static void print(AbstractClass anInstance) {
+    System.out.println(anInstance.anInt());
+    System.out.println(anInstance.getClass().getEnclosingClass());
+    System.out.println(anInstance.getClass().getEnclosingMethod());
+    System.out.println(anInstance.getClass().isAnonymousClass());
+    System.out.println(anInstance.getClass().isLocalClass());
+    System.out.println(anInstance.getClass().isMemberClass());
+  }
+}
diff --git a/src/test/examples/enclosingmethod/proguard.cfg b/src/test/examples/enclosingmethod/proguard.cfg
new file mode 100644
index 0000000..bc5bd8d
--- /dev/null
+++ b/src/test/examples/enclosingmethod/proguard.cfg
@@ -0,0 +1,10 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-keep class enclosingmethod.* {
+  *;
+}
+
+# Keep only the InnerClasses attribute but not the EnclosingMethod one.
+-keepattributes InnerClasses
diff --git a/src/test/examples/filledarray/FilledArray.java b/src/test/examples/filledarray/FilledArray.java
new file mode 100644
index 0000000..04a8f83
--- /dev/null
+++ b/src/test/examples/filledarray/FilledArray.java
@@ -0,0 +1,134 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package filledarray;
+
+import java.util.Arrays;
+
+public class FilledArray {
+  private static boolean[] booleans = new boolean[] { true, true, false, false };
+  private static byte[] bytes = new byte[] {
+      0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, -19, -20, -96,
+      Byte.MAX_VALUE, Byte.MIN_VALUE };
+  private static char[] chars = new char[] {
+      Character.MAX_VALUE, 'a', 'b', 'c', 'd', Character.MIN_VALUE };
+  private static int[] ints = new int[] { Integer.MAX_VALUE, 0, -42, 42, Integer.MIN_VALUE };
+  private static short[] shorts = new short[] { Short.MAX_VALUE, 0, -42, 42, Short.MIN_VALUE };
+  private static long[] longs = new long[] {
+      Long.MAX_VALUE, 0x1234123412341234L, -0x1234123412341234L, Long.MIN_VALUE };
+  private static float[] floats = new float[] {
+      Float.MAX_VALUE, 23.23F, -43.123F, Float.MIN_VALUE, Float.MIN_NORMAL };
+  private static double[] doubles = new double[] {
+      Double.MAX_VALUE, 123123123.123, -43333.123, Double.MIN_VALUE, Double.MIN_NORMAL };
+
+
+  public static void filledArrays() {
+    boolean[] localBooleans = new boolean[] { true, true, false, false };
+    localBooleans[0] = false;
+    byte[] localBytes = new byte[] { 21, 22, -23 };
+    char[] localChars = new char[] { 'a', 'b', 'c', 'd' };
+    int[] localInts = new int[] { Integer.MAX_VALUE, 0, -42, 42, Integer.MIN_VALUE };
+    short[] localShorts = new short[] { Short.MAX_VALUE, 0, -42, 42, Short.MIN_VALUE };
+    long[] localLongs= new long[] { 0x1234432112341234L, -0x1234123412344321L };
+    localLongs[1] = localLongs[1] + 2;
+    float[] localFloats = new float[] { 23.23F, -43.123F };
+    double[] localDoubles = new double[] { 123123123.123, -43333.123 };
+    System.out.println("booleans");
+    for (int i = 0; i < booleans.length; i++) {
+      System.out.println(booleans[i]);
+    }
+    for (int i = 0; i < localBooleans.length; i++) {
+      System.out.println(localBooleans[i]);
+    }
+    System.out.println("bytes");
+    for (int i = 0; i < bytes.length; i++) {
+      System.out.println(bytes[i]);
+    }
+    for (int i = 0; i < localBytes.length; i++) {
+      System.out.println(localBytes[i]);
+    }
+    System.out.println("chars");
+    for (int i = 0; i < chars.length; i++) {
+      System.out.println(chars[i]);
+    }
+    for (int i = 0; i < localChars.length; i++) {
+      System.out.println(localChars[i]);
+    }
+    System.out.println("ints");
+    for (int i = 0; i < ints.length; i++) {
+      System.out.println(ints[i]);
+    }
+    for (int i = 0; i < localInts.length; i++) {
+      System.out.println(localInts[i]);
+    }
+    System.out.println("shorts");
+    for (int i = 0; i < shorts.length; i++) {
+      System.out.println(shorts[i]);
+    }
+    for (int i = 0; i < localShorts.length; i++) {
+      System.out.println(localShorts[i]);
+    }
+    System.out.println("longs");
+    for (int i = 0; i < longs.length; i++) {
+      System.out.println(longs[i]);
+    }
+    for (int i = 0; i < localLongs.length; i++) {
+      System.out.println(localLongs[i]);
+    }
+    System.out.println("floats");
+    for (int i = 0; i < floats.length; i++) {
+      System.out.println(floats[i]);
+    }
+    for (int i = 0; i < localFloats.length; i++) {
+      System.out.println(localFloats[i]);
+    }
+    System.out.println("doubles");
+    for (int i = 0; i < doubles.length; i++) {
+      System.out.println(doubles[i]);
+    }
+    for (int i = 0; i < localDoubles.length; i++) {
+      System.out.println(localDoubles[i]);
+    }
+  }
+
+  public static void filledArraysExceptions(int divisor) {
+    try {
+      // Array creations that can be turned into fill-array-data.
+      int[] ints = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+      int[] ints2 = new int[5];
+      ints2[0] = 0;
+      ints2[1] = 1;
+      ints2[2] = 2;
+      ints2[3] = 3;
+      ints2[4] = 4;
+      int i = ints[1] / divisor;
+      System.out.println("i = " + i);
+      System.out.println("ints = " + Arrays.toString(ints));
+      System.out.println("ints2 = " + Arrays.toString(ints2));
+    } catch (Throwable t) {
+      t.printStackTrace(System.out);
+    }
+
+    try {
+      // Array creation that cannot be turned into fill-array-data because an exception would
+      // cause the initialization sequence to be interrupted.
+      int[] ints = new int[5];
+      ints[0] = 0;
+      ints[1] = 1;
+      ints[2] = 2;
+      ints[3] = 3;
+      int i = 7 / divisor;
+      ints[4] = 4;
+      System.out.println("i = " + i);
+      System.out.println("ints = " + Arrays.toString(ints));
+    } catch (Throwable t) {
+      t.printStackTrace(System.out);
+    }
+  }
+
+  public static void main(String[] args) {
+    filledArrays();
+    filledArraysExceptions(1);
+    filledArraysExceptions(0);
+  }
+}
diff --git a/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotation.java b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotation.java
new file mode 100644
index 0000000..5d7fd8c
--- /dev/null
+++ b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotation.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package floating_point_annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface FloatingPointValuedAnnotation {
+  double doubleValue() default 42.42;
+  float floatValue() default 42.42f;
+}
diff --git a/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotationTest.java b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotationTest.java
new file mode 100644
index 0000000..1772f23
--- /dev/null
+++ b/src/test/examples/floating_point_annotations/FloatingPointValuedAnnotationTest.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package floating_point_annotations;
+
+public class FloatingPointValuedAnnotationTest {
+
+  @FloatingPointValuedAnnotation(doubleValue = -0d)
+  class A {
+  }
+
+  @FloatingPointValuedAnnotation(doubleValue = 0d)
+  class B {
+  }
+
+  @FloatingPointValuedAnnotation(floatValue = -0f)
+  class C {
+  }
+
+  @FloatingPointValuedAnnotation(floatValue = 0f)
+  class D {
+  }
+
+  public static void main(String[] args) {
+    System.out.println(A.class.getAnnotation(FloatingPointValuedAnnotation.class)
+        .equals(B.class.getAnnotation(FloatingPointValuedAnnotation.class)));
+
+    System.out.println(C.class.getAnnotation(FloatingPointValuedAnnotation.class)
+        .equals(D.class.getAnnotation(FloatingPointValuedAnnotation.class)));
+  }
+}
diff --git a/src/test/examples/hello/Hello.java b/src/test/examples/hello/Hello.java
new file mode 100644
index 0000000..c58f329
--- /dev/null
+++ b/src/test/examples/hello/Hello.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'hello.dex' is what is run.
+
+package hello;
+
+class Hello {
+  public static void main(String[] args) {
+    System.out.println("Hello, world");
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/ifstatements/IfStatements.java b/src/test/examples/ifstatements/IfStatements.java
new file mode 100644
index 0000000..5d340ab
--- /dev/null
+++ b/src/test/examples/ifstatements/IfStatements.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package ifstatements;
+
+class IfStatements {
+
+  public static void ifNull(Object a) {
+    if (a != null) {
+      System.out.println("sisnotnull");
+    }
+    if (a == null) {
+      System.out.println("sisnull");
+    }
+  }
+
+  public static void ifCond(int x) {
+    if (x == 0) {
+      System.out.println("ifCond x == 0");
+    }
+    if (x != 0) {
+      System.out.println("ifCond x != 0");
+    }
+    if (x < 0) {
+      System.out.println("ifCond x < 0");
+    }
+    if (x >= 0) {
+      System.out.println("ifCond x >= 0");
+    }
+    if (x > 0) {
+      System.out.println("ifCond x > 0");
+    }
+    if (x <= 0) {
+      System.out.println("ifCond x <= 0");
+    }
+  }
+
+  public static void ifIcmp(int x, int y) {
+    if (x == y) {
+      System.out.println("ifIcmp x == y");
+    }
+    if (x != y) {
+      System.out.println("ifIcmp x != y");
+    }
+    if (x < y) {
+      System.out.println("ifIcmp x < y");
+    }
+    if (x >= y) {
+      System.out.println("ifIcmp x >= y");
+    }
+    if (x > y) {
+      System.out.println("ifIcmp x > y");
+    }
+    if (x <= y) {
+      System.out.println("ifIcmp x <= y");
+    }
+  }
+
+  public static void ifAcmp(Object a, Object b) {
+    if (a == b) {
+      System.out.println("ifAcmp a == b");
+    }
+    if (a != b) {
+      System.out.println("ifAcmp a != b");
+    }
+  }
+
+  public static void main(String[] args) {
+    Object a = new Object();
+    ifNull(a);
+    ifNull(null);
+    ifCond(-1);
+    ifCond(0);
+    ifCond(1);
+    ifIcmp(-1, 1);
+    ifIcmp(0, 0);
+    ifIcmp(1, -1);
+    ifAcmp(a, a);
+    ifAcmp(a, null);
+  }
+}
diff --git a/src/test/examples/inlining/CheckDiscarded.java b/src/test/examples/inlining/CheckDiscarded.java
new file mode 100644
index 0000000..8ab68e0
--- /dev/null
+++ b/src/test/examples/inlining/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package inlining;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/inlining/Inlining.java b/src/test/examples/inlining/Inlining.java
new file mode 100644
index 0000000..e013655
--- /dev/null
+++ b/src/test/examples/inlining/Inlining.java
@@ -0,0 +1,263 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package inlining;
+
+class A {
+
+  int a;
+
+  A(int a) {
+    this.a = a;
+  }
+
+  int a() {
+    return a;
+  }
+}
+
+class B extends A {
+
+  B(int a) {
+    super(a);
+  }
+}
+
+class InlineConstructor {
+
+  int a;
+
+  @CheckDiscarded
+  InlineConstructor(int a) {
+    this.a = a;
+  }
+
+  @CheckDiscarded
+  InlineConstructor(long a) {
+    this((int) a);
+  }
+
+  static InlineConstructor create() {
+    return new InlineConstructor(10L);
+  }
+}
+
+class InlineConstructorOfInner {
+
+  class Inner {
+
+    int a;
+
+    @CheckDiscarded
+    Inner(int a) {
+      this.a = a;
+    }
+
+    // This is not inlined, even though it is only called once, as it is only called from a
+    // non-constructor, and will set a field (the outer object) before calling the other
+    // constructor.
+    Inner(long a) {
+      this((int) a);
+    }
+
+    public Inner create() {
+      return new Inner(10L);
+    }
+  }
+
+  Inner inner;
+
+  InlineConstructorOfInner() {
+    inner = new Inner(10L).create();
+  }
+}
+
+public class Inlining {
+
+  private static void Assert(boolean value) {
+    if (!value) {
+      System.out.println("FAILURE");
+    }
+  }
+
+  public static void main(String[] args) {
+    // Ensure the simple methods are called at least three times, to not be inlined due to being
+    // called only once or twice.
+    Assert(intExpression());
+    Assert(intExpression());
+    Assert(intExpression());
+    Assert(longExpression());
+    Assert(longExpression());
+    Assert(longExpression());
+    Assert(doubleExpression());
+    Assert(floatExpression());
+    Assert(floatExpression());
+    Assert(floatExpression());
+    Assert(stringExpression());
+    Assert(stringExpression());
+    Assert(stringExpression());
+
+    Assert(intArgumentExpression());
+    Assert(intArgumentExpression());
+    Assert(intArgumentExpression());
+    Assert(longArgumentExpression());
+    Assert(longArgumentExpression());
+    Assert(longArgumentExpression());
+    Assert(doubleArgumentExpression());
+    Assert(doubleArgumentExpression());
+    Assert(doubleArgumentExpression());
+    Assert(floatArgumentExpression());
+    Assert(floatArgumentExpression());
+    Assert(floatArgumentExpression());
+    Assert(stringArgumentExpression());
+    Assert(stringArgumentExpression());
+    Assert(stringArgumentExpression());
+
+    Assert(intAddExpression());
+    Assert(intAddExpression());
+    Assert(intAddExpression());
+
+    A b = new B(42);
+    A a = new A(42);
+    Assert(intCmpExpression(a, b));
+    Assert(intCmpExpression(a, b));
+    Assert(intCmpExpression(a, b));
+
+    // This is only called once!
+    Assert(onlyCalledOnce(10));
+
+    // This is only called twice, and is quite small!
+    Assert(onlyCalledTwice(1) == 2);
+    Assert(onlyCalledTwice(1) == 2);
+
+    InlineConstructor ic = InlineConstructor.create();
+    Assert(ic != null);
+    InlineConstructorOfInner icoi = new InlineConstructorOfInner();
+    Assert(icoi != null);
+  }
+
+  private static boolean intCmpExpression(A a, A b) {
+    return a.a() == b.a();
+  }
+
+  @CheckDiscarded
+  private static int intConstantInline() {
+    return 42;
+  }
+
+  @CheckDiscarded
+  private static boolean intExpression() {
+    return 42 == intConstantInline();
+  }
+
+  @CheckDiscarded
+  private static long longConstantInline() {
+    return 50000000000L;
+  }
+
+  @CheckDiscarded
+  private static boolean longExpression() {
+    return 50000000000L == longConstantInline();
+  }
+
+  @CheckDiscarded
+  private static double doubleConstantInline() {
+    return 42.42;
+  }
+
+  @CheckDiscarded
+  private static boolean doubleExpression() {
+    return 42.42 == doubleConstantInline();
+  }
+
+  @CheckDiscarded
+  private static float floatConstantInline() {
+    return 21.21F;
+  }
+
+  @CheckDiscarded
+  private static boolean floatExpression() {
+    return 21.21F == floatConstantInline();
+  }
+
+  private static String stringConstantInline() {
+    return "Fisk er godt";
+  }
+
+  private static boolean stringExpression() {
+    return "Fisk er godt" == stringConstantInline();
+  }
+
+  @CheckDiscarded
+  private static int intArgumentInline(int a, int b, int c) {
+    return b;
+  }
+
+  @CheckDiscarded
+  private static boolean intArgumentExpression() {
+    return 42 == intArgumentInline(-2, 42, -1);
+  }
+
+  @CheckDiscarded
+  private static long longArgumentInline(long a, long b, long c) {
+    return c;
+  }
+
+  @CheckDiscarded
+  private static boolean longArgumentExpression() {
+    return 50000000000L == longArgumentInline(-2L, -1L, 50000000000L);
+  }
+
+  @CheckDiscarded
+  private static double doubleArgumentInline(double a, double b, double c) {
+    return a;
+  }
+
+  @CheckDiscarded
+  private static boolean doubleArgumentExpression() {
+    return 42.42 == doubleArgumentInline(42.42, -2.0, -1.0);
+  }
+
+  @CheckDiscarded
+  private static float floatArgumentInline(float a, float b, float c) {
+    return b;
+  }
+
+  @CheckDiscarded
+  private static boolean floatArgumentExpression() {
+    return 21.21F == floatArgumentInline(-2.0F, 21.21F, -1.0F);
+  }
+
+  @CheckDiscarded
+  private static String stringArgumentInline(String a, String b, String c) {
+    return c;
+  }
+
+  private static boolean stringArgumentExpression() {
+    return "Fisk er godt" == stringArgumentInline("-1", "-1", "Fisk er godt");
+  }
+
+  @CheckDiscarded
+  private static int intAddInline(int a, int b) {
+    return a + b;
+  }
+
+  @CheckDiscarded
+  private static boolean intAddExpression() {
+    return 42 == intAddInline(21, 21);
+  }
+
+  @CheckDiscarded
+  private static boolean onlyCalledOnce(int count) {
+    int anotherCounter = 0;
+    for (int i = 0; i < count; i++) {
+      anotherCounter += i;
+    }
+    return anotherCounter > count;
+  }
+
+  @CheckDiscarded
+  private static int onlyCalledTwice(int count) {
+    return count > 0 ? count + 1 : count - 1;
+  }
+}
diff --git a/src/test/examples/inlining/keep-rules-discard.txt b/src/test/examples/inlining/keep-rules-discard.txt
new file mode 100644
index 0000000..66be962
--- /dev/null
+++ b/src/test/examples/inlining/keep-rules-discard.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class inlining.Inlining {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
+
+# check that methods have been inlined
+-checkdiscard class * {
+  @inlining.CheckDiscarded *;
+}
diff --git a/src/test/examples/inlining/keep-rules.txt b/src/test/examples/inlining/keep-rules.txt
new file mode 100644
index 0000000..dd713bb
--- /dev/null
+++ b/src/test/examples/inlining/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class inlining.Inlining {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/instanceofstring/InstanceofString.java b/src/test/examples/instanceofstring/InstanceofString.java
new file mode 100644
index 0000000..c7ff011
--- /dev/null
+++ b/src/test/examples/instanceofstring/InstanceofString.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package instanceofstring;
+
+class InstanceofString {
+  public static void main(String[] args) {
+    String s = "foobar";
+    System.out.println("is-string:" + (s instanceof String));
+  }
+}
diff --git a/src/test/examples/instancevariable/InstanceVariable.java b/src/test/examples/instancevariable/InstanceVariable.java
new file mode 100644
index 0000000..88768f1
--- /dev/null
+++ b/src/test/examples/instancevariable/InstanceVariable.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package instancevariable;
+
+class InstanceVariable {
+  public int foo = 42;
+  public int bar;
+  public InstanceVariable parent;
+
+  private int privateFoo;
+  private int privateBar = -1;
+  private InstanceVariable privateParent;
+
+  public InstanceVariable() {}
+
+  public InstanceVariable(int bar) {
+    this.bar = bar;
+    privateFoo = 1;
+    privateBar = 43;
+    parent = new InstanceVariable();
+    privateParent = new InstanceVariable();
+  }
+
+  public int sumAll() {
+    return privateFoo + privateBar + bar + foo + parent.foo + privateParent.privateBar;
+  }
+
+  public static void main(String[] args) {
+    InstanceVariable instanceVariable = new InstanceVariable(17);
+    System.out.println(instanceVariable.sumAll() + "=144");
+  }
+}
diff --git a/src/test/examples/invoke/Invoke.java b/src/test/examples/invoke/Invoke.java
new file mode 100644
index 0000000..d936140
--- /dev/null
+++ b/src/test/examples/invoke/Invoke.java
@@ -0,0 +1,456 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'invoke.dex' is what is run.
+
+package invoke;
+
+public class Invoke extends SuperClass implements InvokeInterface {
+
+  public static void static0() {
+    System.out.println("static0");
+  }
+
+  public static void static1(int a) {
+    System.out.println("static1 " + a);
+  }
+
+  public static void static2(int a, int b) {
+    System.out.println("static2 " + a + " " + b);
+  }
+
+  public static void static3(int a, int b, int c) {
+    System.out.println("static3 " + a + " " + b + " " + c);
+  }
+
+  public static void static4(int a, int b, int c, int d) {
+    System.out.println("static4 " + a + " " + b + " " + c + " " + d);
+  }
+
+  public static void static5(int a, int b, int c, int d, int e) {
+    System.out.println("static5 " + a + " " + b + " " + c + " " + d + " " + e);
+  }
+
+  public static void staticRange(int a, int b, int c, int d, int e, int f) {
+    System.out.println("staticRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+  }
+
+  public static void staticDouble0(double a) {
+    System.out.println("staticDouble0 " + a);
+  }
+
+  public static void staticDouble1(double a, double b) {
+    System.out.println("staticDouble2 " + a + " " + b);
+  }
+
+  public static void staticDoubleRange(double a, double b, double c) {
+    System.out.println("staticDoubleRange " + a + " " + b + " " + c);
+  }
+
+  public static void staticMethods() {
+    static0();
+    static1(1);
+    static2(1, 2);
+    static3(1, 2, 3);
+    static4(1, 2, 3, 4);
+    static5(1, 2, 3, 4, 5);
+    staticRange(1, 2, 3, 4, 5, 6);
+    staticDouble0(0.1);
+    staticDouble1(0.1, 0.2);
+    staticDoubleRange(0.1, 0.2, 0.3);
+  }
+
+  private void direct0() {
+    System.out.println("direct0");
+  }
+
+  private void direct1(int a) {
+    System.out.println("direct1 " + a);
+  }
+
+  private void direct2(int a, int b) {
+    System.out.println("direct2 " + a + " " + b);
+  }
+
+  private void direct3(int a, int b, int c) {
+    System.out.println("direct3 " + a + " " + b + " " + c);
+  }
+
+  private void direct4(int a, int b, int c, int d) {
+    System.out.println("direct4 " + a + " " + b + " " + c + " " + d);
+  }
+
+  private void directRange(int a, int b, int c, int d, int e, int f) {
+    System.out.println("directRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+  }
+
+  public static void directMethods() {
+    Invoke instance = new Invoke();
+    instance.direct0();
+    instance.direct1(1);
+    instance.direct2(1, 2);
+    instance.direct3(1, 2, 3);
+    instance.direct4(1, 2, 3, 4);
+    instance.directRange(1, 2, 3, 4, 5, 6);
+  }
+
+  public void interface0() {
+    System.out.println("interface0");
+  }
+
+  public void interface1(int a) {
+    System.out.println("interface1 " + a);
+  }
+
+  public void interface2(int a, int b) {
+    System.out.println("interface2 " + a + " " + b);
+  }
+
+  public void interface3(int a, int b, int c) {
+    System.out.println("interface3 " + a + " " + b + " " + c);
+  }
+
+  public void interface4(int a, int b, int c, int d) {
+    System.out.println("interface4 " + a + " " + b + " " + c + " " + d);
+  }
+
+  public void interface5(int a, int b, int c, int d, int e) {
+    System.out.println("interface5 " + a + " " + b + " " + c + " " + d + " " + e);
+  }
+
+  public void interfaceRange(int a, int b, int c, int d, int e, int f) {
+    System.out.println("interfaceRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+  }
+
+  public static void interfaceMethods(InvokeInterface i) {
+    i.interface0();
+    i.interface1(1);
+    i.interface2(1, 2);
+    i.interface3(1, 2, 3);
+    i.interface4(1, 2, 3, 4);
+    i.interfaceRange(1, 2, 3, 4, 5, 6);
+  }
+
+  public void virtual0() {
+    System.out.println("virtual0");
+  }
+
+  public void virtual1(int a) {
+    System.out.println("virtual1 " + a);
+  }
+
+  public void virtual2(int a, int b) {
+    System.out.println("virtual2 " + a + " " + b);
+  }
+
+  public void virtual3(int a, int b, int c) {
+    System.out.println("virtual3 " + a + " " + b + " " + c);
+  }
+
+  public void virtual4(int a, int b, int c, int d) {
+    System.out.println("virtual4 " + a + " " + b + " " + c + " " + d);
+  }
+
+  public void virtual5(int a, int b, int c, int d, int e) {
+    System.out.println("virtual5 " + a + " " + b + " " + c + " " + d + " " + e);
+  }
+
+  public void virtualRange(int a, int b, int c, int d, int e, int f) {
+    System.out.println("virtualRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+  }
+
+  public static void virtualMethods() {
+    Invoke instance = new Invoke();
+    instance.virtual0();
+    instance.virtual1(1);
+    instance.virtual2(1, 2);
+    instance.virtual3(1, 2, 3);
+    instance.virtual4(1, 2, 3, 4);
+    instance.virtualRange(1, 2, 3, 4, 5, 6);
+  }
+
+  public void super0() {
+    super.super0();
+  }
+
+  public void super1(int a) {
+    super.super1(a);
+  }
+
+  public void super2(int a, int b) {
+    super.super2(a, b);
+  }
+
+  public void super3(int a, int b, int c) {
+    super.super3(a, b, c);
+  }
+
+  public void super4(int a, int b, int c, int d) {
+    super.super4(a, b, c, d);
+  }
+
+  public void super5(int a, int b, int c, int d, int e) {
+    super.super5(a, b, c, d, e);
+  }
+
+  public void superRange(int a, int b, int c, int d, int e, int f) {
+    super.superRange(a, b, c, d, e, f);
+  }
+
+  public static void superInvocations() {
+    Invoke instance = new Invoke();
+    instance.super0();
+    instance.super1(1);
+    instance.super2(1, 2);
+    instance.super3(1, 2, 3);
+    instance.super4(1, 2, 3, 4);
+    instance.superRange(1, 2, 3, 4, 5, 6);
+  }
+
+  public static void rangeInvoke0(int i, int j, double d, double e, long l) {
+    System.out.println("rangeInvoke0 i " + i + " j " + j + " d " + d + " e " + e + " l " + l);
+  }
+
+  public static void rangeInvoke1(double d, double e, int i, int j, long l) {
+    System.out.println("rangeInvoke1 i " + i + " j " + j + " d " + d + " e " + e + " l " + l);
+  }
+
+  public static void rangeInvoke2(long l, double d, double e, int i, int j) {
+    System.out.println("rangeInvoke2 i " + i + " j " + j + " d " + d + " e " + e + " l " + l);
+  }
+
+  public static void rangeInvokes() {
+    int i = 0;
+    int j = 2;
+    double d = 42.42;
+    double e = 43.43;
+    long l = 0x0000000F00000000L;
+    // Range invokes with shuffled argument orders.
+    rangeInvoke0(i, j, d, e, l);
+    rangeInvoke0(i, j, d, e, l);
+    // Different order.
+    rangeInvoke1(d, e, i, j, l);
+    rangeInvoke1(d, e, i, j, l);
+    // And different again.
+    rangeInvoke2(l, d, e, i, j);
+    rangeInvoke2(l, d, e, i, j);
+  }
+
+  public static void oneArgumentMethod(int i) {
+    System.out.println("oneArgumentMethod " + i);
+  }
+
+  public static void twoArgumentMethod(int i, int j) {
+    System.out.println("twoArgumentMethod " + i + " " + j);
+  }
+
+  public static void unusedArgument0(int i0, int i1) {
+    oneArgumentMethod(i0);
+  }
+
+  public static void unusedArgument1(int i0, int i1) {
+    oneArgumentMethod(i1);
+  }
+
+  public static void unusedArgumentRanged(int i0, int i1, int i2, int i3, int i4, int i5, int i6,
+      int i7, int i8, int i9, int i10, int i11, int i12, int i13, int i14, int i15, int i16) {
+    oneArgumentMethod(i16);
+    twoArgumentMethod(i16, i9);
+    twoArgumentMethod(i16, i10);
+    twoArgumentMethod(i16, i11);
+  }
+
+  public static void oneDoubleArgumentMethod(double d) {
+    System.out.println("oneDoubleArgumentMethod " + d);
+  }
+
+  public static void twoDoubleArgumentMethod(double d0, double d1) {
+    System.out.println("twoDoubleArgumentMethod " + d0 + " " + d1);
+  }
+
+  public static void unusedDoubleArgument0(double d0, double d1) {
+    oneDoubleArgumentMethod(d0);
+  }
+
+  public static void unusedDoubleArgument1(double d0, double d1) {
+    oneDoubleArgumentMethod(d1);
+  }
+
+  public static void unusedDoubleArgumentRanged(double d0, double d1, double d2, double d3,
+      double d4, double d5, double d6, double d7, double d8, double d9, double d10, double d11,
+      double d12, double d13, double d14, double d15, double d16) {
+    oneDoubleArgumentMethod(d16);
+    twoDoubleArgumentMethod(d16, d9);
+    twoDoubleArgumentMethod(d16, d10);
+    twoDoubleArgumentMethod(d16, d11);
+  }
+
+  public static void unusedArguments() {
+    unusedArgument0(0, 1);
+    unusedArgument1(2, 3);
+    unusedArgumentRanged(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
+    unusedDoubleArgument0(1.1, 2.2);
+    unusedDoubleArgument1(3.3, 4.4);
+    unusedDoubleArgumentRanged(
+        0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.1, 11.2, 12.3, 13.4, 14.5, 15.6, 16.6);
+  }
+
+  public static void rangeInvokesRepeatedArgument0(
+      int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
+    System.out.println("rangeInvokesRepeatedArgument0 " + i0 + " " + i1 + " " + i2 + " " + i3 +
+        " " + i4 + " " + i5 + " " + i6 + " " + i7);
+  }
+
+  public static void rangeInvokesRepeatedArgument() {
+    int i = 0;
+    int j = 1;
+    rangeInvokesRepeatedArgument0(i, j, i, j, i, j, i, j);
+    rangeInvokesRepeatedArgument0(i, j, j, j, j, j, j, j);
+  }
+
+  public static <T> T identity(T a) {
+    return a;
+  }
+
+  public static void printInt(int i) {
+    System.out.println("int: " + i);
+  }
+
+  public static void printDouble(double d) {
+    System.out.println("double: " + d);
+  }
+
+  public static void genericMethod() {
+    System.out.println("int: " + identity(42));
+    System.out.println("double: " + identity(42.42));
+    printInt(identity(32));
+    printDouble(identity(32.32));
+  }
+
+  public static void manyArgs(
+      int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10,
+      int i11, int i12, int i13, int i14, int i15, int i16, int i17, int i18, int i19, int i20,
+      int i21, int i22, int i23, int i24, int i25, int i26, int i27, int i28, int i29,
+      int i30, int i31, int i32, int i33, int i34, int i35, int i36, int i37, int i38,
+      int i39, int i40, int i41, int i42, int i43, int i44, int i45, int i46, int i47,
+      int i48, int i49, int i50, int i51, int i52, int i53, int i54, int i55, int i56,
+      int i57, int i58, int i59, int i60, int i61, int i62, int i63, int i64, int i65,
+      int i66, int i67, int i68, int i69, int i70, int i71, int i72, int i73, int i74,
+      int i75, int i76, int i77, int i78, int i79, int i80, int i81, int i82, int i83,
+      int i84, int i85, int i86, int i87, int i88, int i89, int i90, int i91, int i92,
+      int i93, int i94, int i95, int i96, int i97, int i98, int i99, int i100, int i101,
+      int i102, int i103, int i104, int i105, int i106, int i107, int i108, int i109, int i110,
+      int i111, int i112, int i113, int i114, int i115, int i116, int i117, int i118, int i119,
+      int i120, int i121, int i122, int i123, int i124, int i125, int i126, int i127, int i128,
+      int i129, int i130, int i131, int i132, int i133, int i134, int i135, int i136, int i137,
+      int i138, int i139, int i140, int i141, int i142, int i143, int i144, int i145, int i146,
+      int i147, int i148, int i149, int i150, int i151, int i152, int i153, int i154, int i155,
+      int i156, int i157, int i158, int i159, int i160, int i161, int i162, int i163, int i164,
+      int i165, int i166, int i167, int i168, int i169, int i170, int i171, int i172, int i173,
+      int i174, int i175, int i176, int i177, int i178, int i179, int i180, int i181, int i182,
+      int i183, int i184, int i185, int i186, int i187, int i188, int i189, int i190, int i191,
+      int i192, int i193, int i194, int i195, int i196, int i197, int i198, int i199, int i200,
+      int i201, int i202, int i203, int i204, int i205, int i206, int i207, int i208, int i209,
+      int i210, int i211, int i212, int i213, int i214, int i215, int i216, int i217, int i218,
+      int i219, int i220, int i221, int i222, int i223, int i224, int i225, int i226, int i227,
+      int i228, int i229, int i230, int i231, int i232, int i233, int i234, int i235, int i236,
+      int i237, int i238, int i239, int i240, int i241, int i242, int i243, int i244, int i245,
+      int i246, int i247, int i248, int i249, int i250, int i251, int i252, int i253, int i254) {
+    // This is here to defeat inlining at this point.
+    System.out.println(i254 + i253);
+  }
+
+  public static void rangedNoInlining(int i0, int i1, int i2, int i3, int i4, int i5) {
+    // This is here to defeat inlining at this point.
+    System.out.println(i0 + i1 + i2 + i3 + i4 + i5);
+  }
+
+  public static void rangeInvokeWithManyLocals(int i, int j, int k) {
+    int i0 = 0; int i1 = 1; int i2 = 2; int i3 = 3; int i4 = 4; int i5 = 5; int i6 = 6;
+    int i7 = 7; int i8 = 8; int i9 = 9; int i10 = 10; int i11 = 11; int i12 = 12;
+    int i13 = 13; int i14 = 14; int i15 = 15; int i16 = 16; int i17 = 17;
+    int i18 = 18; int i19 = 19; int i20 = 20; int i21 = 21; int i22 = 22;
+    int i23 = 23; int i24 = 24; int i25 = 25; int i26 = 26; int i27 = 27;
+    int i28 = 28; int i29 = 29; int i30 = 30; int i31 = 31; int i32 = 32;
+    int i33 = 33; int i34 = 34; int i35 = 35; int i36 = 36; int i37 = 37;
+    int i38 = 38; int i39 = 39; int i40 = 40; int i41 = 41; int i42 = 42;
+    int i43 = 43; int i44 = 44; int i45 = 45; int i46 = 46; int i47 = 47;
+    int i48 = 48; int i49 = 49; int i50 = 50; int i51 = 51; int i52 = 52;
+    int i53 = 53; int i54 = 54; int i55 = 55; int i56 = 56; int i57 = 57;
+    int i58 = 58; int i59 = 59; int i60 = 60; int i61 = 61; int i62 = 62;
+    int i63 = 63; int i64 = 64; int i65 = 65; int i66 = 66; int i67 = 67;
+    int i68 = 68; int i69 = 69; int i70 = 70; int i71 = 71; int i72 = 72;
+    int i73 = 73; int i74 = 74; int i75 = 75; int i76 = 76; int i77 = 77;
+    int i78 = 78; int i79 = 79; int i80 = 80; int i81 = 81; int i82 = 82;
+    int i83 = 83; int i84 = 84; int i85 = 85; int i86 = 86; int i87 = 87;
+    int i88 = 88; int i89 = 89; int i90 = 90; int i91 = 91; int i92 = 92;
+    int i93 = 93; int i94 = 94; int i95 = 95; int i96 = 96; int i97 = 97;
+    int i98 = 98; int i99 = 99; int i100 = 100; int i101 = 101; int i102 = 102;
+    int i103 = 103; int i104 = 104; int i105 = 105; int i106 = 106; int i107 = 107;
+    int i108 = 108; int i109 = 109; int i110 = 110; int i111 = 111; int i112 = 112;
+    int i113 = 113; int i114 = 114; int i115 = 115; int i116 = 116; int i117 = 117;
+    int i118 = 118; int i119 = 119; int i120 = 120; int i121 = 121; int i122 = 122;
+    int i123 = 123; int i124 = 124; int i125 = 125; int i126 = 126; int i127 = 127;
+    int i128 = 128; int i129 = 129; int i130 = 130; int i131 = 131; int i132 = 132;
+    int i133 = 133; int i134 = 134; int i135 = 135; int i136 = 136; int i137 = 137;
+    int i138 = 138; int i139 = 139; int i140 = 140; int i141 = 141; int i142 = 142;
+    int i143 = 143; int i144 = 144; int i145 = 145; int i146 = 146; int i147 = 147;
+    int i148 = 148; int i149 = 149; int i150 = 150; int i151 = 151; int i152 = 152;
+    int i153 = 153; int i154 = 154; int i155 = 155; int i156 = 156; int i157 = 157;
+    int i158 = 158; int i159 = 159; int i160 = 160; int i161 = 161; int i162 = 162;
+    int i163 = 163; int i164 = 164; int i165 = 165; int i166 = 166; int i167 = 167;
+    int i168 = 168; int i169 = 169; int i170 = 170; int i171 = 171; int i172 = 172;
+    int i173 = 173; int i174 = 174; int i175 = 175; int i176 = 176; int i177 = 177;
+    int i178 = 178; int i179 = 179; int i180 = 180; int i181 = 181; int i182 = 182;
+    int i183 = 183; int i184 = 184; int i185 = 185; int i186 = 186; int i187 = 187;
+    int i188 = 188; int i189 = 189; int i190 = 190; int i191 = 191; int i192 = 192;
+    int i193 = 193; int i194 = 194; int i195 = 195; int i196 = 196; int i197 = 197;
+    int i198 = 198; int i199 = 199; int i200 = 200; int i201 = 201; int i202 = 202;
+    int i203 = 203; int i204 = 204; int i205 = 205; int i206 = 206; int i207 = 207;
+    int i208 = 208; int i209 = 209; int i210 = 210; int i211 = 211; int i212 = 212;
+    int i213 = 213; int i214 = 214; int i215 = 215; int i216 = 216; int i217 = 217;
+    int i218 = 218; int i219 = 219; int i220 = 220; int i221 = 221; int i222 = 222;
+    int i223 = 223; int i224 = 224; int i225 = 225; int i226 = 226; int i227 = 227;
+    int i228 = 228; int i229 = 229; int i230 = 230; int i231 = 231; int i232 = 232;
+    int i233 = 233; int i234 = 234; int i235 = 235; int i236 = 236; int i237 = 237;
+    int i238 = 238; int i239 = 239; int i240 = 240; int i241 = 241; int i242 = 242;
+    int i243 = 243; int i244 = 244; int i245 = 245; int i246 = 246; int i247 = 247;
+    int i248 = 248; int i249 = 249; int i250 = 250; int i251 = 251; int i252 = 252;
+    int i253 = 253; int i254 = 254; int i255 = 255; int i256 = 256; int i257 = 257;
+    int i258 = 258; int i259 = 259; int i260 = 260;
+    manyArgs(
+        i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, i17, i18, i19, i20,
+        i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, i34, i35,
+        i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50,
+        i51, i52, i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65,
+        i66, i67, i68, i69, i70, i71, i72, i73, i74, i75, i76, i77, i78, i79, i80,
+        i81, i82, i83, i84, i85, i86, i87, i88, i89, i90, i91, i92, i93, i94, i95,
+        i96, i97, i98, i99, i100, i101, i102, i103, i104, i105, i106, i107, i108, i109, i110,
+        i111, i112, i113, i114, i115, i116, i117, i118, i119, i120, i121, i122, i123, i124, i125,
+        i126, i127, i128, i129, i130, i131, i132, i133, i134, i135, i136, i137, i138, i139, i140,
+        i141, i142, i143, i144, i145, i146, i147, i148, i149, i150, i151, i152, i153, i154, i155,
+        i156, i157, i158, i159, i160, i161, i162, i163, i164, i165, i166, i167, i168, i169, i170,
+        i171, i172, i173, i174, i175, i176, i177, i178, i179, i180, i181, i182, i183, i184, i185,
+        i186, i187, i188, i189, i190, i191, i192, i193, i194, i195, i196, i197, i198, i199, i200,
+        i201, i202, i203, i204, i205, i206, i207, i208, i209, i210, i211, i212, i213, i214, i215,
+        i216, i217, i218, i219, i220, i221, i222, i223, i224, i225, i226, i227, i228, i229, i230,
+        i231, i232, i233, i234, i235, i236, i237, i238, i239, i240, i241, i242, i243, i244, i245,
+        i246, i247, i248, i249, i250, i251, i252, i253, i254, i255, i256, i257, i258, i259, i260);
+    rangedNoInlining(i0, i1, i2, i3, i4, i5);
+  }
+
+  public static void main(String[] args) {
+    staticMethods();
+    directMethods();
+    interfaceMethods(new Invoke());
+    virtualMethods();
+    superInvocations();
+    rangeInvokes();
+    unusedArguments();
+    rangeInvokesRepeatedArgument();
+    genericMethod();
+    rangeInvokeWithManyLocals(1, 2, 3);
+  }
+}
diff --git a/src/test/examples/invoke/InvokeInterface.java b/src/test/examples/invoke/InvokeInterface.java
new file mode 100644
index 0000000..7f5a08e
--- /dev/null
+++ b/src/test/examples/invoke/InvokeInterface.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invoke;
+
+public interface InvokeInterface {
+  public void interface0();
+  public void interface1(int a);
+  public void interface2(int a, int b);
+  public void interface3(int a, int b, int c);
+  public void interface4(int a, int b, int c, int d);
+  public void interfaceRange(int a, int b, int c, int d, int e, int f);
+}
diff --git a/src/test/examples/invoke/SuperClass.java b/src/test/examples/invoke/SuperClass.java
new file mode 100644
index 0000000..c4f7ae5
--- /dev/null
+++ b/src/test/examples/invoke/SuperClass.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invoke;
+
+public class SuperClass {
+  public void super0() {
+    System.out.println("super0");
+  }
+
+  public void super1(int a) {
+    System.out.println("super1 " + a);
+  }
+
+  public void super2(int a, int b) {
+    System.out.println("super2 " + a + " " + b);
+  }
+
+  public void super3(int a, int b, int c) {
+    System.out.println("super3 " + a + " " + b + " " + c);
+  }
+
+  public void super4(int a, int b, int c, int d) {
+    System.out.println("super4 " + a + " " + b + " " + c + " " + d);
+  }
+
+  public void super5(int a, int b, int c, int d, int e) {
+    System.out.println("super5 " + a + " " + b + " " + c + " " + d + " " + e);
+  }
+
+  public void superRange(int a, int b, int c, int d, int e, int f) {
+    System.out.println("superRange " + a + " " + b + " " + c + " " + d + " " + e + " " + f);
+  }
+}
diff --git a/src/test/examples/invokeempty/ClassA.java b/src/test/examples/invokeempty/ClassA.java
new file mode 100644
index 0000000..50ac63c
--- /dev/null
+++ b/src/test/examples/invokeempty/ClassA.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokeempty;
+
+public class ClassA {
+
+  // This class has no constructor, by design.
+
+  public void aMethod() {
+    System.out.print("A");
+  }
+}
diff --git a/src/test/examples/invokeempty/ClassB.java b/src/test/examples/invokeempty/ClassB.java
new file mode 100644
index 0000000..305baa6
--- /dev/null
+++ b/src/test/examples/invokeempty/ClassB.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokeempty;
+
+public class ClassB {
+
+  // This class has no constructor, by design.
+
+  public void aMethod() {
+    System.out.print("B");
+  }
+}
diff --git a/src/test/examples/invokeempty/InvokeEmpty.java b/src/test/examples/invokeempty/InvokeEmpty.java
new file mode 100644
index 0000000..6160eb2
--- /dev/null
+++ b/src/test/examples/invokeempty/InvokeEmpty.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokeempty;
+
+public class InvokeEmpty {
+
+  public static void main(String[] args) {
+    ClassA anA = new ClassA();
+    ClassB anB = new ClassB();
+    anA.aMethod();
+    anB.aMethod();
+  }
+}
diff --git a/src/test/examples/jumbostring/JumboString.java b/src/test/examples/jumbostring/JumboString.java
new file mode 100644
index 0000000..9bfd9ca
--- /dev/null
+++ b/src/test/examples/jumbostring/JumboString.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package jumbostring;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+
+class JumboString {
+  public static void main(String[] args) {
+    // Make sure this string sorts after the field names and string values in the StringPoolX.java
+    // files to ensure this is a jumbo string.
+    System.out.println("zzzz - jumbo string");
+  }
+
+  // Code for generating the StringPoolX.java files.
+  //
+  // We only need to generate two files to get jumbo strings. Each file has 16k static final fields
+  // with values, and both the field name and the value will be in the string pool.
+  public static void generate() throws IOException {
+    int stringsPerFile = (1 << 14);
+    for (int fileNumber = 0; fileNumber < 2; fileNumber++) {
+      Path path = FileSystems.getDefault().getPath("StringPool" + fileNumber + ".java");
+      PrintStream out = new PrintStream(
+          Files.newOutputStream(path, StandardOpenOption.CREATE, StandardOpenOption.APPEND));
+
+      out.println(
+          "// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file");
+      out.println(
+          "// for details. All rights reserved. Use of this source code is governed by a");
+      out.println("// BSD-style license that can be found in the LICENSE file.");
+      out.println("package jumbostring;");
+      out.println();
+      out.println("class StringPool" + fileNumber + " {");
+
+      int offset = fileNumber * stringsPerFile;
+      for (int i = offset; i < offset + stringsPerFile; i++) {
+        out.println("  public static final String s" + i + " = \"" + i + "\";");
+      }
+      out.println("}");
+      out.close();
+    }
+  }
+}
diff --git a/src/test/examples/jumbostring/StringPool0.java b/src/test/examples/jumbostring/StringPool0.java
new file mode 100644
index 0000000..e6e3b4b
--- /dev/null
+++ b/src/test/examples/jumbostring/StringPool0.java
@@ -0,0 +1,16391 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package jumbostring;
+
+class StringPool0 {
+  public static final String s0 = "0";
+  public static final String s1 = "1";
+  public static final String s2 = "2";
+  public static final String s3 = "3";
+  public static final String s4 = "4";
+  public static final String s5 = "5";
+  public static final String s6 = "6";
+  public static final String s7 = "7";
+  public static final String s8 = "8";
+  public static final String s9 = "9";
+  public static final String s10 = "10";
+  public static final String s11 = "11";
+  public static final String s12 = "12";
+  public static final String s13 = "13";
+  public static final String s14 = "14";
+  public static final String s15 = "15";
+  public static final String s16 = "16";
+  public static final String s17 = "17";
+  public static final String s18 = "18";
+  public static final String s19 = "19";
+  public static final String s20 = "20";
+  public static final String s21 = "21";
+  public static final String s22 = "22";
+  public static final String s23 = "23";
+  public static final String s24 = "24";
+  public static final String s25 = "25";
+  public static final String s26 = "26";
+  public static final String s27 = "27";
+  public static final String s28 = "28";
+  public static final String s29 = "29";
+  public static final String s30 = "30";
+  public static final String s31 = "31";
+  public static final String s32 = "32";
+  public static final String s33 = "33";
+  public static final String s34 = "34";
+  public static final String s35 = "35";
+  public static final String s36 = "36";
+  public static final String s37 = "37";
+  public static final String s38 = "38";
+  public static final String s39 = "39";
+  public static final String s40 = "40";
+  public static final String s41 = "41";
+  public static final String s42 = "42";
+  public static final String s43 = "43";
+  public static final String s44 = "44";
+  public static final String s45 = "45";
+  public static final String s46 = "46";
+  public static final String s47 = "47";
+  public static final String s48 = "48";
+  public static final String s49 = "49";
+  public static final String s50 = "50";
+  public static final String s51 = "51";
+  public static final String s52 = "52";
+  public static final String s53 = "53";
+  public static final String s54 = "54";
+  public static final String s55 = "55";
+  public static final String s56 = "56";
+  public static final String s57 = "57";
+  public static final String s58 = "58";
+  public static final String s59 = "59";
+  public static final String s60 = "60";
+  public static final String s61 = "61";
+  public static final String s62 = "62";
+  public static final String s63 = "63";
+  public static final String s64 = "64";
+  public static final String s65 = "65";
+  public static final String s66 = "66";
+  public static final String s67 = "67";
+  public static final String s68 = "68";
+  public static final String s69 = "69";
+  public static final String s70 = "70";
+  public static final String s71 = "71";
+  public static final String s72 = "72";
+  public static final String s73 = "73";
+  public static final String s74 = "74";
+  public static final String s75 = "75";
+  public static final String s76 = "76";
+  public static final String s77 = "77";
+  public static final String s78 = "78";
+  public static final String s79 = "79";
+  public static final String s80 = "80";
+  public static final String s81 = "81";
+  public static final String s82 = "82";
+  public static final String s83 = "83";
+  public static final String s84 = "84";
+  public static final String s85 = "85";
+  public static final String s86 = "86";
+  public static final String s87 = "87";
+  public static final String s88 = "88";
+  public static final String s89 = "89";
+  public static final String s90 = "90";
+  public static final String s91 = "91";
+  public static final String s92 = "92";
+  public static final String s93 = "93";
+  public static final String s94 = "94";
+  public static final String s95 = "95";
+  public static final String s96 = "96";
+  public static final String s97 = "97";
+  public static final String s98 = "98";
+  public static final String s99 = "99";
+  public static final String s100 = "100";
+  public static final String s101 = "101";
+  public static final String s102 = "102";
+  public static final String s103 = "103";
+  public static final String s104 = "104";
+  public static final String s105 = "105";
+  public static final String s106 = "106";
+  public static final String s107 = "107";
+  public static final String s108 = "108";
+  public static final String s109 = "109";
+  public static final String s110 = "110";
+  public static final String s111 = "111";
+  public static final String s112 = "112";
+  public static final String s113 = "113";
+  public static final String s114 = "114";
+  public static final String s115 = "115";
+  public static final String s116 = "116";
+  public static final String s117 = "117";
+  public static final String s118 = "118";
+  public static final String s119 = "119";
+  public static final String s120 = "120";
+  public static final String s121 = "121";
+  public static final String s122 = "122";
+  public static final String s123 = "123";
+  public static final String s124 = "124";
+  public static final String s125 = "125";
+  public static final String s126 = "126";
+  public static final String s127 = "127";
+  public static final String s128 = "128";
+  public static final String s129 = "129";
+  public static final String s130 = "130";
+  public static final String s131 = "131";
+  public static final String s132 = "132";
+  public static final String s133 = "133";
+  public static final String s134 = "134";
+  public static final String s135 = "135";
+  public static final String s136 = "136";
+  public static final String s137 = "137";
+  public static final String s138 = "138";
+  public static final String s139 = "139";
+  public static final String s140 = "140";
+  public static final String s141 = "141";
+  public static final String s142 = "142";
+  public static final String s143 = "143";
+  public static final String s144 = "144";
+  public static final String s145 = "145";
+  public static final String s146 = "146";
+  public static final String s147 = "147";
+  public static final String s148 = "148";
+  public static final String s149 = "149";
+  public static final String s150 = "150";
+  public static final String s151 = "151";
+  public static final String s152 = "152";
+  public static final String s153 = "153";
+  public static final String s154 = "154";
+  public static final String s155 = "155";
+  public static final String s156 = "156";
+  public static final String s157 = "157";
+  public static final String s158 = "158";
+  public static final String s159 = "159";
+  public static final String s160 = "160";
+  public static final String s161 = "161";
+  public static final String s162 = "162";
+  public static final String s163 = "163";
+  public static final String s164 = "164";
+  public static final String s165 = "165";
+  public static final String s166 = "166";
+  public static final String s167 = "167";
+  public static final String s168 = "168";
+  public static final String s169 = "169";
+  public static final String s170 = "170";
+  public static final String s171 = "171";
+  public static final String s172 = "172";
+  public static final String s173 = "173";
+  public static final String s174 = "174";
+  public static final String s175 = "175";
+  public static final String s176 = "176";
+  public static final String s177 = "177";
+  public static final String s178 = "178";
+  public static final String s179 = "179";
+  public static final String s180 = "180";
+  public static final String s181 = "181";
+  public static final String s182 = "182";
+  public static final String s183 = "183";
+  public static final String s184 = "184";
+  public static final String s185 = "185";
+  public static final String s186 = "186";
+  public static final String s187 = "187";
+  public static final String s188 = "188";
+  public static final String s189 = "189";
+  public static final String s190 = "190";
+  public static final String s191 = "191";
+  public static final String s192 = "192";
+  public static final String s193 = "193";
+  public static final String s194 = "194";
+  public static final String s195 = "195";
+  public static final String s196 = "196";
+  public static final String s197 = "197";
+  public static final String s198 = "198";
+  public static final String s199 = "199";
+  public static final String s200 = "200";
+  public static final String s201 = "201";
+  public static final String s202 = "202";
+  public static final String s203 = "203";
+  public static final String s204 = "204";
+  public static final String s205 = "205";
+  public static final String s206 = "206";
+  public static final String s207 = "207";
+  public static final String s208 = "208";
+  public static final String s209 = "209";
+  public static final String s210 = "210";
+  public static final String s211 = "211";
+  public static final String s212 = "212";
+  public static final String s213 = "213";
+  public static final String s214 = "214";
+  public static final String s215 = "215";
+  public static final String s216 = "216";
+  public static final String s217 = "217";
+  public static final String s218 = "218";
+  public static final String s219 = "219";
+  public static final String s220 = "220";
+  public static final String s221 = "221";
+  public static final String s222 = "222";
+  public static final String s223 = "223";
+  public static final String s224 = "224";
+  public static final String s225 = "225";
+  public static final String s226 = "226";
+  public static final String s227 = "227";
+  public static final String s228 = "228";
+  public static final String s229 = "229";
+  public static final String s230 = "230";
+  public static final String s231 = "231";
+  public static final String s232 = "232";
+  public static final String s233 = "233";
+  public static final String s234 = "234";
+  public static final String s235 = "235";
+  public static final String s236 = "236";
+  public static final String s237 = "237";
+  public static final String s238 = "238";
+  public static final String s239 = "239";
+  public static final String s240 = "240";
+  public static final String s241 = "241";
+  public static final String s242 = "242";
+  public static final String s243 = "243";
+  public static final String s244 = "244";
+  public static final String s245 = "245";
+  public static final String s246 = "246";
+  public static final String s247 = "247";
+  public static final String s248 = "248";
+  public static final String s249 = "249";
+  public static final String s250 = "250";
+  public static final String s251 = "251";
+  public static final String s252 = "252";
+  public static final String s253 = "253";
+  public static final String s254 = "254";
+  public static final String s255 = "255";
+  public static final String s256 = "256";
+  public static final String s257 = "257";
+  public static final String s258 = "258";
+  public static final String s259 = "259";
+  public static final String s260 = "260";
+  public static final String s261 = "261";
+  public static final String s262 = "262";
+  public static final String s263 = "263";
+  public static final String s264 = "264";
+  public static final String s265 = "265";
+  public static final String s266 = "266";
+  public static final String s267 = "267";
+  public static final String s268 = "268";
+  public static final String s269 = "269";
+  public static final String s270 = "270";
+  public static final String s271 = "271";
+  public static final String s272 = "272";
+  public static final String s273 = "273";
+  public static final String s274 = "274";
+  public static final String s275 = "275";
+  public static final String s276 = "276";
+  public static final String s277 = "277";
+  public static final String s278 = "278";
+  public static final String s279 = "279";
+  public static final String s280 = "280";
+  public static final String s281 = "281";
+  public static final String s282 = "282";
+  public static final String s283 = "283";
+  public static final String s284 = "284";
+  public static final String s285 = "285";
+  public static final String s286 = "286";
+  public static final String s287 = "287";
+  public static final String s288 = "288";
+  public static final String s289 = "289";
+  public static final String s290 = "290";
+  public static final String s291 = "291";
+  public static final String s292 = "292";
+  public static final String s293 = "293";
+  public static final String s294 = "294";
+  public static final String s295 = "295";
+  public static final String s296 = "296";
+  public static final String s297 = "297";
+  public static final String s298 = "298";
+  public static final String s299 = "299";
+  public static final String s300 = "300";
+  public static final String s301 = "301";
+  public static final String s302 = "302";
+  public static final String s303 = "303";
+  public static final String s304 = "304";
+  public static final String s305 = "305";
+  public static final String s306 = "306";
+  public static final String s307 = "307";
+  public static final String s308 = "308";
+  public static final String s309 = "309";
+  public static final String s310 = "310";
+  public static final String s311 = "311";
+  public static final String s312 = "312";
+  public static final String s313 = "313";
+  public static final String s314 = "314";
+  public static final String s315 = "315";
+  public static final String s316 = "316";
+  public static final String s317 = "317";
+  public static final String s318 = "318";
+  public static final String s319 = "319";
+  public static final String s320 = "320";
+  public static final String s321 = "321";
+  public static final String s322 = "322";
+  public static final String s323 = "323";
+  public static final String s324 = "324";
+  public static final String s325 = "325";
+  public static final String s326 = "326";
+  public static final String s327 = "327";
+  public static final String s328 = "328";
+  public static final String s329 = "329";
+  public static final String s330 = "330";
+  public static final String s331 = "331";
+  public static final String s332 = "332";
+  public static final String s333 = "333";
+  public static final String s334 = "334";
+  public static final String s335 = "335";
+  public static final String s336 = "336";
+  public static final String s337 = "337";
+  public static final String s338 = "338";
+  public static final String s339 = "339";
+  public static final String s340 = "340";
+  public static final String s341 = "341";
+  public static final String s342 = "342";
+  public static final String s343 = "343";
+  public static final String s344 = "344";
+  public static final String s345 = "345";
+  public static final String s346 = "346";
+  public static final String s347 = "347";
+  public static final String s348 = "348";
+  public static final String s349 = "349";
+  public static final String s350 = "350";
+  public static final String s351 = "351";
+  public static final String s352 = "352";
+  public static final String s353 = "353";
+  public static final String s354 = "354";
+  public static final String s355 = "355";
+  public static final String s356 = "356";
+  public static final String s357 = "357";
+  public static final String s358 = "358";
+  public static final String s359 = "359";
+  public static final String s360 = "360";
+  public static final String s361 = "361";
+  public static final String s362 = "362";
+  public static final String s363 = "363";
+  public static final String s364 = "364";
+  public static final String s365 = "365";
+  public static final String s366 = "366";
+  public static final String s367 = "367";
+  public static final String s368 = "368";
+  public static final String s369 = "369";
+  public static final String s370 = "370";
+  public static final String s371 = "371";
+  public static final String s372 = "372";
+  public static final String s373 = "373";
+  public static final String s374 = "374";
+  public static final String s375 = "375";
+  public static final String s376 = "376";
+  public static final String s377 = "377";
+  public static final String s378 = "378";
+  public static final String s379 = "379";
+  public static final String s380 = "380";
+  public static final String s381 = "381";
+  public static final String s382 = "382";
+  public static final String s383 = "383";
+  public static final String s384 = "384";
+  public static final String s385 = "385";
+  public static final String s386 = "386";
+  public static final String s387 = "387";
+  public static final String s388 = "388";
+  public static final String s389 = "389";
+  public static final String s390 = "390";
+  public static final String s391 = "391";
+  public static final String s392 = "392";
+  public static final String s393 = "393";
+  public static final String s394 = "394";
+  public static final String s395 = "395";
+  public static final String s396 = "396";
+  public static final String s397 = "397";
+  public static final String s398 = "398";
+  public static final String s399 = "399";
+  public static final String s400 = "400";
+  public static final String s401 = "401";
+  public static final String s402 = "402";
+  public static final String s403 = "403";
+  public static final String s404 = "404";
+  public static final String s405 = "405";
+  public static final String s406 = "406";
+  public static final String s407 = "407";
+  public static final String s408 = "408";
+  public static final String s409 = "409";
+  public static final String s410 = "410";
+  public static final String s411 = "411";
+  public static final String s412 = "412";
+  public static final String s413 = "413";
+  public static final String s414 = "414";
+  public static final String s415 = "415";
+  public static final String s416 = "416";
+  public static final String s417 = "417";
+  public static final String s418 = "418";
+  public static final String s419 = "419";
+  public static final String s420 = "420";
+  public static final String s421 = "421";
+  public static final String s422 = "422";
+  public static final String s423 = "423";
+  public static final String s424 = "424";
+  public static final String s425 = "425";
+  public static final String s426 = "426";
+  public static final String s427 = "427";
+  public static final String s428 = "428";
+  public static final String s429 = "429";
+  public static final String s430 = "430";
+  public static final String s431 = "431";
+  public static final String s432 = "432";
+  public static final String s433 = "433";
+  public static final String s434 = "434";
+  public static final String s435 = "435";
+  public static final String s436 = "436";
+  public static final String s437 = "437";
+  public static final String s438 = "438";
+  public static final String s439 = "439";
+  public static final String s440 = "440";
+  public static final String s441 = "441";
+  public static final String s442 = "442";
+  public static final String s443 = "443";
+  public static final String s444 = "444";
+  public static final String s445 = "445";
+  public static final String s446 = "446";
+  public static final String s447 = "447";
+  public static final String s448 = "448";
+  public static final String s449 = "449";
+  public static final String s450 = "450";
+  public static final String s451 = "451";
+  public static final String s452 = "452";
+  public static final String s453 = "453";
+  public static final String s454 = "454";
+  public static final String s455 = "455";
+  public static final String s456 = "456";
+  public static final String s457 = "457";
+  public static final String s458 = "458";
+  public static final String s459 = "459";
+  public static final String s460 = "460";
+  public static final String s461 = "461";
+  public static final String s462 = "462";
+  public static final String s463 = "463";
+  public static final String s464 = "464";
+  public static final String s465 = "465";
+  public static final String s466 = "466";
+  public static final String s467 = "467";
+  public static final String s468 = "468";
+  public static final String s469 = "469";
+  public static final String s470 = "470";
+  public static final String s471 = "471";
+  public static final String s472 = "472";
+  public static final String s473 = "473";
+  public static final String s474 = "474";
+  public static final String s475 = "475";
+  public static final String s476 = "476";
+  public static final String s477 = "477";
+  public static final String s478 = "478";
+  public static final String s479 = "479";
+  public static final String s480 = "480";
+  public static final String s481 = "481";
+  public static final String s482 = "482";
+  public static final String s483 = "483";
+  public static final String s484 = "484";
+  public static final String s485 = "485";
+  public static final String s486 = "486";
+  public static final String s487 = "487";
+  public static final String s488 = "488";
+  public static final String s489 = "489";
+  public static final String s490 = "490";
+  public static final String s491 = "491";
+  public static final String s492 = "492";
+  public static final String s493 = "493";
+  public static final String s494 = "494";
+  public static final String s495 = "495";
+  public static final String s496 = "496";
+  public static final String s497 = "497";
+  public static final String s498 = "498";
+  public static final String s499 = "499";
+  public static final String s500 = "500";
+  public static final String s501 = "501";
+  public static final String s502 = "502";
+  public static final String s503 = "503";
+  public static final String s504 = "504";
+  public static final String s505 = "505";
+  public static final String s506 = "506";
+  public static final String s507 = "507";
+  public static final String s508 = "508";
+  public static final String s509 = "509";
+  public static final String s510 = "510";
+  public static final String s511 = "511";
+  public static final String s512 = "512";
+  public static final String s513 = "513";
+  public static final String s514 = "514";
+  public static final String s515 = "515";
+  public static final String s516 = "516";
+  public static final String s517 = "517";
+  public static final String s518 = "518";
+  public static final String s519 = "519";
+  public static final String s520 = "520";
+  public static final String s521 = "521";
+  public static final String s522 = "522";
+  public static final String s523 = "523";
+  public static final String s524 = "524";
+  public static final String s525 = "525";
+  public static final String s526 = "526";
+  public static final String s527 = "527";
+  public static final String s528 = "528";
+  public static final String s529 = "529";
+  public static final String s530 = "530";
+  public static final String s531 = "531";
+  public static final String s532 = "532";
+  public static final String s533 = "533";
+  public static final String s534 = "534";
+  public static final String s535 = "535";
+  public static final String s536 = "536";
+  public static final String s537 = "537";
+  public static final String s538 = "538";
+  public static final String s539 = "539";
+  public static final String s540 = "540";
+  public static final String s541 = "541";
+  public static final String s542 = "542";
+  public static final String s543 = "543";
+  public static final String s544 = "544";
+  public static final String s545 = "545";
+  public static final String s546 = "546";
+  public static final String s547 = "547";
+  public static final String s548 = "548";
+  public static final String s549 = "549";
+  public static final String s550 = "550";
+  public static final String s551 = "551";
+  public static final String s552 = "552";
+  public static final String s553 = "553";
+  public static final String s554 = "554";
+  public static final String s555 = "555";
+  public static final String s556 = "556";
+  public static final String s557 = "557";
+  public static final String s558 = "558";
+  public static final String s559 = "559";
+  public static final String s560 = "560";
+  public static final String s561 = "561";
+  public static final String s562 = "562";
+  public static final String s563 = "563";
+  public static final String s564 = "564";
+  public static final String s565 = "565";
+  public static final String s566 = "566";
+  public static final String s567 = "567";
+  public static final String s568 = "568";
+  public static final String s569 = "569";
+  public static final String s570 = "570";
+  public static final String s571 = "571";
+  public static final String s572 = "572";
+  public static final String s573 = "573";
+  public static final String s574 = "574";
+  public static final String s575 = "575";
+  public static final String s576 = "576";
+  public static final String s577 = "577";
+  public static final String s578 = "578";
+  public static final String s579 = "579";
+  public static final String s580 = "580";
+  public static final String s581 = "581";
+  public static final String s582 = "582";
+  public static final String s583 = "583";
+  public static final String s584 = "584";
+  public static final String s585 = "585";
+  public static final String s586 = "586";
+  public static final String s587 = "587";
+  public static final String s588 = "588";
+  public static final String s589 = "589";
+  public static final String s590 = "590";
+  public static final String s591 = "591";
+  public static final String s592 = "592";
+  public static final String s593 = "593";
+  public static final String s594 = "594";
+  public static final String s595 = "595";
+  public static final String s596 = "596";
+  public static final String s597 = "597";
+  public static final String s598 = "598";
+  public static final String s599 = "599";
+  public static final String s600 = "600";
+  public static final String s601 = "601";
+  public static final String s602 = "602";
+  public static final String s603 = "603";
+  public static final String s604 = "604";
+  public static final String s605 = "605";
+  public static final String s606 = "606";
+  public static final String s607 = "607";
+  public static final String s608 = "608";
+  public static final String s609 = "609";
+  public static final String s610 = "610";
+  public static final String s611 = "611";
+  public static final String s612 = "612";
+  public static final String s613 = "613";
+  public static final String s614 = "614";
+  public static final String s615 = "615";
+  public static final String s616 = "616";
+  public static final String s617 = "617";
+  public static final String s618 = "618";
+  public static final String s619 = "619";
+  public static final String s620 = "620";
+  public static final String s621 = "621";
+  public static final String s622 = "622";
+  public static final String s623 = "623";
+  public static final String s624 = "624";
+  public static final String s625 = "625";
+  public static final String s626 = "626";
+  public static final String s627 = "627";
+  public static final String s628 = "628";
+  public static final String s629 = "629";
+  public static final String s630 = "630";
+  public static final String s631 = "631";
+  public static final String s632 = "632";
+  public static final String s633 = "633";
+  public static final String s634 = "634";
+  public static final String s635 = "635";
+  public static final String s636 = "636";
+  public static final String s637 = "637";
+  public static final String s638 = "638";
+  public static final String s639 = "639";
+  public static final String s640 = "640";
+  public static final String s641 = "641";
+  public static final String s642 = "642";
+  public static final String s643 = "643";
+  public static final String s644 = "644";
+  public static final String s645 = "645";
+  public static final String s646 = "646";
+  public static final String s647 = "647";
+  public static final String s648 = "648";
+  public static final String s649 = "649";
+  public static final String s650 = "650";
+  public static final String s651 = "651";
+  public static final String s652 = "652";
+  public static final String s653 = "653";
+  public static final String s654 = "654";
+  public static final String s655 = "655";
+  public static final String s656 = "656";
+  public static final String s657 = "657";
+  public static final String s658 = "658";
+  public static final String s659 = "659";
+  public static final String s660 = "660";
+  public static final String s661 = "661";
+  public static final String s662 = "662";
+  public static final String s663 = "663";
+  public static final String s664 = "664";
+  public static final String s665 = "665";
+  public static final String s666 = "666";
+  public static final String s667 = "667";
+  public static final String s668 = "668";
+  public static final String s669 = "669";
+  public static final String s670 = "670";
+  public static final String s671 = "671";
+  public static final String s672 = "672";
+  public static final String s673 = "673";
+  public static final String s674 = "674";
+  public static final String s675 = "675";
+  public static final String s676 = "676";
+  public static final String s677 = "677";
+  public static final String s678 = "678";
+  public static final String s679 = "679";
+  public static final String s680 = "680";
+  public static final String s681 = "681";
+  public static final String s682 = "682";
+  public static final String s683 = "683";
+  public static final String s684 = "684";
+  public static final String s685 = "685";
+  public static final String s686 = "686";
+  public static final String s687 = "687";
+  public static final String s688 = "688";
+  public static final String s689 = "689";
+  public static final String s690 = "690";
+  public static final String s691 = "691";
+  public static final String s692 = "692";
+  public static final String s693 = "693";
+  public static final String s694 = "694";
+  public static final String s695 = "695";
+  public static final String s696 = "696";
+  public static final String s697 = "697";
+  public static final String s698 = "698";
+  public static final String s699 = "699";
+  public static final String s700 = "700";
+  public static final String s701 = "701";
+  public static final String s702 = "702";
+  public static final String s703 = "703";
+  public static final String s704 = "704";
+  public static final String s705 = "705";
+  public static final String s706 = "706";
+  public static final String s707 = "707";
+  public static final String s708 = "708";
+  public static final String s709 = "709";
+  public static final String s710 = "710";
+  public static final String s711 = "711";
+  public static final String s712 = "712";
+  public static final String s713 = "713";
+  public static final String s714 = "714";
+  public static final String s715 = "715";
+  public static final String s716 = "716";
+  public static final String s717 = "717";
+  public static final String s718 = "718";
+  public static final String s719 = "719";
+  public static final String s720 = "720";
+  public static final String s721 = "721";
+  public static final String s722 = "722";
+  public static final String s723 = "723";
+  public static final String s724 = "724";
+  public static final String s725 = "725";
+  public static final String s726 = "726";
+  public static final String s727 = "727";
+  public static final String s728 = "728";
+  public static final String s729 = "729";
+  public static final String s730 = "730";
+  public static final String s731 = "731";
+  public static final String s732 = "732";
+  public static final String s733 = "733";
+  public static final String s734 = "734";
+  public static final String s735 = "735";
+  public static final String s736 = "736";
+  public static final String s737 = "737";
+  public static final String s738 = "738";
+  public static final String s739 = "739";
+  public static final String s740 = "740";
+  public static final String s741 = "741";
+  public static final String s742 = "742";
+  public static final String s743 = "743";
+  public static final String s744 = "744";
+  public static final String s745 = "745";
+  public static final String s746 = "746";
+  public static final String s747 = "747";
+  public static final String s748 = "748";
+  public static final String s749 = "749";
+  public static final String s750 = "750";
+  public static final String s751 = "751";
+  public static final String s752 = "752";
+  public static final String s753 = "753";
+  public static final String s754 = "754";
+  public static final String s755 = "755";
+  public static final String s756 = "756";
+  public static final String s757 = "757";
+  public static final String s758 = "758";
+  public static final String s759 = "759";
+  public static final String s760 = "760";
+  public static final String s761 = "761";
+  public static final String s762 = "762";
+  public static final String s763 = "763";
+  public static final String s764 = "764";
+  public static final String s765 = "765";
+  public static final String s766 = "766";
+  public static final String s767 = "767";
+  public static final String s768 = "768";
+  public static final String s769 = "769";
+  public static final String s770 = "770";
+  public static final String s771 = "771";
+  public static final String s772 = "772";
+  public static final String s773 = "773";
+  public static final String s774 = "774";
+  public static final String s775 = "775";
+  public static final String s776 = "776";
+  public static final String s777 = "777";
+  public static final String s778 = "778";
+  public static final String s779 = "779";
+  public static final String s780 = "780";
+  public static final String s781 = "781";
+  public static final String s782 = "782";
+  public static final String s783 = "783";
+  public static final String s784 = "784";
+  public static final String s785 = "785";
+  public static final String s786 = "786";
+  public static final String s787 = "787";
+  public static final String s788 = "788";
+  public static final String s789 = "789";
+  public static final String s790 = "790";
+  public static final String s791 = "791";
+  public static final String s792 = "792";
+  public static final String s793 = "793";
+  public static final String s794 = "794";
+  public static final String s795 = "795";
+  public static final String s796 = "796";
+  public static final String s797 = "797";
+  public static final String s798 = "798";
+  public static final String s799 = "799";
+  public static final String s800 = "800";
+  public static final String s801 = "801";
+  public static final String s802 = "802";
+  public static final String s803 = "803";
+  public static final String s804 = "804";
+  public static final String s805 = "805";
+  public static final String s806 = "806";
+  public static final String s807 = "807";
+  public static final String s808 = "808";
+  public static final String s809 = "809";
+  public static final String s810 = "810";
+  public static final String s811 = "811";
+  public static final String s812 = "812";
+  public static final String s813 = "813";
+  public static final String s814 = "814";
+  public static final String s815 = "815";
+  public static final String s816 = "816";
+  public static final String s817 = "817";
+  public static final String s818 = "818";
+  public static final String s819 = "819";
+  public static final String s820 = "820";
+  public static final String s821 = "821";
+  public static final String s822 = "822";
+  public static final String s823 = "823";
+  public static final String s824 = "824";
+  public static final String s825 = "825";
+  public static final String s826 = "826";
+  public static final String s827 = "827";
+  public static final String s828 = "828";
+  public static final String s829 = "829";
+  public static final String s830 = "830";
+  public static final String s831 = "831";
+  public static final String s832 = "832";
+  public static final String s833 = "833";
+  public static final String s834 = "834";
+  public static final String s835 = "835";
+  public static final String s836 = "836";
+  public static final String s837 = "837";
+  public static final String s838 = "838";
+  public static final String s839 = "839";
+  public static final String s840 = "840";
+  public static final String s841 = "841";
+  public static final String s842 = "842";
+  public static final String s843 = "843";
+  public static final String s844 = "844";
+  public static final String s845 = "845";
+  public static final String s846 = "846";
+  public static final String s847 = "847";
+  public static final String s848 = "848";
+  public static final String s849 = "849";
+  public static final String s850 = "850";
+  public static final String s851 = "851";
+  public static final String s852 = "852";
+  public static final String s853 = "853";
+  public static final String s854 = "854";
+  public static final String s855 = "855";
+  public static final String s856 = "856";
+  public static final String s857 = "857";
+  public static final String s858 = "858";
+  public static final String s859 = "859";
+  public static final String s860 = "860";
+  public static final String s861 = "861";
+  public static final String s862 = "862";
+  public static final String s863 = "863";
+  public static final String s864 = "864";
+  public static final String s865 = "865";
+  public static final String s866 = "866";
+  public static final String s867 = "867";
+  public static final String s868 = "868";
+  public static final String s869 = "869";
+  public static final String s870 = "870";
+  public static final String s871 = "871";
+  public static final String s872 = "872";
+  public static final String s873 = "873";
+  public static final String s874 = "874";
+  public static final String s875 = "875";
+  public static final String s876 = "876";
+  public static final String s877 = "877";
+  public static final String s878 = "878";
+  public static final String s879 = "879";
+  public static final String s880 = "880";
+  public static final String s881 = "881";
+  public static final String s882 = "882";
+  public static final String s883 = "883";
+  public static final String s884 = "884";
+  public static final String s885 = "885";
+  public static final String s886 = "886";
+  public static final String s887 = "887";
+  public static final String s888 = "888";
+  public static final String s889 = "889";
+  public static final String s890 = "890";
+  public static final String s891 = "891";
+  public static final String s892 = "892";
+  public static final String s893 = "893";
+  public static final String s894 = "894";
+  public static final String s895 = "895";
+  public static final String s896 = "896";
+  public static final String s897 = "897";
+  public static final String s898 = "898";
+  public static final String s899 = "899";
+  public static final String s900 = "900";
+  public static final String s901 = "901";
+  public static final String s902 = "902";
+  public static final String s903 = "903";
+  public static final String s904 = "904";
+  public static final String s905 = "905";
+  public static final String s906 = "906";
+  public static final String s907 = "907";
+  public static final String s908 = "908";
+  public static final String s909 = "909";
+  public static final String s910 = "910";
+  public static final String s911 = "911";
+  public static final String s912 = "912";
+  public static final String s913 = "913";
+  public static final String s914 = "914";
+  public static final String s915 = "915";
+  public static final String s916 = "916";
+  public static final String s917 = "917";
+  public static final String s918 = "918";
+  public static final String s919 = "919";
+  public static final String s920 = "920";
+  public static final String s921 = "921";
+  public static final String s922 = "922";
+  public static final String s923 = "923";
+  public static final String s924 = "924";
+  public static final String s925 = "925";
+  public static final String s926 = "926";
+  public static final String s927 = "927";
+  public static final String s928 = "928";
+  public static final String s929 = "929";
+  public static final String s930 = "930";
+  public static final String s931 = "931";
+  public static final String s932 = "932";
+  public static final String s933 = "933";
+  public static final String s934 = "934";
+  public static final String s935 = "935";
+  public static final String s936 = "936";
+  public static final String s937 = "937";
+  public static final String s938 = "938";
+  public static final String s939 = "939";
+  public static final String s940 = "940";
+  public static final String s941 = "941";
+  public static final String s942 = "942";
+  public static final String s943 = "943";
+  public static final String s944 = "944";
+  public static final String s945 = "945";
+  public static final String s946 = "946";
+  public static final String s947 = "947";
+  public static final String s948 = "948";
+  public static final String s949 = "949";
+  public static final String s950 = "950";
+  public static final String s951 = "951";
+  public static final String s952 = "952";
+  public static final String s953 = "953";
+  public static final String s954 = "954";
+  public static final String s955 = "955";
+  public static final String s956 = "956";
+  public static final String s957 = "957";
+  public static final String s958 = "958";
+  public static final String s959 = "959";
+  public static final String s960 = "960";
+  public static final String s961 = "961";
+  public static final String s962 = "962";
+  public static final String s963 = "963";
+  public static final String s964 = "964";
+  public static final String s965 = "965";
+  public static final String s966 = "966";
+  public static final String s967 = "967";
+  public static final String s968 = "968";
+  public static final String s969 = "969";
+  public static final String s970 = "970";
+  public static final String s971 = "971";
+  public static final String s972 = "972";
+  public static final String s973 = "973";
+  public static final String s974 = "974";
+  public static final String s975 = "975";
+  public static final String s976 = "976";
+  public static final String s977 = "977";
+  public static final String s978 = "978";
+  public static final String s979 = "979";
+  public static final String s980 = "980";
+  public static final String s981 = "981";
+  public static final String s982 = "982";
+  public static final String s983 = "983";
+  public static final String s984 = "984";
+  public static final String s985 = "985";
+  public static final String s986 = "986";
+  public static final String s987 = "987";
+  public static final String s988 = "988";
+  public static final String s989 = "989";
+  public static final String s990 = "990";
+  public static final String s991 = "991";
+  public static final String s992 = "992";
+  public static final String s993 = "993";
+  public static final String s994 = "994";
+  public static final String s995 = "995";
+  public static final String s996 = "996";
+  public static final String s997 = "997";
+  public static final String s998 = "998";
+  public static final String s999 = "999";
+  public static final String s1000 = "1000";
+  public static final String s1001 = "1001";
+  public static final String s1002 = "1002";
+  public static final String s1003 = "1003";
+  public static final String s1004 = "1004";
+  public static final String s1005 = "1005";
+  public static final String s1006 = "1006";
+  public static final String s1007 = "1007";
+  public static final String s1008 = "1008";
+  public static final String s1009 = "1009";
+  public static final String s1010 = "1010";
+  public static final String s1011 = "1011";
+  public static final String s1012 = "1012";
+  public static final String s1013 = "1013";
+  public static final String s1014 = "1014";
+  public static final String s1015 = "1015";
+  public static final String s1016 = "1016";
+  public static final String s1017 = "1017";
+  public static final String s1018 = "1018";
+  public static final String s1019 = "1019";
+  public static final String s1020 = "1020";
+  public static final String s1021 = "1021";
+  public static final String s1022 = "1022";
+  public static final String s1023 = "1023";
+  public static final String s1024 = "1024";
+  public static final String s1025 = "1025";
+  public static final String s1026 = "1026";
+  public static final String s1027 = "1027";
+  public static final String s1028 = "1028";
+  public static final String s1029 = "1029";
+  public static final String s1030 = "1030";
+  public static final String s1031 = "1031";
+  public static final String s1032 = "1032";
+  public static final String s1033 = "1033";
+  public static final String s1034 = "1034";
+  public static final String s1035 = "1035";
+  public static final String s1036 = "1036";
+  public static final String s1037 = "1037";
+  public static final String s1038 = "1038";
+  public static final String s1039 = "1039";
+  public static final String s1040 = "1040";
+  public static final String s1041 = "1041";
+  public static final String s1042 = "1042";
+  public static final String s1043 = "1043";
+  public static final String s1044 = "1044";
+  public static final String s1045 = "1045";
+  public static final String s1046 = "1046";
+  public static final String s1047 = "1047";
+  public static final String s1048 = "1048";
+  public static final String s1049 = "1049";
+  public static final String s1050 = "1050";
+  public static final String s1051 = "1051";
+  public static final String s1052 = "1052";
+  public static final String s1053 = "1053";
+  public static final String s1054 = "1054";
+  public static final String s1055 = "1055";
+  public static final String s1056 = "1056";
+  public static final String s1057 = "1057";
+  public static final String s1058 = "1058";
+  public static final String s1059 = "1059";
+  public static final String s1060 = "1060";
+  public static final String s1061 = "1061";
+  public static final String s1062 = "1062";
+  public static final String s1063 = "1063";
+  public static final String s1064 = "1064";
+  public static final String s1065 = "1065";
+  public static final String s1066 = "1066";
+  public static final String s1067 = "1067";
+  public static final String s1068 = "1068";
+  public static final String s1069 = "1069";
+  public static final String s1070 = "1070";
+  public static final String s1071 = "1071";
+  public static final String s1072 = "1072";
+  public static final String s1073 = "1073";
+  public static final String s1074 = "1074";
+  public static final String s1075 = "1075";
+  public static final String s1076 = "1076";
+  public static final String s1077 = "1077";
+  public static final String s1078 = "1078";
+  public static final String s1079 = "1079";
+  public static final String s1080 = "1080";
+  public static final String s1081 = "1081";
+  public static final String s1082 = "1082";
+  public static final String s1083 = "1083";
+  public static final String s1084 = "1084";
+  public static final String s1085 = "1085";
+  public static final String s1086 = "1086";
+  public static final String s1087 = "1087";
+  public static final String s1088 = "1088";
+  public static final String s1089 = "1089";
+  public static final String s1090 = "1090";
+  public static final String s1091 = "1091";
+  public static final String s1092 = "1092";
+  public static final String s1093 = "1093";
+  public static final String s1094 = "1094";
+  public static final String s1095 = "1095";
+  public static final String s1096 = "1096";
+  public static final String s1097 = "1097";
+  public static final String s1098 = "1098";
+  public static final String s1099 = "1099";
+  public static final String s1100 = "1100";
+  public static final String s1101 = "1101";
+  public static final String s1102 = "1102";
+  public static final String s1103 = "1103";
+  public static final String s1104 = "1104";
+  public static final String s1105 = "1105";
+  public static final String s1106 = "1106";
+  public static final String s1107 = "1107";
+  public static final String s1108 = "1108";
+  public static final String s1109 = "1109";
+  public static final String s1110 = "1110";
+  public static final String s1111 = "1111";
+  public static final String s1112 = "1112";
+  public static final String s1113 = "1113";
+  public static final String s1114 = "1114";
+  public static final String s1115 = "1115";
+  public static final String s1116 = "1116";
+  public static final String s1117 = "1117";
+  public static final String s1118 = "1118";
+  public static final String s1119 = "1119";
+  public static final String s1120 = "1120";
+  public static final String s1121 = "1121";
+  public static final String s1122 = "1122";
+  public static final String s1123 = "1123";
+  public static final String s1124 = "1124";
+  public static final String s1125 = "1125";
+  public static final String s1126 = "1126";
+  public static final String s1127 = "1127";
+  public static final String s1128 = "1128";
+  public static final String s1129 = "1129";
+  public static final String s1130 = "1130";
+  public static final String s1131 = "1131";
+  public static final String s1132 = "1132";
+  public static final String s1133 = "1133";
+  public static final String s1134 = "1134";
+  public static final String s1135 = "1135";
+  public static final String s1136 = "1136";
+  public static final String s1137 = "1137";
+  public static final String s1138 = "1138";
+  public static final String s1139 = "1139";
+  public static final String s1140 = "1140";
+  public static final String s1141 = "1141";
+  public static final String s1142 = "1142";
+  public static final String s1143 = "1143";
+  public static final String s1144 = "1144";
+  public static final String s1145 = "1145";
+  public static final String s1146 = "1146";
+  public static final String s1147 = "1147";
+  public static final String s1148 = "1148";
+  public static final String s1149 = "1149";
+  public static final String s1150 = "1150";
+  public static final String s1151 = "1151";
+  public static final String s1152 = "1152";
+  public static final String s1153 = "1153";
+  public static final String s1154 = "1154";
+  public static final String s1155 = "1155";
+  public static final String s1156 = "1156";
+  public static final String s1157 = "1157";
+  public static final String s1158 = "1158";
+  public static final String s1159 = "1159";
+  public static final String s1160 = "1160";
+  public static final String s1161 = "1161";
+  public static final String s1162 = "1162";
+  public static final String s1163 = "1163";
+  public static final String s1164 = "1164";
+  public static final String s1165 = "1165";
+  public static final String s1166 = "1166";
+  public static final String s1167 = "1167";
+  public static final String s1168 = "1168";
+  public static final String s1169 = "1169";
+  public static final String s1170 = "1170";
+  public static final String s1171 = "1171";
+  public static final String s1172 = "1172";
+  public static final String s1173 = "1173";
+  public static final String s1174 = "1174";
+  public static final String s1175 = "1175";
+  public static final String s1176 = "1176";
+  public static final String s1177 = "1177";
+  public static final String s1178 = "1178";
+  public static final String s1179 = "1179";
+  public static final String s1180 = "1180";
+  public static final String s1181 = "1181";
+  public static final String s1182 = "1182";
+  public static final String s1183 = "1183";
+  public static final String s1184 = "1184";
+  public static final String s1185 = "1185";
+  public static final String s1186 = "1186";
+  public static final String s1187 = "1187";
+  public static final String s1188 = "1188";
+  public static final String s1189 = "1189";
+  public static final String s1190 = "1190";
+  public static final String s1191 = "1191";
+  public static final String s1192 = "1192";
+  public static final String s1193 = "1193";
+  public static final String s1194 = "1194";
+  public static final String s1195 = "1195";
+  public static final String s1196 = "1196";
+  public static final String s1197 = "1197";
+  public static final String s1198 = "1198";
+  public static final String s1199 = "1199";
+  public static final String s1200 = "1200";
+  public static final String s1201 = "1201";
+  public static final String s1202 = "1202";
+  public static final String s1203 = "1203";
+  public static final String s1204 = "1204";
+  public static final String s1205 = "1205";
+  public static final String s1206 = "1206";
+  public static final String s1207 = "1207";
+  public static final String s1208 = "1208";
+  public static final String s1209 = "1209";
+  public static final String s1210 = "1210";
+  public static final String s1211 = "1211";
+  public static final String s1212 = "1212";
+  public static final String s1213 = "1213";
+  public static final String s1214 = "1214";
+  public static final String s1215 = "1215";
+  public static final String s1216 = "1216";
+  public static final String s1217 = "1217";
+  public static final String s1218 = "1218";
+  public static final String s1219 = "1219";
+  public static final String s1220 = "1220";
+  public static final String s1221 = "1221";
+  public static final String s1222 = "1222";
+  public static final String s1223 = "1223";
+  public static final String s1224 = "1224";
+  public static final String s1225 = "1225";
+  public static final String s1226 = "1226";
+  public static final String s1227 = "1227";
+  public static final String s1228 = "1228";
+  public static final String s1229 = "1229";
+  public static final String s1230 = "1230";
+  public static final String s1231 = "1231";
+  public static final String s1232 = "1232";
+  public static final String s1233 = "1233";
+  public static final String s1234 = "1234";
+  public static final String s1235 = "1235";
+  public static final String s1236 = "1236";
+  public static final String s1237 = "1237";
+  public static final String s1238 = "1238";
+  public static final String s1239 = "1239";
+  public static final String s1240 = "1240";
+  public static final String s1241 = "1241";
+  public static final String s1242 = "1242";
+  public static final String s1243 = "1243";
+  public static final String s1244 = "1244";
+  public static final String s1245 = "1245";
+  public static final String s1246 = "1246";
+  public static final String s1247 = "1247";
+  public static final String s1248 = "1248";
+  public static final String s1249 = "1249";
+  public static final String s1250 = "1250";
+  public static final String s1251 = "1251";
+  public static final String s1252 = "1252";
+  public static final String s1253 = "1253";
+  public static final String s1254 = "1254";
+  public static final String s1255 = "1255";
+  public static final String s1256 = "1256";
+  public static final String s1257 = "1257";
+  public static final String s1258 = "1258";
+  public static final String s1259 = "1259";
+  public static final String s1260 = "1260";
+  public static final String s1261 = "1261";
+  public static final String s1262 = "1262";
+  public static final String s1263 = "1263";
+  public static final String s1264 = "1264";
+  public static final String s1265 = "1265";
+  public static final String s1266 = "1266";
+  public static final String s1267 = "1267";
+  public static final String s1268 = "1268";
+  public static final String s1269 = "1269";
+  public static final String s1270 = "1270";
+  public static final String s1271 = "1271";
+  public static final String s1272 = "1272";
+  public static final String s1273 = "1273";
+  public static final String s1274 = "1274";
+  public static final String s1275 = "1275";
+  public static final String s1276 = "1276";
+  public static final String s1277 = "1277";
+  public static final String s1278 = "1278";
+  public static final String s1279 = "1279";
+  public static final String s1280 = "1280";
+  public static final String s1281 = "1281";
+  public static final String s1282 = "1282";
+  public static final String s1283 = "1283";
+  public static final String s1284 = "1284";
+  public static final String s1285 = "1285";
+  public static final String s1286 = "1286";
+  public static final String s1287 = "1287";
+  public static final String s1288 = "1288";
+  public static final String s1289 = "1289";
+  public static final String s1290 = "1290";
+  public static final String s1291 = "1291";
+  public static final String s1292 = "1292";
+  public static final String s1293 = "1293";
+  public static final String s1294 = "1294";
+  public static final String s1295 = "1295";
+  public static final String s1296 = "1296";
+  public static final String s1297 = "1297";
+  public static final String s1298 = "1298";
+  public static final String s1299 = "1299";
+  public static final String s1300 = "1300";
+  public static final String s1301 = "1301";
+  public static final String s1302 = "1302";
+  public static final String s1303 = "1303";
+  public static final String s1304 = "1304";
+  public static final String s1305 = "1305";
+  public static final String s1306 = "1306";
+  public static final String s1307 = "1307";
+  public static final String s1308 = "1308";
+  public static final String s1309 = "1309";
+  public static final String s1310 = "1310";
+  public static final String s1311 = "1311";
+  public static final String s1312 = "1312";
+  public static final String s1313 = "1313";
+  public static final String s1314 = "1314";
+  public static final String s1315 = "1315";
+  public static final String s1316 = "1316";
+  public static final String s1317 = "1317";
+  public static final String s1318 = "1318";
+  public static final String s1319 = "1319";
+  public static final String s1320 = "1320";
+  public static final String s1321 = "1321";
+  public static final String s1322 = "1322";
+  public static final String s1323 = "1323";
+  public static final String s1324 = "1324";
+  public static final String s1325 = "1325";
+  public static final String s1326 = "1326";
+  public static final String s1327 = "1327";
+  public static final String s1328 = "1328";
+  public static final String s1329 = "1329";
+  public static final String s1330 = "1330";
+  public static final String s1331 = "1331";
+  public static final String s1332 = "1332";
+  public static final String s1333 = "1333";
+  public static final String s1334 = "1334";
+  public static final String s1335 = "1335";
+  public static final String s1336 = "1336";
+  public static final String s1337 = "1337";
+  public static final String s1338 = "1338";
+  public static final String s1339 = "1339";
+  public static final String s1340 = "1340";
+  public static final String s1341 = "1341";
+  public static final String s1342 = "1342";
+  public static final String s1343 = "1343";
+  public static final String s1344 = "1344";
+  public static final String s1345 = "1345";
+  public static final String s1346 = "1346";
+  public static final String s1347 = "1347";
+  public static final String s1348 = "1348";
+  public static final String s1349 = "1349";
+  public static final String s1350 = "1350";
+  public static final String s1351 = "1351";
+  public static final String s1352 = "1352";
+  public static final String s1353 = "1353";
+  public static final String s1354 = "1354";
+  public static final String s1355 = "1355";
+  public static final String s1356 = "1356";
+  public static final String s1357 = "1357";
+  public static final String s1358 = "1358";
+  public static final String s1359 = "1359";
+  public static final String s1360 = "1360";
+  public static final String s1361 = "1361";
+  public static final String s1362 = "1362";
+  public static final String s1363 = "1363";
+  public static final String s1364 = "1364";
+  public static final String s1365 = "1365";
+  public static final String s1366 = "1366";
+  public static final String s1367 = "1367";
+  public static final String s1368 = "1368";
+  public static final String s1369 = "1369";
+  public static final String s1370 = "1370";
+  public static final String s1371 = "1371";
+  public static final String s1372 = "1372";
+  public static final String s1373 = "1373";
+  public static final String s1374 = "1374";
+  public static final String s1375 = "1375";
+  public static final String s1376 = "1376";
+  public static final String s1377 = "1377";
+  public static final String s1378 = "1378";
+  public static final String s1379 = "1379";
+  public static final String s1380 = "1380";
+  public static final String s1381 = "1381";
+  public static final String s1382 = "1382";
+  public static final String s1383 = "1383";
+  public static final String s1384 = "1384";
+  public static final String s1385 = "1385";
+  public static final String s1386 = "1386";
+  public static final String s1387 = "1387";
+  public static final String s1388 = "1388";
+  public static final String s1389 = "1389";
+  public static final String s1390 = "1390";
+  public static final String s1391 = "1391";
+  public static final String s1392 = "1392";
+  public static final String s1393 = "1393";
+  public static final String s1394 = "1394";
+  public static final String s1395 = "1395";
+  public static final String s1396 = "1396";
+  public static final String s1397 = "1397";
+  public static final String s1398 = "1398";
+  public static final String s1399 = "1399";
+  public static final String s1400 = "1400";
+  public static final String s1401 = "1401";
+  public static final String s1402 = "1402";
+  public static final String s1403 = "1403";
+  public static final String s1404 = "1404";
+  public static final String s1405 = "1405";
+  public static final String s1406 = "1406";
+  public static final String s1407 = "1407";
+  public static final String s1408 = "1408";
+  public static final String s1409 = "1409";
+  public static final String s1410 = "1410";
+  public static final String s1411 = "1411";
+  public static final String s1412 = "1412";
+  public static final String s1413 = "1413";
+  public static final String s1414 = "1414";
+  public static final String s1415 = "1415";
+  public static final String s1416 = "1416";
+  public static final String s1417 = "1417";
+  public static final String s1418 = "1418";
+  public static final String s1419 = "1419";
+  public static final String s1420 = "1420";
+  public static final String s1421 = "1421";
+  public static final String s1422 = "1422";
+  public static final String s1423 = "1423";
+  public static final String s1424 = "1424";
+  public static final String s1425 = "1425";
+  public static final String s1426 = "1426";
+  public static final String s1427 = "1427";
+  public static final String s1428 = "1428";
+  public static final String s1429 = "1429";
+  public static final String s1430 = "1430";
+  public static final String s1431 = "1431";
+  public static final String s1432 = "1432";
+  public static final String s1433 = "1433";
+  public static final String s1434 = "1434";
+  public static final String s1435 = "1435";
+  public static final String s1436 = "1436";
+  public static final String s1437 = "1437";
+  public static final String s1438 = "1438";
+  public static final String s1439 = "1439";
+  public static final String s1440 = "1440";
+  public static final String s1441 = "1441";
+  public static final String s1442 = "1442";
+  public static final String s1443 = "1443";
+  public static final String s1444 = "1444";
+  public static final String s1445 = "1445";
+  public static final String s1446 = "1446";
+  public static final String s1447 = "1447";
+  public static final String s1448 = "1448";
+  public static final String s1449 = "1449";
+  public static final String s1450 = "1450";
+  public static final String s1451 = "1451";
+  public static final String s1452 = "1452";
+  public static final String s1453 = "1453";
+  public static final String s1454 = "1454";
+  public static final String s1455 = "1455";
+  public static final String s1456 = "1456";
+  public static final String s1457 = "1457";
+  public static final String s1458 = "1458";
+  public static final String s1459 = "1459";
+  public static final String s1460 = "1460";
+  public static final String s1461 = "1461";
+  public static final String s1462 = "1462";
+  public static final String s1463 = "1463";
+  public static final String s1464 = "1464";
+  public static final String s1465 = "1465";
+  public static final String s1466 = "1466";
+  public static final String s1467 = "1467";
+  public static final String s1468 = "1468";
+  public static final String s1469 = "1469";
+  public static final String s1470 = "1470";
+  public static final String s1471 = "1471";
+  public static final String s1472 = "1472";
+  public static final String s1473 = "1473";
+  public static final String s1474 = "1474";
+  public static final String s1475 = "1475";
+  public static final String s1476 = "1476";
+  public static final String s1477 = "1477";
+  public static final String s1478 = "1478";
+  public static final String s1479 = "1479";
+  public static final String s1480 = "1480";
+  public static final String s1481 = "1481";
+  public static final String s1482 = "1482";
+  public static final String s1483 = "1483";
+  public static final String s1484 = "1484";
+  public static final String s1485 = "1485";
+  public static final String s1486 = "1486";
+  public static final String s1487 = "1487";
+  public static final String s1488 = "1488";
+  public static final String s1489 = "1489";
+  public static final String s1490 = "1490";
+  public static final String s1491 = "1491";
+  public static final String s1492 = "1492";
+  public static final String s1493 = "1493";
+  public static final String s1494 = "1494";
+  public static final String s1495 = "1495";
+  public static final String s1496 = "1496";
+  public static final String s1497 = "1497";
+  public static final String s1498 = "1498";
+  public static final String s1499 = "1499";
+  public static final String s1500 = "1500";
+  public static final String s1501 = "1501";
+  public static final String s1502 = "1502";
+  public static final String s1503 = "1503";
+  public static final String s1504 = "1504";
+  public static final String s1505 = "1505";
+  public static final String s1506 = "1506";
+  public static final String s1507 = "1507";
+  public static final String s1508 = "1508";
+  public static final String s1509 = "1509";
+  public static final String s1510 = "1510";
+  public static final String s1511 = "1511";
+  public static final String s1512 = "1512";
+  public static final String s1513 = "1513";
+  public static final String s1514 = "1514";
+  public static final String s1515 = "1515";
+  public static final String s1516 = "1516";
+  public static final String s1517 = "1517";
+  public static final String s1518 = "1518";
+  public static final String s1519 = "1519";
+  public static final String s1520 = "1520";
+  public static final String s1521 = "1521";
+  public static final String s1522 = "1522";
+  public static final String s1523 = "1523";
+  public static final String s1524 = "1524";
+  public static final String s1525 = "1525";
+  public static final String s1526 = "1526";
+  public static final String s1527 = "1527";
+  public static final String s1528 = "1528";
+  public static final String s1529 = "1529";
+  public static final String s1530 = "1530";
+  public static final String s1531 = "1531";
+  public static final String s1532 = "1532";
+  public static final String s1533 = "1533";
+  public static final String s1534 = "1534";
+  public static final String s1535 = "1535";
+  public static final String s1536 = "1536";
+  public static final String s1537 = "1537";
+  public static final String s1538 = "1538";
+  public static final String s1539 = "1539";
+  public static final String s1540 = "1540";
+  public static final String s1541 = "1541";
+  public static final String s1542 = "1542";
+  public static final String s1543 = "1543";
+  public static final String s1544 = "1544";
+  public static final String s1545 = "1545";
+  public static final String s1546 = "1546";
+  public static final String s1547 = "1547";
+  public static final String s1548 = "1548";
+  public static final String s1549 = "1549";
+  public static final String s1550 = "1550";
+  public static final String s1551 = "1551";
+  public static final String s1552 = "1552";
+  public static final String s1553 = "1553";
+  public static final String s1554 = "1554";
+  public static final String s1555 = "1555";
+  public static final String s1556 = "1556";
+  public static final String s1557 = "1557";
+  public static final String s1558 = "1558";
+  public static final String s1559 = "1559";
+  public static final String s1560 = "1560";
+  public static final String s1561 = "1561";
+  public static final String s1562 = "1562";
+  public static final String s1563 = "1563";
+  public static final String s1564 = "1564";
+  public static final String s1565 = "1565";
+  public static final String s1566 = "1566";
+  public static final String s1567 = "1567";
+  public static final String s1568 = "1568";
+  public static final String s1569 = "1569";
+  public static final String s1570 = "1570";
+  public static final String s1571 = "1571";
+  public static final String s1572 = "1572";
+  public static final String s1573 = "1573";
+  public static final String s1574 = "1574";
+  public static final String s1575 = "1575";
+  public static final String s1576 = "1576";
+  public static final String s1577 = "1577";
+  public static final String s1578 = "1578";
+  public static final String s1579 = "1579";
+  public static final String s1580 = "1580";
+  public static final String s1581 = "1581";
+  public static final String s1582 = "1582";
+  public static final String s1583 = "1583";
+  public static final String s1584 = "1584";
+  public static final String s1585 = "1585";
+  public static final String s1586 = "1586";
+  public static final String s1587 = "1587";
+  public static final String s1588 = "1588";
+  public static final String s1589 = "1589";
+  public static final String s1590 = "1590";
+  public static final String s1591 = "1591";
+  public static final String s1592 = "1592";
+  public static final String s1593 = "1593";
+  public static final String s1594 = "1594";
+  public static final String s1595 = "1595";
+  public static final String s1596 = "1596";
+  public static final String s1597 = "1597";
+  public static final String s1598 = "1598";
+  public static final String s1599 = "1599";
+  public static final String s1600 = "1600";
+  public static final String s1601 = "1601";
+  public static final String s1602 = "1602";
+  public static final String s1603 = "1603";
+  public static final String s1604 = "1604";
+  public static final String s1605 = "1605";
+  public static final String s1606 = "1606";
+  public static final String s1607 = "1607";
+  public static final String s1608 = "1608";
+  public static final String s1609 = "1609";
+  public static final String s1610 = "1610";
+  public static final String s1611 = "1611";
+  public static final String s1612 = "1612";
+  public static final String s1613 = "1613";
+  public static final String s1614 = "1614";
+  public static final String s1615 = "1615";
+  public static final String s1616 = "1616";
+  public static final String s1617 = "1617";
+  public static final String s1618 = "1618";
+  public static final String s1619 = "1619";
+  public static final String s1620 = "1620";
+  public static final String s1621 = "1621";
+  public static final String s1622 = "1622";
+  public static final String s1623 = "1623";
+  public static final String s1624 = "1624";
+  public static final String s1625 = "1625";
+  public static final String s1626 = "1626";
+  public static final String s1627 = "1627";
+  public static final String s1628 = "1628";
+  public static final String s1629 = "1629";
+  public static final String s1630 = "1630";
+  public static final String s1631 = "1631";
+  public static final String s1632 = "1632";
+  public static final String s1633 = "1633";
+  public static final String s1634 = "1634";
+  public static final String s1635 = "1635";
+  public static final String s1636 = "1636";
+  public static final String s1637 = "1637";
+  public static final String s1638 = "1638";
+  public static final String s1639 = "1639";
+  public static final String s1640 = "1640";
+  public static final String s1641 = "1641";
+  public static final String s1642 = "1642";
+  public static final String s1643 = "1643";
+  public static final String s1644 = "1644";
+  public static final String s1645 = "1645";
+  public static final String s1646 = "1646";
+  public static final String s1647 = "1647";
+  public static final String s1648 = "1648";
+  public static final String s1649 = "1649";
+  public static final String s1650 = "1650";
+  public static final String s1651 = "1651";
+  public static final String s1652 = "1652";
+  public static final String s1653 = "1653";
+  public static final String s1654 = "1654";
+  public static final String s1655 = "1655";
+  public static final String s1656 = "1656";
+  public static final String s1657 = "1657";
+  public static final String s1658 = "1658";
+  public static final String s1659 = "1659";
+  public static final String s1660 = "1660";
+  public static final String s1661 = "1661";
+  public static final String s1662 = "1662";
+  public static final String s1663 = "1663";
+  public static final String s1664 = "1664";
+  public static final String s1665 = "1665";
+  public static final String s1666 = "1666";
+  public static final String s1667 = "1667";
+  public static final String s1668 = "1668";
+  public static final String s1669 = "1669";
+  public static final String s1670 = "1670";
+  public static final String s1671 = "1671";
+  public static final String s1672 = "1672";
+  public static final String s1673 = "1673";
+  public static final String s1674 = "1674";
+  public static final String s1675 = "1675";
+  public static final String s1676 = "1676";
+  public static final String s1677 = "1677";
+  public static final String s1678 = "1678";
+  public static final String s1679 = "1679";
+  public static final String s1680 = "1680";
+  public static final String s1681 = "1681";
+  public static final String s1682 = "1682";
+  public static final String s1683 = "1683";
+  public static final String s1684 = "1684";
+  public static final String s1685 = "1685";
+  public static final String s1686 = "1686";
+  public static final String s1687 = "1687";
+  public static final String s1688 = "1688";
+  public static final String s1689 = "1689";
+  public static final String s1690 = "1690";
+  public static final String s1691 = "1691";
+  public static final String s1692 = "1692";
+  public static final String s1693 = "1693";
+  public static final String s1694 = "1694";
+  public static final String s1695 = "1695";
+  public static final String s1696 = "1696";
+  public static final String s1697 = "1697";
+  public static final String s1698 = "1698";
+  public static final String s1699 = "1699";
+  public static final String s1700 = "1700";
+  public static final String s1701 = "1701";
+  public static final String s1702 = "1702";
+  public static final String s1703 = "1703";
+  public static final String s1704 = "1704";
+  public static final String s1705 = "1705";
+  public static final String s1706 = "1706";
+  public static final String s1707 = "1707";
+  public static final String s1708 = "1708";
+  public static final String s1709 = "1709";
+  public static final String s1710 = "1710";
+  public static final String s1711 = "1711";
+  public static final String s1712 = "1712";
+  public static final String s1713 = "1713";
+  public static final String s1714 = "1714";
+  public static final String s1715 = "1715";
+  public static final String s1716 = "1716";
+  public static final String s1717 = "1717";
+  public static final String s1718 = "1718";
+  public static final String s1719 = "1719";
+  public static final String s1720 = "1720";
+  public static final String s1721 = "1721";
+  public static final String s1722 = "1722";
+  public static final String s1723 = "1723";
+  public static final String s1724 = "1724";
+  public static final String s1725 = "1725";
+  public static final String s1726 = "1726";
+  public static final String s1727 = "1727";
+  public static final String s1728 = "1728";
+  public static final String s1729 = "1729";
+  public static final String s1730 = "1730";
+  public static final String s1731 = "1731";
+  public static final String s1732 = "1732";
+  public static final String s1733 = "1733";
+  public static final String s1734 = "1734";
+  public static final String s1735 = "1735";
+  public static final String s1736 = "1736";
+  public static final String s1737 = "1737";
+  public static final String s1738 = "1738";
+  public static final String s1739 = "1739";
+  public static final String s1740 = "1740";
+  public static final String s1741 = "1741";
+  public static final String s1742 = "1742";
+  public static final String s1743 = "1743";
+  public static final String s1744 = "1744";
+  public static final String s1745 = "1745";
+  public static final String s1746 = "1746";
+  public static final String s1747 = "1747";
+  public static final String s1748 = "1748";
+  public static final String s1749 = "1749";
+  public static final String s1750 = "1750";
+  public static final String s1751 = "1751";
+  public static final String s1752 = "1752";
+  public static final String s1753 = "1753";
+  public static final String s1754 = "1754";
+  public static final String s1755 = "1755";
+  public static final String s1756 = "1756";
+  public static final String s1757 = "1757";
+  public static final String s1758 = "1758";
+  public static final String s1759 = "1759";
+  public static final String s1760 = "1760";
+  public static final String s1761 = "1761";
+  public static final String s1762 = "1762";
+  public static final String s1763 = "1763";
+  public static final String s1764 = "1764";
+  public static final String s1765 = "1765";
+  public static final String s1766 = "1766";
+  public static final String s1767 = "1767";
+  public static final String s1768 = "1768";
+  public static final String s1769 = "1769";
+  public static final String s1770 = "1770";
+  public static final String s1771 = "1771";
+  public static final String s1772 = "1772";
+  public static final String s1773 = "1773";
+  public static final String s1774 = "1774";
+  public static final String s1775 = "1775";
+  public static final String s1776 = "1776";
+  public static final String s1777 = "1777";
+  public static final String s1778 = "1778";
+  public static final String s1779 = "1779";
+  public static final String s1780 = "1780";
+  public static final String s1781 = "1781";
+  public static final String s1782 = "1782";
+  public static final String s1783 = "1783";
+  public static final String s1784 = "1784";
+  public static final String s1785 = "1785";
+  public static final String s1786 = "1786";
+  public static final String s1787 = "1787";
+  public static final String s1788 = "1788";
+  public static final String s1789 = "1789";
+  public static final String s1790 = "1790";
+  public static final String s1791 = "1791";
+  public static final String s1792 = "1792";
+  public static final String s1793 = "1793";
+  public static final String s1794 = "1794";
+  public static final String s1795 = "1795";
+  public static final String s1796 = "1796";
+  public static final String s1797 = "1797";
+  public static final String s1798 = "1798";
+  public static final String s1799 = "1799";
+  public static final String s1800 = "1800";
+  public static final String s1801 = "1801";
+  public static final String s1802 = "1802";
+  public static final String s1803 = "1803";
+  public static final String s1804 = "1804";
+  public static final String s1805 = "1805";
+  public static final String s1806 = "1806";
+  public static final String s1807 = "1807";
+  public static final String s1808 = "1808";
+  public static final String s1809 = "1809";
+  public static final String s1810 = "1810";
+  public static final String s1811 = "1811";
+  public static final String s1812 = "1812";
+  public static final String s1813 = "1813";
+  public static final String s1814 = "1814";
+  public static final String s1815 = "1815";
+  public static final String s1816 = "1816";
+  public static final String s1817 = "1817";
+  public static final String s1818 = "1818";
+  public static final String s1819 = "1819";
+  public static final String s1820 = "1820";
+  public static final String s1821 = "1821";
+  public static final String s1822 = "1822";
+  public static final String s1823 = "1823";
+  public static final String s1824 = "1824";
+  public static final String s1825 = "1825";
+  public static final String s1826 = "1826";
+  public static final String s1827 = "1827";
+  public static final String s1828 = "1828";
+  public static final String s1829 = "1829";
+  public static final String s1830 = "1830";
+  public static final String s1831 = "1831";
+  public static final String s1832 = "1832";
+  public static final String s1833 = "1833";
+  public static final String s1834 = "1834";
+  public static final String s1835 = "1835";
+  public static final String s1836 = "1836";
+  public static final String s1837 = "1837";
+  public static final String s1838 = "1838";
+  public static final String s1839 = "1839";
+  public static final String s1840 = "1840";
+  public static final String s1841 = "1841";
+  public static final String s1842 = "1842";
+  public static final String s1843 = "1843";
+  public static final String s1844 = "1844";
+  public static final String s1845 = "1845";
+  public static final String s1846 = "1846";
+  public static final String s1847 = "1847";
+  public static final String s1848 = "1848";
+  public static final String s1849 = "1849";
+  public static final String s1850 = "1850";
+  public static final String s1851 = "1851";
+  public static final String s1852 = "1852";
+  public static final String s1853 = "1853";
+  public static final String s1854 = "1854";
+  public static final String s1855 = "1855";
+  public static final String s1856 = "1856";
+  public static final String s1857 = "1857";
+  public static final String s1858 = "1858";
+  public static final String s1859 = "1859";
+  public static final String s1860 = "1860";
+  public static final String s1861 = "1861";
+  public static final String s1862 = "1862";
+  public static final String s1863 = "1863";
+  public static final String s1864 = "1864";
+  public static final String s1865 = "1865";
+  public static final String s1866 = "1866";
+  public static final String s1867 = "1867";
+  public static final String s1868 = "1868";
+  public static final String s1869 = "1869";
+  public static final String s1870 = "1870";
+  public static final String s1871 = "1871";
+  public static final String s1872 = "1872";
+  public static final String s1873 = "1873";
+  public static final String s1874 = "1874";
+  public static final String s1875 = "1875";
+  public static final String s1876 = "1876";
+  public static final String s1877 = "1877";
+  public static final String s1878 = "1878";
+  public static final String s1879 = "1879";
+  public static final String s1880 = "1880";
+  public static final String s1881 = "1881";
+  public static final String s1882 = "1882";
+  public static final String s1883 = "1883";
+  public static final String s1884 = "1884";
+  public static final String s1885 = "1885";
+  public static final String s1886 = "1886";
+  public static final String s1887 = "1887";
+  public static final String s1888 = "1888";
+  public static final String s1889 = "1889";
+  public static final String s1890 = "1890";
+  public static final String s1891 = "1891";
+  public static final String s1892 = "1892";
+  public static final String s1893 = "1893";
+  public static final String s1894 = "1894";
+  public static final String s1895 = "1895";
+  public static final String s1896 = "1896";
+  public static final String s1897 = "1897";
+  public static final String s1898 = "1898";
+  public static final String s1899 = "1899";
+  public static final String s1900 = "1900";
+  public static final String s1901 = "1901";
+  public static final String s1902 = "1902";
+  public static final String s1903 = "1903";
+  public static final String s1904 = "1904";
+  public static final String s1905 = "1905";
+  public static final String s1906 = "1906";
+  public static final String s1907 = "1907";
+  public static final String s1908 = "1908";
+  public static final String s1909 = "1909";
+  public static final String s1910 = "1910";
+  public static final String s1911 = "1911";
+  public static final String s1912 = "1912";
+  public static final String s1913 = "1913";
+  public static final String s1914 = "1914";
+  public static final String s1915 = "1915";
+  public static final String s1916 = "1916";
+  public static final String s1917 = "1917";
+  public static final String s1918 = "1918";
+  public static final String s1919 = "1919";
+  public static final String s1920 = "1920";
+  public static final String s1921 = "1921";
+  public static final String s1922 = "1922";
+  public static final String s1923 = "1923";
+  public static final String s1924 = "1924";
+  public static final String s1925 = "1925";
+  public static final String s1926 = "1926";
+  public static final String s1927 = "1927";
+  public static final String s1928 = "1928";
+  public static final String s1929 = "1929";
+  public static final String s1930 = "1930";
+  public static final String s1931 = "1931";
+  public static final String s1932 = "1932";
+  public static final String s1933 = "1933";
+  public static final String s1934 = "1934";
+  public static final String s1935 = "1935";
+  public static final String s1936 = "1936";
+  public static final String s1937 = "1937";
+  public static final String s1938 = "1938";
+  public static final String s1939 = "1939";
+  public static final String s1940 = "1940";
+  public static final String s1941 = "1941";
+  public static final String s1942 = "1942";
+  public static final String s1943 = "1943";
+  public static final String s1944 = "1944";
+  public static final String s1945 = "1945";
+  public static final String s1946 = "1946";
+  public static final String s1947 = "1947";
+  public static final String s1948 = "1948";
+  public static final String s1949 = "1949";
+  public static final String s1950 = "1950";
+  public static final String s1951 = "1951";
+  public static final String s1952 = "1952";
+  public static final String s1953 = "1953";
+  public static final String s1954 = "1954";
+  public static final String s1955 = "1955";
+  public static final String s1956 = "1956";
+  public static final String s1957 = "1957";
+  public static final String s1958 = "1958";
+  public static final String s1959 = "1959";
+  public static final String s1960 = "1960";
+  public static final String s1961 = "1961";
+  public static final String s1962 = "1962";
+  public static final String s1963 = "1963";
+  public static final String s1964 = "1964";
+  public static final String s1965 = "1965";
+  public static final String s1966 = "1966";
+  public static final String s1967 = "1967";
+  public static final String s1968 = "1968";
+  public static final String s1969 = "1969";
+  public static final String s1970 = "1970";
+  public static final String s1971 = "1971";
+  public static final String s1972 = "1972";
+  public static final String s1973 = "1973";
+  public static final String s1974 = "1974";
+  public static final String s1975 = "1975";
+  public static final String s1976 = "1976";
+  public static final String s1977 = "1977";
+  public static final String s1978 = "1978";
+  public static final String s1979 = "1979";
+  public static final String s1980 = "1980";
+  public static final String s1981 = "1981";
+  public static final String s1982 = "1982";
+  public static final String s1983 = "1983";
+  public static final String s1984 = "1984";
+  public static final String s1985 = "1985";
+  public static final String s1986 = "1986";
+  public static final String s1987 = "1987";
+  public static final String s1988 = "1988";
+  public static final String s1989 = "1989";
+  public static final String s1990 = "1990";
+  public static final String s1991 = "1991";
+  public static final String s1992 = "1992";
+  public static final String s1993 = "1993";
+  public static final String s1994 = "1994";
+  public static final String s1995 = "1995";
+  public static final String s1996 = "1996";
+  public static final String s1997 = "1997";
+  public static final String s1998 = "1998";
+  public static final String s1999 = "1999";
+  public static final String s2000 = "2000";
+  public static final String s2001 = "2001";
+  public static final String s2002 = "2002";
+  public static final String s2003 = "2003";
+  public static final String s2004 = "2004";
+  public static final String s2005 = "2005";
+  public static final String s2006 = "2006";
+  public static final String s2007 = "2007";
+  public static final String s2008 = "2008";
+  public static final String s2009 = "2009";
+  public static final String s2010 = "2010";
+  public static final String s2011 = "2011";
+  public static final String s2012 = "2012";
+  public static final String s2013 = "2013";
+  public static final String s2014 = "2014";
+  public static final String s2015 = "2015";
+  public static final String s2016 = "2016";
+  public static final String s2017 = "2017";
+  public static final String s2018 = "2018";
+  public static final String s2019 = "2019";
+  public static final String s2020 = "2020";
+  public static final String s2021 = "2021";
+  public static final String s2022 = "2022";
+  public static final String s2023 = "2023";
+  public static final String s2024 = "2024";
+  public static final String s2025 = "2025";
+  public static final String s2026 = "2026";
+  public static final String s2027 = "2027";
+  public static final String s2028 = "2028";
+  public static final String s2029 = "2029";
+  public static final String s2030 = "2030";
+  public static final String s2031 = "2031";
+  public static final String s2032 = "2032";
+  public static final String s2033 = "2033";
+  public static final String s2034 = "2034";
+  public static final String s2035 = "2035";
+  public static final String s2036 = "2036";
+  public static final String s2037 = "2037";
+  public static final String s2038 = "2038";
+  public static final String s2039 = "2039";
+  public static final String s2040 = "2040";
+  public static final String s2041 = "2041";
+  public static final String s2042 = "2042";
+  public static final String s2043 = "2043";
+  public static final String s2044 = "2044";
+  public static final String s2045 = "2045";
+  public static final String s2046 = "2046";
+  public static final String s2047 = "2047";
+  public static final String s2048 = "2048";
+  public static final String s2049 = "2049";
+  public static final String s2050 = "2050";
+  public static final String s2051 = "2051";
+  public static final String s2052 = "2052";
+  public static final String s2053 = "2053";
+  public static final String s2054 = "2054";
+  public static final String s2055 = "2055";
+  public static final String s2056 = "2056";
+  public static final String s2057 = "2057";
+  public static final String s2058 = "2058";
+  public static final String s2059 = "2059";
+  public static final String s2060 = "2060";
+  public static final String s2061 = "2061";
+  public static final String s2062 = "2062";
+  public static final String s2063 = "2063";
+  public static final String s2064 = "2064";
+  public static final String s2065 = "2065";
+  public static final String s2066 = "2066";
+  public static final String s2067 = "2067";
+  public static final String s2068 = "2068";
+  public static final String s2069 = "2069";
+  public static final String s2070 = "2070";
+  public static final String s2071 = "2071";
+  public static final String s2072 = "2072";
+  public static final String s2073 = "2073";
+  public static final String s2074 = "2074";
+  public static final String s2075 = "2075";
+  public static final String s2076 = "2076";
+  public static final String s2077 = "2077";
+  public static final String s2078 = "2078";
+  public static final String s2079 = "2079";
+  public static final String s2080 = "2080";
+  public static final String s2081 = "2081";
+  public static final String s2082 = "2082";
+  public static final String s2083 = "2083";
+  public static final String s2084 = "2084";
+  public static final String s2085 = "2085";
+  public static final String s2086 = "2086";
+  public static final String s2087 = "2087";
+  public static final String s2088 = "2088";
+  public static final String s2089 = "2089";
+  public static final String s2090 = "2090";
+  public static final String s2091 = "2091";
+  public static final String s2092 = "2092";
+  public static final String s2093 = "2093";
+  public static final String s2094 = "2094";
+  public static final String s2095 = "2095";
+  public static final String s2096 = "2096";
+  public static final String s2097 = "2097";
+  public static final String s2098 = "2098";
+  public static final String s2099 = "2099";
+  public static final String s2100 = "2100";
+  public static final String s2101 = "2101";
+  public static final String s2102 = "2102";
+  public static final String s2103 = "2103";
+  public static final String s2104 = "2104";
+  public static final String s2105 = "2105";
+  public static final String s2106 = "2106";
+  public static final String s2107 = "2107";
+  public static final String s2108 = "2108";
+  public static final String s2109 = "2109";
+  public static final String s2110 = "2110";
+  public static final String s2111 = "2111";
+  public static final String s2112 = "2112";
+  public static final String s2113 = "2113";
+  public static final String s2114 = "2114";
+  public static final String s2115 = "2115";
+  public static final String s2116 = "2116";
+  public static final String s2117 = "2117";
+  public static final String s2118 = "2118";
+  public static final String s2119 = "2119";
+  public static final String s2120 = "2120";
+  public static final String s2121 = "2121";
+  public static final String s2122 = "2122";
+  public static final String s2123 = "2123";
+  public static final String s2124 = "2124";
+  public static final String s2125 = "2125";
+  public static final String s2126 = "2126";
+  public static final String s2127 = "2127";
+  public static final String s2128 = "2128";
+  public static final String s2129 = "2129";
+  public static final String s2130 = "2130";
+  public static final String s2131 = "2131";
+  public static final String s2132 = "2132";
+  public static final String s2133 = "2133";
+  public static final String s2134 = "2134";
+  public static final String s2135 = "2135";
+  public static final String s2136 = "2136";
+  public static final String s2137 = "2137";
+  public static final String s2138 = "2138";
+  public static final String s2139 = "2139";
+  public static final String s2140 = "2140";
+  public static final String s2141 = "2141";
+  public static final String s2142 = "2142";
+  public static final String s2143 = "2143";
+  public static final String s2144 = "2144";
+  public static final String s2145 = "2145";
+  public static final String s2146 = "2146";
+  public static final String s2147 = "2147";
+  public static final String s2148 = "2148";
+  public static final String s2149 = "2149";
+  public static final String s2150 = "2150";
+  public static final String s2151 = "2151";
+  public static final String s2152 = "2152";
+  public static final String s2153 = "2153";
+  public static final String s2154 = "2154";
+  public static final String s2155 = "2155";
+  public static final String s2156 = "2156";
+  public static final String s2157 = "2157";
+  public static final String s2158 = "2158";
+  public static final String s2159 = "2159";
+  public static final String s2160 = "2160";
+  public static final String s2161 = "2161";
+  public static final String s2162 = "2162";
+  public static final String s2163 = "2163";
+  public static final String s2164 = "2164";
+  public static final String s2165 = "2165";
+  public static final String s2166 = "2166";
+  public static final String s2167 = "2167";
+  public static final String s2168 = "2168";
+  public static final String s2169 = "2169";
+  public static final String s2170 = "2170";
+  public static final String s2171 = "2171";
+  public static final String s2172 = "2172";
+  public static final String s2173 = "2173";
+  public static final String s2174 = "2174";
+  public static final String s2175 = "2175";
+  public static final String s2176 = "2176";
+  public static final String s2177 = "2177";
+  public static final String s2178 = "2178";
+  public static final String s2179 = "2179";
+  public static final String s2180 = "2180";
+  public static final String s2181 = "2181";
+  public static final String s2182 = "2182";
+  public static final String s2183 = "2183";
+  public static final String s2184 = "2184";
+  public static final String s2185 = "2185";
+  public static final String s2186 = "2186";
+  public static final String s2187 = "2187";
+  public static final String s2188 = "2188";
+  public static final String s2189 = "2189";
+  public static final String s2190 = "2190";
+  public static final String s2191 = "2191";
+  public static final String s2192 = "2192";
+  public static final String s2193 = "2193";
+  public static final String s2194 = "2194";
+  public static final String s2195 = "2195";
+  public static final String s2196 = "2196";
+  public static final String s2197 = "2197";
+  public static final String s2198 = "2198";
+  public static final String s2199 = "2199";
+  public static final String s2200 = "2200";
+  public static final String s2201 = "2201";
+  public static final String s2202 = "2202";
+  public static final String s2203 = "2203";
+  public static final String s2204 = "2204";
+  public static final String s2205 = "2205";
+  public static final String s2206 = "2206";
+  public static final String s2207 = "2207";
+  public static final String s2208 = "2208";
+  public static final String s2209 = "2209";
+  public static final String s2210 = "2210";
+  public static final String s2211 = "2211";
+  public static final String s2212 = "2212";
+  public static final String s2213 = "2213";
+  public static final String s2214 = "2214";
+  public static final String s2215 = "2215";
+  public static final String s2216 = "2216";
+  public static final String s2217 = "2217";
+  public static final String s2218 = "2218";
+  public static final String s2219 = "2219";
+  public static final String s2220 = "2220";
+  public static final String s2221 = "2221";
+  public static final String s2222 = "2222";
+  public static final String s2223 = "2223";
+  public static final String s2224 = "2224";
+  public static final String s2225 = "2225";
+  public static final String s2226 = "2226";
+  public static final String s2227 = "2227";
+  public static final String s2228 = "2228";
+  public static final String s2229 = "2229";
+  public static final String s2230 = "2230";
+  public static final String s2231 = "2231";
+  public static final String s2232 = "2232";
+  public static final String s2233 = "2233";
+  public static final String s2234 = "2234";
+  public static final String s2235 = "2235";
+  public static final String s2236 = "2236";
+  public static final String s2237 = "2237";
+  public static final String s2238 = "2238";
+  public static final String s2239 = "2239";
+  public static final String s2240 = "2240";
+  public static final String s2241 = "2241";
+  public static final String s2242 = "2242";
+  public static final String s2243 = "2243";
+  public static final String s2244 = "2244";
+  public static final String s2245 = "2245";
+  public static final String s2246 = "2246";
+  public static final String s2247 = "2247";
+  public static final String s2248 = "2248";
+  public static final String s2249 = "2249";
+  public static final String s2250 = "2250";
+  public static final String s2251 = "2251";
+  public static final String s2252 = "2252";
+  public static final String s2253 = "2253";
+  public static final String s2254 = "2254";
+  public static final String s2255 = "2255";
+  public static final String s2256 = "2256";
+  public static final String s2257 = "2257";
+  public static final String s2258 = "2258";
+  public static final String s2259 = "2259";
+  public static final String s2260 = "2260";
+  public static final String s2261 = "2261";
+  public static final String s2262 = "2262";
+  public static final String s2263 = "2263";
+  public static final String s2264 = "2264";
+  public static final String s2265 = "2265";
+  public static final String s2266 = "2266";
+  public static final String s2267 = "2267";
+  public static final String s2268 = "2268";
+  public static final String s2269 = "2269";
+  public static final String s2270 = "2270";
+  public static final String s2271 = "2271";
+  public static final String s2272 = "2272";
+  public static final String s2273 = "2273";
+  public static final String s2274 = "2274";
+  public static final String s2275 = "2275";
+  public static final String s2276 = "2276";
+  public static final String s2277 = "2277";
+  public static final String s2278 = "2278";
+  public static final String s2279 = "2279";
+  public static final String s2280 = "2280";
+  public static final String s2281 = "2281";
+  public static final String s2282 = "2282";
+  public static final String s2283 = "2283";
+  public static final String s2284 = "2284";
+  public static final String s2285 = "2285";
+  public static final String s2286 = "2286";
+  public static final String s2287 = "2287";
+  public static final String s2288 = "2288";
+  public static final String s2289 = "2289";
+  public static final String s2290 = "2290";
+  public static final String s2291 = "2291";
+  public static final String s2292 = "2292";
+  public static final String s2293 = "2293";
+  public static final String s2294 = "2294";
+  public static final String s2295 = "2295";
+  public static final String s2296 = "2296";
+  public static final String s2297 = "2297";
+  public static final String s2298 = "2298";
+  public static final String s2299 = "2299";
+  public static final String s2300 = "2300";
+  public static final String s2301 = "2301";
+  public static final String s2302 = "2302";
+  public static final String s2303 = "2303";
+  public static final String s2304 = "2304";
+  public static final String s2305 = "2305";
+  public static final String s2306 = "2306";
+  public static final String s2307 = "2307";
+  public static final String s2308 = "2308";
+  public static final String s2309 = "2309";
+  public static final String s2310 = "2310";
+  public static final String s2311 = "2311";
+  public static final String s2312 = "2312";
+  public static final String s2313 = "2313";
+  public static final String s2314 = "2314";
+  public static final String s2315 = "2315";
+  public static final String s2316 = "2316";
+  public static final String s2317 = "2317";
+  public static final String s2318 = "2318";
+  public static final String s2319 = "2319";
+  public static final String s2320 = "2320";
+  public static final String s2321 = "2321";
+  public static final String s2322 = "2322";
+  public static final String s2323 = "2323";
+  public static final String s2324 = "2324";
+  public static final String s2325 = "2325";
+  public static final String s2326 = "2326";
+  public static final String s2327 = "2327";
+  public static final String s2328 = "2328";
+  public static final String s2329 = "2329";
+  public static final String s2330 = "2330";
+  public static final String s2331 = "2331";
+  public static final String s2332 = "2332";
+  public static final String s2333 = "2333";
+  public static final String s2334 = "2334";
+  public static final String s2335 = "2335";
+  public static final String s2336 = "2336";
+  public static final String s2337 = "2337";
+  public static final String s2338 = "2338";
+  public static final String s2339 = "2339";
+  public static final String s2340 = "2340";
+  public static final String s2341 = "2341";
+  public static final String s2342 = "2342";
+  public static final String s2343 = "2343";
+  public static final String s2344 = "2344";
+  public static final String s2345 = "2345";
+  public static final String s2346 = "2346";
+  public static final String s2347 = "2347";
+  public static final String s2348 = "2348";
+  public static final String s2349 = "2349";
+  public static final String s2350 = "2350";
+  public static final String s2351 = "2351";
+  public static final String s2352 = "2352";
+  public static final String s2353 = "2353";
+  public static final String s2354 = "2354";
+  public static final String s2355 = "2355";
+  public static final String s2356 = "2356";
+  public static final String s2357 = "2357";
+  public static final String s2358 = "2358";
+  public static final String s2359 = "2359";
+  public static final String s2360 = "2360";
+  public static final String s2361 = "2361";
+  public static final String s2362 = "2362";
+  public static final String s2363 = "2363";
+  public static final String s2364 = "2364";
+  public static final String s2365 = "2365";
+  public static final String s2366 = "2366";
+  public static final String s2367 = "2367";
+  public static final String s2368 = "2368";
+  public static final String s2369 = "2369";
+  public static final String s2370 = "2370";
+  public static final String s2371 = "2371";
+  public static final String s2372 = "2372";
+  public static final String s2373 = "2373";
+  public static final String s2374 = "2374";
+  public static final String s2375 = "2375";
+  public static final String s2376 = "2376";
+  public static final String s2377 = "2377";
+  public static final String s2378 = "2378";
+  public static final String s2379 = "2379";
+  public static final String s2380 = "2380";
+  public static final String s2381 = "2381";
+  public static final String s2382 = "2382";
+  public static final String s2383 = "2383";
+  public static final String s2384 = "2384";
+  public static final String s2385 = "2385";
+  public static final String s2386 = "2386";
+  public static final String s2387 = "2387";
+  public static final String s2388 = "2388";
+  public static final String s2389 = "2389";
+  public static final String s2390 = "2390";
+  public static final String s2391 = "2391";
+  public static final String s2392 = "2392";
+  public static final String s2393 = "2393";
+  public static final String s2394 = "2394";
+  public static final String s2395 = "2395";
+  public static final String s2396 = "2396";
+  public static final String s2397 = "2397";
+  public static final String s2398 = "2398";
+  public static final String s2399 = "2399";
+  public static final String s2400 = "2400";
+  public static final String s2401 = "2401";
+  public static final String s2402 = "2402";
+  public static final String s2403 = "2403";
+  public static final String s2404 = "2404";
+  public static final String s2405 = "2405";
+  public static final String s2406 = "2406";
+  public static final String s2407 = "2407";
+  public static final String s2408 = "2408";
+  public static final String s2409 = "2409";
+  public static final String s2410 = "2410";
+  public static final String s2411 = "2411";
+  public static final String s2412 = "2412";
+  public static final String s2413 = "2413";
+  public static final String s2414 = "2414";
+  public static final String s2415 = "2415";
+  public static final String s2416 = "2416";
+  public static final String s2417 = "2417";
+  public static final String s2418 = "2418";
+  public static final String s2419 = "2419";
+  public static final String s2420 = "2420";
+  public static final String s2421 = "2421";
+  public static final String s2422 = "2422";
+  public static final String s2423 = "2423";
+  public static final String s2424 = "2424";
+  public static final String s2425 = "2425";
+  public static final String s2426 = "2426";
+  public static final String s2427 = "2427";
+  public static final String s2428 = "2428";
+  public static final String s2429 = "2429";
+  public static final String s2430 = "2430";
+  public static final String s2431 = "2431";
+  public static final String s2432 = "2432";
+  public static final String s2433 = "2433";
+  public static final String s2434 = "2434";
+  public static final String s2435 = "2435";
+  public static final String s2436 = "2436";
+  public static final String s2437 = "2437";
+  public static final String s2438 = "2438";
+  public static final String s2439 = "2439";
+  public static final String s2440 = "2440";
+  public static final String s2441 = "2441";
+  public static final String s2442 = "2442";
+  public static final String s2443 = "2443";
+  public static final String s2444 = "2444";
+  public static final String s2445 = "2445";
+  public static final String s2446 = "2446";
+  public static final String s2447 = "2447";
+  public static final String s2448 = "2448";
+  public static final String s2449 = "2449";
+  public static final String s2450 = "2450";
+  public static final String s2451 = "2451";
+  public static final String s2452 = "2452";
+  public static final String s2453 = "2453";
+  public static final String s2454 = "2454";
+  public static final String s2455 = "2455";
+  public static final String s2456 = "2456";
+  public static final String s2457 = "2457";
+  public static final String s2458 = "2458";
+  public static final String s2459 = "2459";
+  public static final String s2460 = "2460";
+  public static final String s2461 = "2461";
+  public static final String s2462 = "2462";
+  public static final String s2463 = "2463";
+  public static final String s2464 = "2464";
+  public static final String s2465 = "2465";
+  public static final String s2466 = "2466";
+  public static final String s2467 = "2467";
+  public static final String s2468 = "2468";
+  public static final String s2469 = "2469";
+  public static final String s2470 = "2470";
+  public static final String s2471 = "2471";
+  public static final String s2472 = "2472";
+  public static final String s2473 = "2473";
+  public static final String s2474 = "2474";
+  public static final String s2475 = "2475";
+  public static final String s2476 = "2476";
+  public static final String s2477 = "2477";
+  public static final String s2478 = "2478";
+  public static final String s2479 = "2479";
+  public static final String s2480 = "2480";
+  public static final String s2481 = "2481";
+  public static final String s2482 = "2482";
+  public static final String s2483 = "2483";
+  public static final String s2484 = "2484";
+  public static final String s2485 = "2485";
+  public static final String s2486 = "2486";
+  public static final String s2487 = "2487";
+  public static final String s2488 = "2488";
+  public static final String s2489 = "2489";
+  public static final String s2490 = "2490";
+  public static final String s2491 = "2491";
+  public static final String s2492 = "2492";
+  public static final String s2493 = "2493";
+  public static final String s2494 = "2494";
+  public static final String s2495 = "2495";
+  public static final String s2496 = "2496";
+  public static final String s2497 = "2497";
+  public static final String s2498 = "2498";
+  public static final String s2499 = "2499";
+  public static final String s2500 = "2500";
+  public static final String s2501 = "2501";
+  public static final String s2502 = "2502";
+  public static final String s2503 = "2503";
+  public static final String s2504 = "2504";
+  public static final String s2505 = "2505";
+  public static final String s2506 = "2506";
+  public static final String s2507 = "2507";
+  public static final String s2508 = "2508";
+  public static final String s2509 = "2509";
+  public static final String s2510 = "2510";
+  public static final String s2511 = "2511";
+  public static final String s2512 = "2512";
+  public static final String s2513 = "2513";
+  public static final String s2514 = "2514";
+  public static final String s2515 = "2515";
+  public static final String s2516 = "2516";
+  public static final String s2517 = "2517";
+  public static final String s2518 = "2518";
+  public static final String s2519 = "2519";
+  public static final String s2520 = "2520";
+  public static final String s2521 = "2521";
+  public static final String s2522 = "2522";
+  public static final String s2523 = "2523";
+  public static final String s2524 = "2524";
+  public static final String s2525 = "2525";
+  public static final String s2526 = "2526";
+  public static final String s2527 = "2527";
+  public static final String s2528 = "2528";
+  public static final String s2529 = "2529";
+  public static final String s2530 = "2530";
+  public static final String s2531 = "2531";
+  public static final String s2532 = "2532";
+  public static final String s2533 = "2533";
+  public static final String s2534 = "2534";
+  public static final String s2535 = "2535";
+  public static final String s2536 = "2536";
+  public static final String s2537 = "2537";
+  public static final String s2538 = "2538";
+  public static final String s2539 = "2539";
+  public static final String s2540 = "2540";
+  public static final String s2541 = "2541";
+  public static final String s2542 = "2542";
+  public static final String s2543 = "2543";
+  public static final String s2544 = "2544";
+  public static final String s2545 = "2545";
+  public static final String s2546 = "2546";
+  public static final String s2547 = "2547";
+  public static final String s2548 = "2548";
+  public static final String s2549 = "2549";
+  public static final String s2550 = "2550";
+  public static final String s2551 = "2551";
+  public static final String s2552 = "2552";
+  public static final String s2553 = "2553";
+  public static final String s2554 = "2554";
+  public static final String s2555 = "2555";
+  public static final String s2556 = "2556";
+  public static final String s2557 = "2557";
+  public static final String s2558 = "2558";
+  public static final String s2559 = "2559";
+  public static final String s2560 = "2560";
+  public static final String s2561 = "2561";
+  public static final String s2562 = "2562";
+  public static final String s2563 = "2563";
+  public static final String s2564 = "2564";
+  public static final String s2565 = "2565";
+  public static final String s2566 = "2566";
+  public static final String s2567 = "2567";
+  public static final String s2568 = "2568";
+  public static final String s2569 = "2569";
+  public static final String s2570 = "2570";
+  public static final String s2571 = "2571";
+  public static final String s2572 = "2572";
+  public static final String s2573 = "2573";
+  public static final String s2574 = "2574";
+  public static final String s2575 = "2575";
+  public static final String s2576 = "2576";
+  public static final String s2577 = "2577";
+  public static final String s2578 = "2578";
+  public static final String s2579 = "2579";
+  public static final String s2580 = "2580";
+  public static final String s2581 = "2581";
+  public static final String s2582 = "2582";
+  public static final String s2583 = "2583";
+  public static final String s2584 = "2584";
+  public static final String s2585 = "2585";
+  public static final String s2586 = "2586";
+  public static final String s2587 = "2587";
+  public static final String s2588 = "2588";
+  public static final String s2589 = "2589";
+  public static final String s2590 = "2590";
+  public static final String s2591 = "2591";
+  public static final String s2592 = "2592";
+  public static final String s2593 = "2593";
+  public static final String s2594 = "2594";
+  public static final String s2595 = "2595";
+  public static final String s2596 = "2596";
+  public static final String s2597 = "2597";
+  public static final String s2598 = "2598";
+  public static final String s2599 = "2599";
+  public static final String s2600 = "2600";
+  public static final String s2601 = "2601";
+  public static final String s2602 = "2602";
+  public static final String s2603 = "2603";
+  public static final String s2604 = "2604";
+  public static final String s2605 = "2605";
+  public static final String s2606 = "2606";
+  public static final String s2607 = "2607";
+  public static final String s2608 = "2608";
+  public static final String s2609 = "2609";
+  public static final String s2610 = "2610";
+  public static final String s2611 = "2611";
+  public static final String s2612 = "2612";
+  public static final String s2613 = "2613";
+  public static final String s2614 = "2614";
+  public static final String s2615 = "2615";
+  public static final String s2616 = "2616";
+  public static final String s2617 = "2617";
+  public static final String s2618 = "2618";
+  public static final String s2619 = "2619";
+  public static final String s2620 = "2620";
+  public static final String s2621 = "2621";
+  public static final String s2622 = "2622";
+  public static final String s2623 = "2623";
+  public static final String s2624 = "2624";
+  public static final String s2625 = "2625";
+  public static final String s2626 = "2626";
+  public static final String s2627 = "2627";
+  public static final String s2628 = "2628";
+  public static final String s2629 = "2629";
+  public static final String s2630 = "2630";
+  public static final String s2631 = "2631";
+  public static final String s2632 = "2632";
+  public static final String s2633 = "2633";
+  public static final String s2634 = "2634";
+  public static final String s2635 = "2635";
+  public static final String s2636 = "2636";
+  public static final String s2637 = "2637";
+  public static final String s2638 = "2638";
+  public static final String s2639 = "2639";
+  public static final String s2640 = "2640";
+  public static final String s2641 = "2641";
+  public static final String s2642 = "2642";
+  public static final String s2643 = "2643";
+  public static final String s2644 = "2644";
+  public static final String s2645 = "2645";
+  public static final String s2646 = "2646";
+  public static final String s2647 = "2647";
+  public static final String s2648 = "2648";
+  public static final String s2649 = "2649";
+  public static final String s2650 = "2650";
+  public static final String s2651 = "2651";
+  public static final String s2652 = "2652";
+  public static final String s2653 = "2653";
+  public static final String s2654 = "2654";
+  public static final String s2655 = "2655";
+  public static final String s2656 = "2656";
+  public static final String s2657 = "2657";
+  public static final String s2658 = "2658";
+  public static final String s2659 = "2659";
+  public static final String s2660 = "2660";
+  public static final String s2661 = "2661";
+  public static final String s2662 = "2662";
+  public static final String s2663 = "2663";
+  public static final String s2664 = "2664";
+  public static final String s2665 = "2665";
+  public static final String s2666 = "2666";
+  public static final String s2667 = "2667";
+  public static final String s2668 = "2668";
+  public static final String s2669 = "2669";
+  public static final String s2670 = "2670";
+  public static final String s2671 = "2671";
+  public static final String s2672 = "2672";
+  public static final String s2673 = "2673";
+  public static final String s2674 = "2674";
+  public static final String s2675 = "2675";
+  public static final String s2676 = "2676";
+  public static final String s2677 = "2677";
+  public static final String s2678 = "2678";
+  public static final String s2679 = "2679";
+  public static final String s2680 = "2680";
+  public static final String s2681 = "2681";
+  public static final String s2682 = "2682";
+  public static final String s2683 = "2683";
+  public static final String s2684 = "2684";
+  public static final String s2685 = "2685";
+  public static final String s2686 = "2686";
+  public static final String s2687 = "2687";
+  public static final String s2688 = "2688";
+  public static final String s2689 = "2689";
+  public static final String s2690 = "2690";
+  public static final String s2691 = "2691";
+  public static final String s2692 = "2692";
+  public static final String s2693 = "2693";
+  public static final String s2694 = "2694";
+  public static final String s2695 = "2695";
+  public static final String s2696 = "2696";
+  public static final String s2697 = "2697";
+  public static final String s2698 = "2698";
+  public static final String s2699 = "2699";
+  public static final String s2700 = "2700";
+  public static final String s2701 = "2701";
+  public static final String s2702 = "2702";
+  public static final String s2703 = "2703";
+  public static final String s2704 = "2704";
+  public static final String s2705 = "2705";
+  public static final String s2706 = "2706";
+  public static final String s2707 = "2707";
+  public static final String s2708 = "2708";
+  public static final String s2709 = "2709";
+  public static final String s2710 = "2710";
+  public static final String s2711 = "2711";
+  public static final String s2712 = "2712";
+  public static final String s2713 = "2713";
+  public static final String s2714 = "2714";
+  public static final String s2715 = "2715";
+  public static final String s2716 = "2716";
+  public static final String s2717 = "2717";
+  public static final String s2718 = "2718";
+  public static final String s2719 = "2719";
+  public static final String s2720 = "2720";
+  public static final String s2721 = "2721";
+  public static final String s2722 = "2722";
+  public static final String s2723 = "2723";
+  public static final String s2724 = "2724";
+  public static final String s2725 = "2725";
+  public static final String s2726 = "2726";
+  public static final String s2727 = "2727";
+  public static final String s2728 = "2728";
+  public static final String s2729 = "2729";
+  public static final String s2730 = "2730";
+  public static final String s2731 = "2731";
+  public static final String s2732 = "2732";
+  public static final String s2733 = "2733";
+  public static final String s2734 = "2734";
+  public static final String s2735 = "2735";
+  public static final String s2736 = "2736";
+  public static final String s2737 = "2737";
+  public static final String s2738 = "2738";
+  public static final String s2739 = "2739";
+  public static final String s2740 = "2740";
+  public static final String s2741 = "2741";
+  public static final String s2742 = "2742";
+  public static final String s2743 = "2743";
+  public static final String s2744 = "2744";
+  public static final String s2745 = "2745";
+  public static final String s2746 = "2746";
+  public static final String s2747 = "2747";
+  public static final String s2748 = "2748";
+  public static final String s2749 = "2749";
+  public static final String s2750 = "2750";
+  public static final String s2751 = "2751";
+  public static final String s2752 = "2752";
+  public static final String s2753 = "2753";
+  public static final String s2754 = "2754";
+  public static final String s2755 = "2755";
+  public static final String s2756 = "2756";
+  public static final String s2757 = "2757";
+  public static final String s2758 = "2758";
+  public static final String s2759 = "2759";
+  public static final String s2760 = "2760";
+  public static final String s2761 = "2761";
+  public static final String s2762 = "2762";
+  public static final String s2763 = "2763";
+  public static final String s2764 = "2764";
+  public static final String s2765 = "2765";
+  public static final String s2766 = "2766";
+  public static final String s2767 = "2767";
+  public static final String s2768 = "2768";
+  public static final String s2769 = "2769";
+  public static final String s2770 = "2770";
+  public static final String s2771 = "2771";
+  public static final String s2772 = "2772";
+  public static final String s2773 = "2773";
+  public static final String s2774 = "2774";
+  public static final String s2775 = "2775";
+  public static final String s2776 = "2776";
+  public static final String s2777 = "2777";
+  public static final String s2778 = "2778";
+  public static final String s2779 = "2779";
+  public static final String s2780 = "2780";
+  public static final String s2781 = "2781";
+  public static final String s2782 = "2782";
+  public static final String s2783 = "2783";
+  public static final String s2784 = "2784";
+  public static final String s2785 = "2785";
+  public static final String s2786 = "2786";
+  public static final String s2787 = "2787";
+  public static final String s2788 = "2788";
+  public static final String s2789 = "2789";
+  public static final String s2790 = "2790";
+  public static final String s2791 = "2791";
+  public static final String s2792 = "2792";
+  public static final String s2793 = "2793";
+  public static final String s2794 = "2794";
+  public static final String s2795 = "2795";
+  public static final String s2796 = "2796";
+  public static final String s2797 = "2797";
+  public static final String s2798 = "2798";
+  public static final String s2799 = "2799";
+  public static final String s2800 = "2800";
+  public static final String s2801 = "2801";
+  public static final String s2802 = "2802";
+  public static final String s2803 = "2803";
+  public static final String s2804 = "2804";
+  public static final String s2805 = "2805";
+  public static final String s2806 = "2806";
+  public static final String s2807 = "2807";
+  public static final String s2808 = "2808";
+  public static final String s2809 = "2809";
+  public static final String s2810 = "2810";
+  public static final String s2811 = "2811";
+  public static final String s2812 = "2812";
+  public static final String s2813 = "2813";
+  public static final String s2814 = "2814";
+  public static final String s2815 = "2815";
+  public static final String s2816 = "2816";
+  public static final String s2817 = "2817";
+  public static final String s2818 = "2818";
+  public static final String s2819 = "2819";
+  public static final String s2820 = "2820";
+  public static final String s2821 = "2821";
+  public static final String s2822 = "2822";
+  public static final String s2823 = "2823";
+  public static final String s2824 = "2824";
+  public static final String s2825 = "2825";
+  public static final String s2826 = "2826";
+  public static final String s2827 = "2827";
+  public static final String s2828 = "2828";
+  public static final String s2829 = "2829";
+  public static final String s2830 = "2830";
+  public static final String s2831 = "2831";
+  public static final String s2832 = "2832";
+  public static final String s2833 = "2833";
+  public static final String s2834 = "2834";
+  public static final String s2835 = "2835";
+  public static final String s2836 = "2836";
+  public static final String s2837 = "2837";
+  public static final String s2838 = "2838";
+  public static final String s2839 = "2839";
+  public static final String s2840 = "2840";
+  public static final String s2841 = "2841";
+  public static final String s2842 = "2842";
+  public static final String s2843 = "2843";
+  public static final String s2844 = "2844";
+  public static final String s2845 = "2845";
+  public static final String s2846 = "2846";
+  public static final String s2847 = "2847";
+  public static final String s2848 = "2848";
+  public static final String s2849 = "2849";
+  public static final String s2850 = "2850";
+  public static final String s2851 = "2851";
+  public static final String s2852 = "2852";
+  public static final String s2853 = "2853";
+  public static final String s2854 = "2854";
+  public static final String s2855 = "2855";
+  public static final String s2856 = "2856";
+  public static final String s2857 = "2857";
+  public static final String s2858 = "2858";
+  public static final String s2859 = "2859";
+  public static final String s2860 = "2860";
+  public static final String s2861 = "2861";
+  public static final String s2862 = "2862";
+  public static final String s2863 = "2863";
+  public static final String s2864 = "2864";
+  public static final String s2865 = "2865";
+  public static final String s2866 = "2866";
+  public static final String s2867 = "2867";
+  public static final String s2868 = "2868";
+  public static final String s2869 = "2869";
+  public static final String s2870 = "2870";
+  public static final String s2871 = "2871";
+  public static final String s2872 = "2872";
+  public static final String s2873 = "2873";
+  public static final String s2874 = "2874";
+  public static final String s2875 = "2875";
+  public static final String s2876 = "2876";
+  public static final String s2877 = "2877";
+  public static final String s2878 = "2878";
+  public static final String s2879 = "2879";
+  public static final String s2880 = "2880";
+  public static final String s2881 = "2881";
+  public static final String s2882 = "2882";
+  public static final String s2883 = "2883";
+  public static final String s2884 = "2884";
+  public static final String s2885 = "2885";
+  public static final String s2886 = "2886";
+  public static final String s2887 = "2887";
+  public static final String s2888 = "2888";
+  public static final String s2889 = "2889";
+  public static final String s2890 = "2890";
+  public static final String s2891 = "2891";
+  public static final String s2892 = "2892";
+  public static final String s2893 = "2893";
+  public static final String s2894 = "2894";
+  public static final String s2895 = "2895";
+  public static final String s2896 = "2896";
+  public static final String s2897 = "2897";
+  public static final String s2898 = "2898";
+  public static final String s2899 = "2899";
+  public static final String s2900 = "2900";
+  public static final String s2901 = "2901";
+  public static final String s2902 = "2902";
+  public static final String s2903 = "2903";
+  public static final String s2904 = "2904";
+  public static final String s2905 = "2905";
+  public static final String s2906 = "2906";
+  public static final String s2907 = "2907";
+  public static final String s2908 = "2908";
+  public static final String s2909 = "2909";
+  public static final String s2910 = "2910";
+  public static final String s2911 = "2911";
+  public static final String s2912 = "2912";
+  public static final String s2913 = "2913";
+  public static final String s2914 = "2914";
+  public static final String s2915 = "2915";
+  public static final String s2916 = "2916";
+  public static final String s2917 = "2917";
+  public static final String s2918 = "2918";
+  public static final String s2919 = "2919";
+  public static final String s2920 = "2920";
+  public static final String s2921 = "2921";
+  public static final String s2922 = "2922";
+  public static final String s2923 = "2923";
+  public static final String s2924 = "2924";
+  public static final String s2925 = "2925";
+  public static final String s2926 = "2926";
+  public static final String s2927 = "2927";
+  public static final String s2928 = "2928";
+  public static final String s2929 = "2929";
+  public static final String s2930 = "2930";
+  public static final String s2931 = "2931";
+  public static final String s2932 = "2932";
+  public static final String s2933 = "2933";
+  public static final String s2934 = "2934";
+  public static final String s2935 = "2935";
+  public static final String s2936 = "2936";
+  public static final String s2937 = "2937";
+  public static final String s2938 = "2938";
+  public static final String s2939 = "2939";
+  public static final String s2940 = "2940";
+  public static final String s2941 = "2941";
+  public static final String s2942 = "2942";
+  public static final String s2943 = "2943";
+  public static final String s2944 = "2944";
+  public static final String s2945 = "2945";
+  public static final String s2946 = "2946";
+  public static final String s2947 = "2947";
+  public static final String s2948 = "2948";
+  public static final String s2949 = "2949";
+  public static final String s2950 = "2950";
+  public static final String s2951 = "2951";
+  public static final String s2952 = "2952";
+  public static final String s2953 = "2953";
+  public static final String s2954 = "2954";
+  public static final String s2955 = "2955";
+  public static final String s2956 = "2956";
+  public static final String s2957 = "2957";
+  public static final String s2958 = "2958";
+  public static final String s2959 = "2959";
+  public static final String s2960 = "2960";
+  public static final String s2961 = "2961";
+  public static final String s2962 = "2962";
+  public static final String s2963 = "2963";
+  public static final String s2964 = "2964";
+  public static final String s2965 = "2965";
+  public static final String s2966 = "2966";
+  public static final String s2967 = "2967";
+  public static final String s2968 = "2968";
+  public static final String s2969 = "2969";
+  public static final String s2970 = "2970";
+  public static final String s2971 = "2971";
+  public static final String s2972 = "2972";
+  public static final String s2973 = "2973";
+  public static final String s2974 = "2974";
+  public static final String s2975 = "2975";
+  public static final String s2976 = "2976";
+  public static final String s2977 = "2977";
+  public static final String s2978 = "2978";
+  public static final String s2979 = "2979";
+  public static final String s2980 = "2980";
+  public static final String s2981 = "2981";
+  public static final String s2982 = "2982";
+  public static final String s2983 = "2983";
+  public static final String s2984 = "2984";
+  public static final String s2985 = "2985";
+  public static final String s2986 = "2986";
+  public static final String s2987 = "2987";
+  public static final String s2988 = "2988";
+  public static final String s2989 = "2989";
+  public static final String s2990 = "2990";
+  public static final String s2991 = "2991";
+  public static final String s2992 = "2992";
+  public static final String s2993 = "2993";
+  public static final String s2994 = "2994";
+  public static final String s2995 = "2995";
+  public static final String s2996 = "2996";
+  public static final String s2997 = "2997";
+  public static final String s2998 = "2998";
+  public static final String s2999 = "2999";
+  public static final String s3000 = "3000";
+  public static final String s3001 = "3001";
+  public static final String s3002 = "3002";
+  public static final String s3003 = "3003";
+  public static final String s3004 = "3004";
+  public static final String s3005 = "3005";
+  public static final String s3006 = "3006";
+  public static final String s3007 = "3007";
+  public static final String s3008 = "3008";
+  public static final String s3009 = "3009";
+  public static final String s3010 = "3010";
+  public static final String s3011 = "3011";
+  public static final String s3012 = "3012";
+  public static final String s3013 = "3013";
+  public static final String s3014 = "3014";
+  public static final String s3015 = "3015";
+  public static final String s3016 = "3016";
+  public static final String s3017 = "3017";
+  public static final String s3018 = "3018";
+  public static final String s3019 = "3019";
+  public static final String s3020 = "3020";
+  public static final String s3021 = "3021";
+  public static final String s3022 = "3022";
+  public static final String s3023 = "3023";
+  public static final String s3024 = "3024";
+  public static final String s3025 = "3025";
+  public static final String s3026 = "3026";
+  public static final String s3027 = "3027";
+  public static final String s3028 = "3028";
+  public static final String s3029 = "3029";
+  public static final String s3030 = "3030";
+  public static final String s3031 = "3031";
+  public static final String s3032 = "3032";
+  public static final String s3033 = "3033";
+  public static final String s3034 = "3034";
+  public static final String s3035 = "3035";
+  public static final String s3036 = "3036";
+  public static final String s3037 = "3037";
+  public static final String s3038 = "3038";
+  public static final String s3039 = "3039";
+  public static final String s3040 = "3040";
+  public static final String s3041 = "3041";
+  public static final String s3042 = "3042";
+  public static final String s3043 = "3043";
+  public static final String s3044 = "3044";
+  public static final String s3045 = "3045";
+  public static final String s3046 = "3046";
+  public static final String s3047 = "3047";
+  public static final String s3048 = "3048";
+  public static final String s3049 = "3049";
+  public static final String s3050 = "3050";
+  public static final String s3051 = "3051";
+  public static final String s3052 = "3052";
+  public static final String s3053 = "3053";
+  public static final String s3054 = "3054";
+  public static final String s3055 = "3055";
+  public static final String s3056 = "3056";
+  public static final String s3057 = "3057";
+  public static final String s3058 = "3058";
+  public static final String s3059 = "3059";
+  public static final String s3060 = "3060";
+  public static final String s3061 = "3061";
+  public static final String s3062 = "3062";
+  public static final String s3063 = "3063";
+  public static final String s3064 = "3064";
+  public static final String s3065 = "3065";
+  public static final String s3066 = "3066";
+  public static final String s3067 = "3067";
+  public static final String s3068 = "3068";
+  public static final String s3069 = "3069";
+  public static final String s3070 = "3070";
+  public static final String s3071 = "3071";
+  public static final String s3072 = "3072";
+  public static final String s3073 = "3073";
+  public static final String s3074 = "3074";
+  public static final String s3075 = "3075";
+  public static final String s3076 = "3076";
+  public static final String s3077 = "3077";
+  public static final String s3078 = "3078";
+  public static final String s3079 = "3079";
+  public static final String s3080 = "3080";
+  public static final String s3081 = "3081";
+  public static final String s3082 = "3082";
+  public static final String s3083 = "3083";
+  public static final String s3084 = "3084";
+  public static final String s3085 = "3085";
+  public static final String s3086 = "3086";
+  public static final String s3087 = "3087";
+  public static final String s3088 = "3088";
+  public static final String s3089 = "3089";
+  public static final String s3090 = "3090";
+  public static final String s3091 = "3091";
+  public static final String s3092 = "3092";
+  public static final String s3093 = "3093";
+  public static final String s3094 = "3094";
+  public static final String s3095 = "3095";
+  public static final String s3096 = "3096";
+  public static final String s3097 = "3097";
+  public static final String s3098 = "3098";
+  public static final String s3099 = "3099";
+  public static final String s3100 = "3100";
+  public static final String s3101 = "3101";
+  public static final String s3102 = "3102";
+  public static final String s3103 = "3103";
+  public static final String s3104 = "3104";
+  public static final String s3105 = "3105";
+  public static final String s3106 = "3106";
+  public static final String s3107 = "3107";
+  public static final String s3108 = "3108";
+  public static final String s3109 = "3109";
+  public static final String s3110 = "3110";
+  public static final String s3111 = "3111";
+  public static final String s3112 = "3112";
+  public static final String s3113 = "3113";
+  public static final String s3114 = "3114";
+  public static final String s3115 = "3115";
+  public static final String s3116 = "3116";
+  public static final String s3117 = "3117";
+  public static final String s3118 = "3118";
+  public static final String s3119 = "3119";
+  public static final String s3120 = "3120";
+  public static final String s3121 = "3121";
+  public static final String s3122 = "3122";
+  public static final String s3123 = "3123";
+  public static final String s3124 = "3124";
+  public static final String s3125 = "3125";
+  public static final String s3126 = "3126";
+  public static final String s3127 = "3127";
+  public static final String s3128 = "3128";
+  public static final String s3129 = "3129";
+  public static final String s3130 = "3130";
+  public static final String s3131 = "3131";
+  public static final String s3132 = "3132";
+  public static final String s3133 = "3133";
+  public static final String s3134 = "3134";
+  public static final String s3135 = "3135";
+  public static final String s3136 = "3136";
+  public static final String s3137 = "3137";
+  public static final String s3138 = "3138";
+  public static final String s3139 = "3139";
+  public static final String s3140 = "3140";
+  public static final String s3141 = "3141";
+  public static final String s3142 = "3142";
+  public static final String s3143 = "3143";
+  public static final String s3144 = "3144";
+  public static final String s3145 = "3145";
+  public static final String s3146 = "3146";
+  public static final String s3147 = "3147";
+  public static final String s3148 = "3148";
+  public static final String s3149 = "3149";
+  public static final String s3150 = "3150";
+  public static final String s3151 = "3151";
+  public static final String s3152 = "3152";
+  public static final String s3153 = "3153";
+  public static final String s3154 = "3154";
+  public static final String s3155 = "3155";
+  public static final String s3156 = "3156";
+  public static final String s3157 = "3157";
+  public static final String s3158 = "3158";
+  public static final String s3159 = "3159";
+  public static final String s3160 = "3160";
+  public static final String s3161 = "3161";
+  public static final String s3162 = "3162";
+  public static final String s3163 = "3163";
+  public static final String s3164 = "3164";
+  public static final String s3165 = "3165";
+  public static final String s3166 = "3166";
+  public static final String s3167 = "3167";
+  public static final String s3168 = "3168";
+  public static final String s3169 = "3169";
+  public static final String s3170 = "3170";
+  public static final String s3171 = "3171";
+  public static final String s3172 = "3172";
+  public static final String s3173 = "3173";
+  public static final String s3174 = "3174";
+  public static final String s3175 = "3175";
+  public static final String s3176 = "3176";
+  public static final String s3177 = "3177";
+  public static final String s3178 = "3178";
+  public static final String s3179 = "3179";
+  public static final String s3180 = "3180";
+  public static final String s3181 = "3181";
+  public static final String s3182 = "3182";
+  public static final String s3183 = "3183";
+  public static final String s3184 = "3184";
+  public static final String s3185 = "3185";
+  public static final String s3186 = "3186";
+  public static final String s3187 = "3187";
+  public static final String s3188 = "3188";
+  public static final String s3189 = "3189";
+  public static final String s3190 = "3190";
+  public static final String s3191 = "3191";
+  public static final String s3192 = "3192";
+  public static final String s3193 = "3193";
+  public static final String s3194 = "3194";
+  public static final String s3195 = "3195";
+  public static final String s3196 = "3196";
+  public static final String s3197 = "3197";
+  public static final String s3198 = "3198";
+  public static final String s3199 = "3199";
+  public static final String s3200 = "3200";
+  public static final String s3201 = "3201";
+  public static final String s3202 = "3202";
+  public static final String s3203 = "3203";
+  public static final String s3204 = "3204";
+  public static final String s3205 = "3205";
+  public static final String s3206 = "3206";
+  public static final String s3207 = "3207";
+  public static final String s3208 = "3208";
+  public static final String s3209 = "3209";
+  public static final String s3210 = "3210";
+  public static final String s3211 = "3211";
+  public static final String s3212 = "3212";
+  public static final String s3213 = "3213";
+  public static final String s3214 = "3214";
+  public static final String s3215 = "3215";
+  public static final String s3216 = "3216";
+  public static final String s3217 = "3217";
+  public static final String s3218 = "3218";
+  public static final String s3219 = "3219";
+  public static final String s3220 = "3220";
+  public static final String s3221 = "3221";
+  public static final String s3222 = "3222";
+  public static final String s3223 = "3223";
+  public static final String s3224 = "3224";
+  public static final String s3225 = "3225";
+  public static final String s3226 = "3226";
+  public static final String s3227 = "3227";
+  public static final String s3228 = "3228";
+  public static final String s3229 = "3229";
+  public static final String s3230 = "3230";
+  public static final String s3231 = "3231";
+  public static final String s3232 = "3232";
+  public static final String s3233 = "3233";
+  public static final String s3234 = "3234";
+  public static final String s3235 = "3235";
+  public static final String s3236 = "3236";
+  public static final String s3237 = "3237";
+  public static final String s3238 = "3238";
+  public static final String s3239 = "3239";
+  public static final String s3240 = "3240";
+  public static final String s3241 = "3241";
+  public static final String s3242 = "3242";
+  public static final String s3243 = "3243";
+  public static final String s3244 = "3244";
+  public static final String s3245 = "3245";
+  public static final String s3246 = "3246";
+  public static final String s3247 = "3247";
+  public static final String s3248 = "3248";
+  public static final String s3249 = "3249";
+  public static final String s3250 = "3250";
+  public static final String s3251 = "3251";
+  public static final String s3252 = "3252";
+  public static final String s3253 = "3253";
+  public static final String s3254 = "3254";
+  public static final String s3255 = "3255";
+  public static final String s3256 = "3256";
+  public static final String s3257 = "3257";
+  public static final String s3258 = "3258";
+  public static final String s3259 = "3259";
+  public static final String s3260 = "3260";
+  public static final String s3261 = "3261";
+  public static final String s3262 = "3262";
+  public static final String s3263 = "3263";
+  public static final String s3264 = "3264";
+  public static final String s3265 = "3265";
+  public static final String s3266 = "3266";
+  public static final String s3267 = "3267";
+  public static final String s3268 = "3268";
+  public static final String s3269 = "3269";
+  public static final String s3270 = "3270";
+  public static final String s3271 = "3271";
+  public static final String s3272 = "3272";
+  public static final String s3273 = "3273";
+  public static final String s3274 = "3274";
+  public static final String s3275 = "3275";
+  public static final String s3276 = "3276";
+  public static final String s3277 = "3277";
+  public static final String s3278 = "3278";
+  public static final String s3279 = "3279";
+  public static final String s3280 = "3280";
+  public static final String s3281 = "3281";
+  public static final String s3282 = "3282";
+  public static final String s3283 = "3283";
+  public static final String s3284 = "3284";
+  public static final String s3285 = "3285";
+  public static final String s3286 = "3286";
+  public static final String s3287 = "3287";
+  public static final String s3288 = "3288";
+  public static final String s3289 = "3289";
+  public static final String s3290 = "3290";
+  public static final String s3291 = "3291";
+  public static final String s3292 = "3292";
+  public static final String s3293 = "3293";
+  public static final String s3294 = "3294";
+  public static final String s3295 = "3295";
+  public static final String s3296 = "3296";
+  public static final String s3297 = "3297";
+  public static final String s3298 = "3298";
+  public static final String s3299 = "3299";
+  public static final String s3300 = "3300";
+  public static final String s3301 = "3301";
+  public static final String s3302 = "3302";
+  public static final String s3303 = "3303";
+  public static final String s3304 = "3304";
+  public static final String s3305 = "3305";
+  public static final String s3306 = "3306";
+  public static final String s3307 = "3307";
+  public static final String s3308 = "3308";
+  public static final String s3309 = "3309";
+  public static final String s3310 = "3310";
+  public static final String s3311 = "3311";
+  public static final String s3312 = "3312";
+  public static final String s3313 = "3313";
+  public static final String s3314 = "3314";
+  public static final String s3315 = "3315";
+  public static final String s3316 = "3316";
+  public static final String s3317 = "3317";
+  public static final String s3318 = "3318";
+  public static final String s3319 = "3319";
+  public static final String s3320 = "3320";
+  public static final String s3321 = "3321";
+  public static final String s3322 = "3322";
+  public static final String s3323 = "3323";
+  public static final String s3324 = "3324";
+  public static final String s3325 = "3325";
+  public static final String s3326 = "3326";
+  public static final String s3327 = "3327";
+  public static final String s3328 = "3328";
+  public static final String s3329 = "3329";
+  public static final String s3330 = "3330";
+  public static final String s3331 = "3331";
+  public static final String s3332 = "3332";
+  public static final String s3333 = "3333";
+  public static final String s3334 = "3334";
+  public static final String s3335 = "3335";
+  public static final String s3336 = "3336";
+  public static final String s3337 = "3337";
+  public static final String s3338 = "3338";
+  public static final String s3339 = "3339";
+  public static final String s3340 = "3340";
+  public static final String s3341 = "3341";
+  public static final String s3342 = "3342";
+  public static final String s3343 = "3343";
+  public static final String s3344 = "3344";
+  public static final String s3345 = "3345";
+  public static final String s3346 = "3346";
+  public static final String s3347 = "3347";
+  public static final String s3348 = "3348";
+  public static final String s3349 = "3349";
+  public static final String s3350 = "3350";
+  public static final String s3351 = "3351";
+  public static final String s3352 = "3352";
+  public static final String s3353 = "3353";
+  public static final String s3354 = "3354";
+  public static final String s3355 = "3355";
+  public static final String s3356 = "3356";
+  public static final String s3357 = "3357";
+  public static final String s3358 = "3358";
+  public static final String s3359 = "3359";
+  public static final String s3360 = "3360";
+  public static final String s3361 = "3361";
+  public static final String s3362 = "3362";
+  public static final String s3363 = "3363";
+  public static final String s3364 = "3364";
+  public static final String s3365 = "3365";
+  public static final String s3366 = "3366";
+  public static final String s3367 = "3367";
+  public static final String s3368 = "3368";
+  public static final String s3369 = "3369";
+  public static final String s3370 = "3370";
+  public static final String s3371 = "3371";
+  public static final String s3372 = "3372";
+  public static final String s3373 = "3373";
+  public static final String s3374 = "3374";
+  public static final String s3375 = "3375";
+  public static final String s3376 = "3376";
+  public static final String s3377 = "3377";
+  public static final String s3378 = "3378";
+  public static final String s3379 = "3379";
+  public static final String s3380 = "3380";
+  public static final String s3381 = "3381";
+  public static final String s3382 = "3382";
+  public static final String s3383 = "3383";
+  public static final String s3384 = "3384";
+  public static final String s3385 = "3385";
+  public static final String s3386 = "3386";
+  public static final String s3387 = "3387";
+  public static final String s3388 = "3388";
+  public static final String s3389 = "3389";
+  public static final String s3390 = "3390";
+  public static final String s3391 = "3391";
+  public static final String s3392 = "3392";
+  public static final String s3393 = "3393";
+  public static final String s3394 = "3394";
+  public static final String s3395 = "3395";
+  public static final String s3396 = "3396";
+  public static final String s3397 = "3397";
+  public static final String s3398 = "3398";
+  public static final String s3399 = "3399";
+  public static final String s3400 = "3400";
+  public static final String s3401 = "3401";
+  public static final String s3402 = "3402";
+  public static final String s3403 = "3403";
+  public static final String s3404 = "3404";
+  public static final String s3405 = "3405";
+  public static final String s3406 = "3406";
+  public static final String s3407 = "3407";
+  public static final String s3408 = "3408";
+  public static final String s3409 = "3409";
+  public static final String s3410 = "3410";
+  public static final String s3411 = "3411";
+  public static final String s3412 = "3412";
+  public static final String s3413 = "3413";
+  public static final String s3414 = "3414";
+  public static final String s3415 = "3415";
+  public static final String s3416 = "3416";
+  public static final String s3417 = "3417";
+  public static final String s3418 = "3418";
+  public static final String s3419 = "3419";
+  public static final String s3420 = "3420";
+  public static final String s3421 = "3421";
+  public static final String s3422 = "3422";
+  public static final String s3423 = "3423";
+  public static final String s3424 = "3424";
+  public static final String s3425 = "3425";
+  public static final String s3426 = "3426";
+  public static final String s3427 = "3427";
+  public static final String s3428 = "3428";
+  public static final String s3429 = "3429";
+  public static final String s3430 = "3430";
+  public static final String s3431 = "3431";
+  public static final String s3432 = "3432";
+  public static final String s3433 = "3433";
+  public static final String s3434 = "3434";
+  public static final String s3435 = "3435";
+  public static final String s3436 = "3436";
+  public static final String s3437 = "3437";
+  public static final String s3438 = "3438";
+  public static final String s3439 = "3439";
+  public static final String s3440 = "3440";
+  public static final String s3441 = "3441";
+  public static final String s3442 = "3442";
+  public static final String s3443 = "3443";
+  public static final String s3444 = "3444";
+  public static final String s3445 = "3445";
+  public static final String s3446 = "3446";
+  public static final String s3447 = "3447";
+  public static final String s3448 = "3448";
+  public static final String s3449 = "3449";
+  public static final String s3450 = "3450";
+  public static final String s3451 = "3451";
+  public static final String s3452 = "3452";
+  public static final String s3453 = "3453";
+  public static final String s3454 = "3454";
+  public static final String s3455 = "3455";
+  public static final String s3456 = "3456";
+  public static final String s3457 = "3457";
+  public static final String s3458 = "3458";
+  public static final String s3459 = "3459";
+  public static final String s3460 = "3460";
+  public static final String s3461 = "3461";
+  public static final String s3462 = "3462";
+  public static final String s3463 = "3463";
+  public static final String s3464 = "3464";
+  public static final String s3465 = "3465";
+  public static final String s3466 = "3466";
+  public static final String s3467 = "3467";
+  public static final String s3468 = "3468";
+  public static final String s3469 = "3469";
+  public static final String s3470 = "3470";
+  public static final String s3471 = "3471";
+  public static final String s3472 = "3472";
+  public static final String s3473 = "3473";
+  public static final String s3474 = "3474";
+  public static final String s3475 = "3475";
+  public static final String s3476 = "3476";
+  public static final String s3477 = "3477";
+  public static final String s3478 = "3478";
+  public static final String s3479 = "3479";
+  public static final String s3480 = "3480";
+  public static final String s3481 = "3481";
+  public static final String s3482 = "3482";
+  public static final String s3483 = "3483";
+  public static final String s3484 = "3484";
+  public static final String s3485 = "3485";
+  public static final String s3486 = "3486";
+  public static final String s3487 = "3487";
+  public static final String s3488 = "3488";
+  public static final String s3489 = "3489";
+  public static final String s3490 = "3490";
+  public static final String s3491 = "3491";
+  public static final String s3492 = "3492";
+  public static final String s3493 = "3493";
+  public static final String s3494 = "3494";
+  public static final String s3495 = "3495";
+  public static final String s3496 = "3496";
+  public static final String s3497 = "3497";
+  public static final String s3498 = "3498";
+  public static final String s3499 = "3499";
+  public static final String s3500 = "3500";
+  public static final String s3501 = "3501";
+  public static final String s3502 = "3502";
+  public static final String s3503 = "3503";
+  public static final String s3504 = "3504";
+  public static final String s3505 = "3505";
+  public static final String s3506 = "3506";
+  public static final String s3507 = "3507";
+  public static final String s3508 = "3508";
+  public static final String s3509 = "3509";
+  public static final String s3510 = "3510";
+  public static final String s3511 = "3511";
+  public static final String s3512 = "3512";
+  public static final String s3513 = "3513";
+  public static final String s3514 = "3514";
+  public static final String s3515 = "3515";
+  public static final String s3516 = "3516";
+  public static final String s3517 = "3517";
+  public static final String s3518 = "3518";
+  public static final String s3519 = "3519";
+  public static final String s3520 = "3520";
+  public static final String s3521 = "3521";
+  public static final String s3522 = "3522";
+  public static final String s3523 = "3523";
+  public static final String s3524 = "3524";
+  public static final String s3525 = "3525";
+  public static final String s3526 = "3526";
+  public static final String s3527 = "3527";
+  public static final String s3528 = "3528";
+  public static final String s3529 = "3529";
+  public static final String s3530 = "3530";
+  public static final String s3531 = "3531";
+  public static final String s3532 = "3532";
+  public static final String s3533 = "3533";
+  public static final String s3534 = "3534";
+  public static final String s3535 = "3535";
+  public static final String s3536 = "3536";
+  public static final String s3537 = "3537";
+  public static final String s3538 = "3538";
+  public static final String s3539 = "3539";
+  public static final String s3540 = "3540";
+  public static final String s3541 = "3541";
+  public static final String s3542 = "3542";
+  public static final String s3543 = "3543";
+  public static final String s3544 = "3544";
+  public static final String s3545 = "3545";
+  public static final String s3546 = "3546";
+  public static final String s3547 = "3547";
+  public static final String s3548 = "3548";
+  public static final String s3549 = "3549";
+  public static final String s3550 = "3550";
+  public static final String s3551 = "3551";
+  public static final String s3552 = "3552";
+  public static final String s3553 = "3553";
+  public static final String s3554 = "3554";
+  public static final String s3555 = "3555";
+  public static final String s3556 = "3556";
+  public static final String s3557 = "3557";
+  public static final String s3558 = "3558";
+  public static final String s3559 = "3559";
+  public static final String s3560 = "3560";
+  public static final String s3561 = "3561";
+  public static final String s3562 = "3562";
+  public static final String s3563 = "3563";
+  public static final String s3564 = "3564";
+  public static final String s3565 = "3565";
+  public static final String s3566 = "3566";
+  public static final String s3567 = "3567";
+  public static final String s3568 = "3568";
+  public static final String s3569 = "3569";
+  public static final String s3570 = "3570";
+  public static final String s3571 = "3571";
+  public static final String s3572 = "3572";
+  public static final String s3573 = "3573";
+  public static final String s3574 = "3574";
+  public static final String s3575 = "3575";
+  public static final String s3576 = "3576";
+  public static final String s3577 = "3577";
+  public static final String s3578 = "3578";
+  public static final String s3579 = "3579";
+  public static final String s3580 = "3580";
+  public static final String s3581 = "3581";
+  public static final String s3582 = "3582";
+  public static final String s3583 = "3583";
+  public static final String s3584 = "3584";
+  public static final String s3585 = "3585";
+  public static final String s3586 = "3586";
+  public static final String s3587 = "3587";
+  public static final String s3588 = "3588";
+  public static final String s3589 = "3589";
+  public static final String s3590 = "3590";
+  public static final String s3591 = "3591";
+  public static final String s3592 = "3592";
+  public static final String s3593 = "3593";
+  public static final String s3594 = "3594";
+  public static final String s3595 = "3595";
+  public static final String s3596 = "3596";
+  public static final String s3597 = "3597";
+  public static final String s3598 = "3598";
+  public static final String s3599 = "3599";
+  public static final String s3600 = "3600";
+  public static final String s3601 = "3601";
+  public static final String s3602 = "3602";
+  public static final String s3603 = "3603";
+  public static final String s3604 = "3604";
+  public static final String s3605 = "3605";
+  public static final String s3606 = "3606";
+  public static final String s3607 = "3607";
+  public static final String s3608 = "3608";
+  public static final String s3609 = "3609";
+  public static final String s3610 = "3610";
+  public static final String s3611 = "3611";
+  public static final String s3612 = "3612";
+  public static final String s3613 = "3613";
+  public static final String s3614 = "3614";
+  public static final String s3615 = "3615";
+  public static final String s3616 = "3616";
+  public static final String s3617 = "3617";
+  public static final String s3618 = "3618";
+  public static final String s3619 = "3619";
+  public static final String s3620 = "3620";
+  public static final String s3621 = "3621";
+  public static final String s3622 = "3622";
+  public static final String s3623 = "3623";
+  public static final String s3624 = "3624";
+  public static final String s3625 = "3625";
+  public static final String s3626 = "3626";
+  public static final String s3627 = "3627";
+  public static final String s3628 = "3628";
+  public static final String s3629 = "3629";
+  public static final String s3630 = "3630";
+  public static final String s3631 = "3631";
+  public static final String s3632 = "3632";
+  public static final String s3633 = "3633";
+  public static final String s3634 = "3634";
+  public static final String s3635 = "3635";
+  public static final String s3636 = "3636";
+  public static final String s3637 = "3637";
+  public static final String s3638 = "3638";
+  public static final String s3639 = "3639";
+  public static final String s3640 = "3640";
+  public static final String s3641 = "3641";
+  public static final String s3642 = "3642";
+  public static final String s3643 = "3643";
+  public static final String s3644 = "3644";
+  public static final String s3645 = "3645";
+  public static final String s3646 = "3646";
+  public static final String s3647 = "3647";
+  public static final String s3648 = "3648";
+  public static final String s3649 = "3649";
+  public static final String s3650 = "3650";
+  public static final String s3651 = "3651";
+  public static final String s3652 = "3652";
+  public static final String s3653 = "3653";
+  public static final String s3654 = "3654";
+  public static final String s3655 = "3655";
+  public static final String s3656 = "3656";
+  public static final String s3657 = "3657";
+  public static final String s3658 = "3658";
+  public static final String s3659 = "3659";
+  public static final String s3660 = "3660";
+  public static final String s3661 = "3661";
+  public static final String s3662 = "3662";
+  public static final String s3663 = "3663";
+  public static final String s3664 = "3664";
+  public static final String s3665 = "3665";
+  public static final String s3666 = "3666";
+  public static final String s3667 = "3667";
+  public static final String s3668 = "3668";
+  public static final String s3669 = "3669";
+  public static final String s3670 = "3670";
+  public static final String s3671 = "3671";
+  public static final String s3672 = "3672";
+  public static final String s3673 = "3673";
+  public static final String s3674 = "3674";
+  public static final String s3675 = "3675";
+  public static final String s3676 = "3676";
+  public static final String s3677 = "3677";
+  public static final String s3678 = "3678";
+  public static final String s3679 = "3679";
+  public static final String s3680 = "3680";
+  public static final String s3681 = "3681";
+  public static final String s3682 = "3682";
+  public static final String s3683 = "3683";
+  public static final String s3684 = "3684";
+  public static final String s3685 = "3685";
+  public static final String s3686 = "3686";
+  public static final String s3687 = "3687";
+  public static final String s3688 = "3688";
+  public static final String s3689 = "3689";
+  public static final String s3690 = "3690";
+  public static final String s3691 = "3691";
+  public static final String s3692 = "3692";
+  public static final String s3693 = "3693";
+  public static final String s3694 = "3694";
+  public static final String s3695 = "3695";
+  public static final String s3696 = "3696";
+  public static final String s3697 = "3697";
+  public static final String s3698 = "3698";
+  public static final String s3699 = "3699";
+  public static final String s3700 = "3700";
+  public static final String s3701 = "3701";
+  public static final String s3702 = "3702";
+  public static final String s3703 = "3703";
+  public static final String s3704 = "3704";
+  public static final String s3705 = "3705";
+  public static final String s3706 = "3706";
+  public static final String s3707 = "3707";
+  public static final String s3708 = "3708";
+  public static final String s3709 = "3709";
+  public static final String s3710 = "3710";
+  public static final String s3711 = "3711";
+  public static final String s3712 = "3712";
+  public static final String s3713 = "3713";
+  public static final String s3714 = "3714";
+  public static final String s3715 = "3715";
+  public static final String s3716 = "3716";
+  public static final String s3717 = "3717";
+  public static final String s3718 = "3718";
+  public static final String s3719 = "3719";
+  public static final String s3720 = "3720";
+  public static final String s3721 = "3721";
+  public static final String s3722 = "3722";
+  public static final String s3723 = "3723";
+  public static final String s3724 = "3724";
+  public static final String s3725 = "3725";
+  public static final String s3726 = "3726";
+  public static final String s3727 = "3727";
+  public static final String s3728 = "3728";
+  public static final String s3729 = "3729";
+  public static final String s3730 = "3730";
+  public static final String s3731 = "3731";
+  public static final String s3732 = "3732";
+  public static final String s3733 = "3733";
+  public static final String s3734 = "3734";
+  public static final String s3735 = "3735";
+  public static final String s3736 = "3736";
+  public static final String s3737 = "3737";
+  public static final String s3738 = "3738";
+  public static final String s3739 = "3739";
+  public static final String s3740 = "3740";
+  public static final String s3741 = "3741";
+  public static final String s3742 = "3742";
+  public static final String s3743 = "3743";
+  public static final String s3744 = "3744";
+  public static final String s3745 = "3745";
+  public static final String s3746 = "3746";
+  public static final String s3747 = "3747";
+  public static final String s3748 = "3748";
+  public static final String s3749 = "3749";
+  public static final String s3750 = "3750";
+  public static final String s3751 = "3751";
+  public static final String s3752 = "3752";
+  public static final String s3753 = "3753";
+  public static final String s3754 = "3754";
+  public static final String s3755 = "3755";
+  public static final String s3756 = "3756";
+  public static final String s3757 = "3757";
+  public static final String s3758 = "3758";
+  public static final String s3759 = "3759";
+  public static final String s3760 = "3760";
+  public static final String s3761 = "3761";
+  public static final String s3762 = "3762";
+  public static final String s3763 = "3763";
+  public static final String s3764 = "3764";
+  public static final String s3765 = "3765";
+  public static final String s3766 = "3766";
+  public static final String s3767 = "3767";
+  public static final String s3768 = "3768";
+  public static final String s3769 = "3769";
+  public static final String s3770 = "3770";
+  public static final String s3771 = "3771";
+  public static final String s3772 = "3772";
+  public static final String s3773 = "3773";
+  public static final String s3774 = "3774";
+  public static final String s3775 = "3775";
+  public static final String s3776 = "3776";
+  public static final String s3777 = "3777";
+  public static final String s3778 = "3778";
+  public static final String s3779 = "3779";
+  public static final String s3780 = "3780";
+  public static final String s3781 = "3781";
+  public static final String s3782 = "3782";
+  public static final String s3783 = "3783";
+  public static final String s3784 = "3784";
+  public static final String s3785 = "3785";
+  public static final String s3786 = "3786";
+  public static final String s3787 = "3787";
+  public static final String s3788 = "3788";
+  public static final String s3789 = "3789";
+  public static final String s3790 = "3790";
+  public static final String s3791 = "3791";
+  public static final String s3792 = "3792";
+  public static final String s3793 = "3793";
+  public static final String s3794 = "3794";
+  public static final String s3795 = "3795";
+  public static final String s3796 = "3796";
+  public static final String s3797 = "3797";
+  public static final String s3798 = "3798";
+  public static final String s3799 = "3799";
+  public static final String s3800 = "3800";
+  public static final String s3801 = "3801";
+  public static final String s3802 = "3802";
+  public static final String s3803 = "3803";
+  public static final String s3804 = "3804";
+  public static final String s3805 = "3805";
+  public static final String s3806 = "3806";
+  public static final String s3807 = "3807";
+  public static final String s3808 = "3808";
+  public static final String s3809 = "3809";
+  public static final String s3810 = "3810";
+  public static final String s3811 = "3811";
+  public static final String s3812 = "3812";
+  public static final String s3813 = "3813";
+  public static final String s3814 = "3814";
+  public static final String s3815 = "3815";
+  public static final String s3816 = "3816";
+  public static final String s3817 = "3817";
+  public static final String s3818 = "3818";
+  public static final String s3819 = "3819";
+  public static final String s3820 = "3820";
+  public static final String s3821 = "3821";
+  public static final String s3822 = "3822";
+  public static final String s3823 = "3823";
+  public static final String s3824 = "3824";
+  public static final String s3825 = "3825";
+  public static final String s3826 = "3826";
+  public static final String s3827 = "3827";
+  public static final String s3828 = "3828";
+  public static final String s3829 = "3829";
+  public static final String s3830 = "3830";
+  public static final String s3831 = "3831";
+  public static final String s3832 = "3832";
+  public static final String s3833 = "3833";
+  public static final String s3834 = "3834";
+  public static final String s3835 = "3835";
+  public static final String s3836 = "3836";
+  public static final String s3837 = "3837";
+  public static final String s3838 = "3838";
+  public static final String s3839 = "3839";
+  public static final String s3840 = "3840";
+  public static final String s3841 = "3841";
+  public static final String s3842 = "3842";
+  public static final String s3843 = "3843";
+  public static final String s3844 = "3844";
+  public static final String s3845 = "3845";
+  public static final String s3846 = "3846";
+  public static final String s3847 = "3847";
+  public static final String s3848 = "3848";
+  public static final String s3849 = "3849";
+  public static final String s3850 = "3850";
+  public static final String s3851 = "3851";
+  public static final String s3852 = "3852";
+  public static final String s3853 = "3853";
+  public static final String s3854 = "3854";
+  public static final String s3855 = "3855";
+  public static final String s3856 = "3856";
+  public static final String s3857 = "3857";
+  public static final String s3858 = "3858";
+  public static final String s3859 = "3859";
+  public static final String s3860 = "3860";
+  public static final String s3861 = "3861";
+  public static final String s3862 = "3862";
+  public static final String s3863 = "3863";
+  public static final String s3864 = "3864";
+  public static final String s3865 = "3865";
+  public static final String s3866 = "3866";
+  public static final String s3867 = "3867";
+  public static final String s3868 = "3868";
+  public static final String s3869 = "3869";
+  public static final String s3870 = "3870";
+  public static final String s3871 = "3871";
+  public static final String s3872 = "3872";
+  public static final String s3873 = "3873";
+  public static final String s3874 = "3874";
+  public static final String s3875 = "3875";
+  public static final String s3876 = "3876";
+  public static final String s3877 = "3877";
+  public static final String s3878 = "3878";
+  public static final String s3879 = "3879";
+  public static final String s3880 = "3880";
+  public static final String s3881 = "3881";
+  public static final String s3882 = "3882";
+  public static final String s3883 = "3883";
+  public static final String s3884 = "3884";
+  public static final String s3885 = "3885";
+  public static final String s3886 = "3886";
+  public static final String s3887 = "3887";
+  public static final String s3888 = "3888";
+  public static final String s3889 = "3889";
+  public static final String s3890 = "3890";
+  public static final String s3891 = "3891";
+  public static final String s3892 = "3892";
+  public static final String s3893 = "3893";
+  public static final String s3894 = "3894";
+  public static final String s3895 = "3895";
+  public static final String s3896 = "3896";
+  public static final String s3897 = "3897";
+  public static final String s3898 = "3898";
+  public static final String s3899 = "3899";
+  public static final String s3900 = "3900";
+  public static final String s3901 = "3901";
+  public static final String s3902 = "3902";
+  public static final String s3903 = "3903";
+  public static final String s3904 = "3904";
+  public static final String s3905 = "3905";
+  public static final String s3906 = "3906";
+  public static final String s3907 = "3907";
+  public static final String s3908 = "3908";
+  public static final String s3909 = "3909";
+  public static final String s3910 = "3910";
+  public static final String s3911 = "3911";
+  public static final String s3912 = "3912";
+  public static final String s3913 = "3913";
+  public static final String s3914 = "3914";
+  public static final String s3915 = "3915";
+  public static final String s3916 = "3916";
+  public static final String s3917 = "3917";
+  public static final String s3918 = "3918";
+  public static final String s3919 = "3919";
+  public static final String s3920 = "3920";
+  public static final String s3921 = "3921";
+  public static final String s3922 = "3922";
+  public static final String s3923 = "3923";
+  public static final String s3924 = "3924";
+  public static final String s3925 = "3925";
+  public static final String s3926 = "3926";
+  public static final String s3927 = "3927";
+  public static final String s3928 = "3928";
+  public static final String s3929 = "3929";
+  public static final String s3930 = "3930";
+  public static final String s3931 = "3931";
+  public static final String s3932 = "3932";
+  public static final String s3933 = "3933";
+  public static final String s3934 = "3934";
+  public static final String s3935 = "3935";
+  public static final String s3936 = "3936";
+  public static final String s3937 = "3937";
+  public static final String s3938 = "3938";
+  public static final String s3939 = "3939";
+  public static final String s3940 = "3940";
+  public static final String s3941 = "3941";
+  public static final String s3942 = "3942";
+  public static final String s3943 = "3943";
+  public static final String s3944 = "3944";
+  public static final String s3945 = "3945";
+  public static final String s3946 = "3946";
+  public static final String s3947 = "3947";
+  public static final String s3948 = "3948";
+  public static final String s3949 = "3949";
+  public static final String s3950 = "3950";
+  public static final String s3951 = "3951";
+  public static final String s3952 = "3952";
+  public static final String s3953 = "3953";
+  public static final String s3954 = "3954";
+  public static final String s3955 = "3955";
+  public static final String s3956 = "3956";
+  public static final String s3957 = "3957";
+  public static final String s3958 = "3958";
+  public static final String s3959 = "3959";
+  public static final String s3960 = "3960";
+  public static final String s3961 = "3961";
+  public static final String s3962 = "3962";
+  public static final String s3963 = "3963";
+  public static final String s3964 = "3964";
+  public static final String s3965 = "3965";
+  public static final String s3966 = "3966";
+  public static final String s3967 = "3967";
+  public static final String s3968 = "3968";
+  public static final String s3969 = "3969";
+  public static final String s3970 = "3970";
+  public static final String s3971 = "3971";
+  public static final String s3972 = "3972";
+  public static final String s3973 = "3973";
+  public static final String s3974 = "3974";
+  public static final String s3975 = "3975";
+  public static final String s3976 = "3976";
+  public static final String s3977 = "3977";
+  public static final String s3978 = "3978";
+  public static final String s3979 = "3979";
+  public static final String s3980 = "3980";
+  public static final String s3981 = "3981";
+  public static final String s3982 = "3982";
+  public static final String s3983 = "3983";
+  public static final String s3984 = "3984";
+  public static final String s3985 = "3985";
+  public static final String s3986 = "3986";
+  public static final String s3987 = "3987";
+  public static final String s3988 = "3988";
+  public static final String s3989 = "3989";
+  public static final String s3990 = "3990";
+  public static final String s3991 = "3991";
+  public static final String s3992 = "3992";
+  public static final String s3993 = "3993";
+  public static final String s3994 = "3994";
+  public static final String s3995 = "3995";
+  public static final String s3996 = "3996";
+  public static final String s3997 = "3997";
+  public static final String s3998 = "3998";
+  public static final String s3999 = "3999";
+  public static final String s4000 = "4000";
+  public static final String s4001 = "4001";
+  public static final String s4002 = "4002";
+  public static final String s4003 = "4003";
+  public static final String s4004 = "4004";
+  public static final String s4005 = "4005";
+  public static final String s4006 = "4006";
+  public static final String s4007 = "4007";
+  public static final String s4008 = "4008";
+  public static final String s4009 = "4009";
+  public static final String s4010 = "4010";
+  public static final String s4011 = "4011";
+  public static final String s4012 = "4012";
+  public static final String s4013 = "4013";
+  public static final String s4014 = "4014";
+  public static final String s4015 = "4015";
+  public static final String s4016 = "4016";
+  public static final String s4017 = "4017";
+  public static final String s4018 = "4018";
+  public static final String s4019 = "4019";
+  public static final String s4020 = "4020";
+  public static final String s4021 = "4021";
+  public static final String s4022 = "4022";
+  public static final String s4023 = "4023";
+  public static final String s4024 = "4024";
+  public static final String s4025 = "4025";
+  public static final String s4026 = "4026";
+  public static final String s4027 = "4027";
+  public static final String s4028 = "4028";
+  public static final String s4029 = "4029";
+  public static final String s4030 = "4030";
+  public static final String s4031 = "4031";
+  public static final String s4032 = "4032";
+  public static final String s4033 = "4033";
+  public static final String s4034 = "4034";
+  public static final String s4035 = "4035";
+  public static final String s4036 = "4036";
+  public static final String s4037 = "4037";
+  public static final String s4038 = "4038";
+  public static final String s4039 = "4039";
+  public static final String s4040 = "4040";
+  public static final String s4041 = "4041";
+  public static final String s4042 = "4042";
+  public static final String s4043 = "4043";
+  public static final String s4044 = "4044";
+  public static final String s4045 = "4045";
+  public static final String s4046 = "4046";
+  public static final String s4047 = "4047";
+  public static final String s4048 = "4048";
+  public static final String s4049 = "4049";
+  public static final String s4050 = "4050";
+  public static final String s4051 = "4051";
+  public static final String s4052 = "4052";
+  public static final String s4053 = "4053";
+  public static final String s4054 = "4054";
+  public static final String s4055 = "4055";
+  public static final String s4056 = "4056";
+  public static final String s4057 = "4057";
+  public static final String s4058 = "4058";
+  public static final String s4059 = "4059";
+  public static final String s4060 = "4060";
+  public static final String s4061 = "4061";
+  public static final String s4062 = "4062";
+  public static final String s4063 = "4063";
+  public static final String s4064 = "4064";
+  public static final String s4065 = "4065";
+  public static final String s4066 = "4066";
+  public static final String s4067 = "4067";
+  public static final String s4068 = "4068";
+  public static final String s4069 = "4069";
+  public static final String s4070 = "4070";
+  public static final String s4071 = "4071";
+  public static final String s4072 = "4072";
+  public static final String s4073 = "4073";
+  public static final String s4074 = "4074";
+  public static final String s4075 = "4075";
+  public static final String s4076 = "4076";
+  public static final String s4077 = "4077";
+  public static final String s4078 = "4078";
+  public static final String s4079 = "4079";
+  public static final String s4080 = "4080";
+  public static final String s4081 = "4081";
+  public static final String s4082 = "4082";
+  public static final String s4083 = "4083";
+  public static final String s4084 = "4084";
+  public static final String s4085 = "4085";
+  public static final String s4086 = "4086";
+  public static final String s4087 = "4087";
+  public static final String s4088 = "4088";
+  public static final String s4089 = "4089";
+  public static final String s4090 = "4090";
+  public static final String s4091 = "4091";
+  public static final String s4092 = "4092";
+  public static final String s4093 = "4093";
+  public static final String s4094 = "4094";
+  public static final String s4095 = "4095";
+  public static final String s4096 = "4096";
+  public static final String s4097 = "4097";
+  public static final String s4098 = "4098";
+  public static final String s4099 = "4099";
+  public static final String s4100 = "4100";
+  public static final String s4101 = "4101";
+  public static final String s4102 = "4102";
+  public static final String s4103 = "4103";
+  public static final String s4104 = "4104";
+  public static final String s4105 = "4105";
+  public static final String s4106 = "4106";
+  public static final String s4107 = "4107";
+  public static final String s4108 = "4108";
+  public static final String s4109 = "4109";
+  public static final String s4110 = "4110";
+  public static final String s4111 = "4111";
+  public static final String s4112 = "4112";
+  public static final String s4113 = "4113";
+  public static final String s4114 = "4114";
+  public static final String s4115 = "4115";
+  public static final String s4116 = "4116";
+  public static final String s4117 = "4117";
+  public static final String s4118 = "4118";
+  public static final String s4119 = "4119";
+  public static final String s4120 = "4120";
+  public static final String s4121 = "4121";
+  public static final String s4122 = "4122";
+  public static final String s4123 = "4123";
+  public static final String s4124 = "4124";
+  public static final String s4125 = "4125";
+  public static final String s4126 = "4126";
+  public static final String s4127 = "4127";
+  public static final String s4128 = "4128";
+  public static final String s4129 = "4129";
+  public static final String s4130 = "4130";
+  public static final String s4131 = "4131";
+  public static final String s4132 = "4132";
+  public static final String s4133 = "4133";
+  public static final String s4134 = "4134";
+  public static final String s4135 = "4135";
+  public static final String s4136 = "4136";
+  public static final String s4137 = "4137";
+  public static final String s4138 = "4138";
+  public static final String s4139 = "4139";
+  public static final String s4140 = "4140";
+  public static final String s4141 = "4141";
+  public static final String s4142 = "4142";
+  public static final String s4143 = "4143";
+  public static final String s4144 = "4144";
+  public static final String s4145 = "4145";
+  public static final String s4146 = "4146";
+  public static final String s4147 = "4147";
+  public static final String s4148 = "4148";
+  public static final String s4149 = "4149";
+  public static final String s4150 = "4150";
+  public static final String s4151 = "4151";
+  public static final String s4152 = "4152";
+  public static final String s4153 = "4153";
+  public static final String s4154 = "4154";
+  public static final String s4155 = "4155";
+  public static final String s4156 = "4156";
+  public static final String s4157 = "4157";
+  public static final String s4158 = "4158";
+  public static final String s4159 = "4159";
+  public static final String s4160 = "4160";
+  public static final String s4161 = "4161";
+  public static final String s4162 = "4162";
+  public static final String s4163 = "4163";
+  public static final String s4164 = "4164";
+  public static final String s4165 = "4165";
+  public static final String s4166 = "4166";
+  public static final String s4167 = "4167";
+  public static final String s4168 = "4168";
+  public static final String s4169 = "4169";
+  public static final String s4170 = "4170";
+  public static final String s4171 = "4171";
+  public static final String s4172 = "4172";
+  public static final String s4173 = "4173";
+  public static final String s4174 = "4174";
+  public static final String s4175 = "4175";
+  public static final String s4176 = "4176";
+  public static final String s4177 = "4177";
+  public static final String s4178 = "4178";
+  public static final String s4179 = "4179";
+  public static final String s4180 = "4180";
+  public static final String s4181 = "4181";
+  public static final String s4182 = "4182";
+  public static final String s4183 = "4183";
+  public static final String s4184 = "4184";
+  public static final String s4185 = "4185";
+  public static final String s4186 = "4186";
+  public static final String s4187 = "4187";
+  public static final String s4188 = "4188";
+  public static final String s4189 = "4189";
+  public static final String s4190 = "4190";
+  public static final String s4191 = "4191";
+  public static final String s4192 = "4192";
+  public static final String s4193 = "4193";
+  public static final String s4194 = "4194";
+  public static final String s4195 = "4195";
+  public static final String s4196 = "4196";
+  public static final String s4197 = "4197";
+  public static final String s4198 = "4198";
+  public static final String s4199 = "4199";
+  public static final String s4200 = "4200";
+  public static final String s4201 = "4201";
+  public static final String s4202 = "4202";
+  public static final String s4203 = "4203";
+  public static final String s4204 = "4204";
+  public static final String s4205 = "4205";
+  public static final String s4206 = "4206";
+  public static final String s4207 = "4207";
+  public static final String s4208 = "4208";
+  public static final String s4209 = "4209";
+  public static final String s4210 = "4210";
+  public static final String s4211 = "4211";
+  public static final String s4212 = "4212";
+  public static final String s4213 = "4213";
+  public static final String s4214 = "4214";
+  public static final String s4215 = "4215";
+  public static final String s4216 = "4216";
+  public static final String s4217 = "4217";
+  public static final String s4218 = "4218";
+  public static final String s4219 = "4219";
+  public static final String s4220 = "4220";
+  public static final String s4221 = "4221";
+  public static final String s4222 = "4222";
+  public static final String s4223 = "4223";
+  public static final String s4224 = "4224";
+  public static final String s4225 = "4225";
+  public static final String s4226 = "4226";
+  public static final String s4227 = "4227";
+  public static final String s4228 = "4228";
+  public static final String s4229 = "4229";
+  public static final String s4230 = "4230";
+  public static final String s4231 = "4231";
+  public static final String s4232 = "4232";
+  public static final String s4233 = "4233";
+  public static final String s4234 = "4234";
+  public static final String s4235 = "4235";
+  public static final String s4236 = "4236";
+  public static final String s4237 = "4237";
+  public static final String s4238 = "4238";
+  public static final String s4239 = "4239";
+  public static final String s4240 = "4240";
+  public static final String s4241 = "4241";
+  public static final String s4242 = "4242";
+  public static final String s4243 = "4243";
+  public static final String s4244 = "4244";
+  public static final String s4245 = "4245";
+  public static final String s4246 = "4246";
+  public static final String s4247 = "4247";
+  public static final String s4248 = "4248";
+  public static final String s4249 = "4249";
+  public static final String s4250 = "4250";
+  public static final String s4251 = "4251";
+  public static final String s4252 = "4252";
+  public static final String s4253 = "4253";
+  public static final String s4254 = "4254";
+  public static final String s4255 = "4255";
+  public static final String s4256 = "4256";
+  public static final String s4257 = "4257";
+  public static final String s4258 = "4258";
+  public static final String s4259 = "4259";
+  public static final String s4260 = "4260";
+  public static final String s4261 = "4261";
+  public static final String s4262 = "4262";
+  public static final String s4263 = "4263";
+  public static final String s4264 = "4264";
+  public static final String s4265 = "4265";
+  public static final String s4266 = "4266";
+  public static final String s4267 = "4267";
+  public static final String s4268 = "4268";
+  public static final String s4269 = "4269";
+  public static final String s4270 = "4270";
+  public static final String s4271 = "4271";
+  public static final String s4272 = "4272";
+  public static final String s4273 = "4273";
+  public static final String s4274 = "4274";
+  public static final String s4275 = "4275";
+  public static final String s4276 = "4276";
+  public static final String s4277 = "4277";
+  public static final String s4278 = "4278";
+  public static final String s4279 = "4279";
+  public static final String s4280 = "4280";
+  public static final String s4281 = "4281";
+  public static final String s4282 = "4282";
+  public static final String s4283 = "4283";
+  public static final String s4284 = "4284";
+  public static final String s4285 = "4285";
+  public static final String s4286 = "4286";
+  public static final String s4287 = "4287";
+  public static final String s4288 = "4288";
+  public static final String s4289 = "4289";
+  public static final String s4290 = "4290";
+  public static final String s4291 = "4291";
+  public static final String s4292 = "4292";
+  public static final String s4293 = "4293";
+  public static final String s4294 = "4294";
+  public static final String s4295 = "4295";
+  public static final String s4296 = "4296";
+  public static final String s4297 = "4297";
+  public static final String s4298 = "4298";
+  public static final String s4299 = "4299";
+  public static final String s4300 = "4300";
+  public static final String s4301 = "4301";
+  public static final String s4302 = "4302";
+  public static final String s4303 = "4303";
+  public static final String s4304 = "4304";
+  public static final String s4305 = "4305";
+  public static final String s4306 = "4306";
+  public static final String s4307 = "4307";
+  public static final String s4308 = "4308";
+  public static final String s4309 = "4309";
+  public static final String s4310 = "4310";
+  public static final String s4311 = "4311";
+  public static final String s4312 = "4312";
+  public static final String s4313 = "4313";
+  public static final String s4314 = "4314";
+  public static final String s4315 = "4315";
+  public static final String s4316 = "4316";
+  public static final String s4317 = "4317";
+  public static final String s4318 = "4318";
+  public static final String s4319 = "4319";
+  public static final String s4320 = "4320";
+  public static final String s4321 = "4321";
+  public static final String s4322 = "4322";
+  public static final String s4323 = "4323";
+  public static final String s4324 = "4324";
+  public static final String s4325 = "4325";
+  public static final String s4326 = "4326";
+  public static final String s4327 = "4327";
+  public static final String s4328 = "4328";
+  public static final String s4329 = "4329";
+  public static final String s4330 = "4330";
+  public static final String s4331 = "4331";
+  public static final String s4332 = "4332";
+  public static final String s4333 = "4333";
+  public static final String s4334 = "4334";
+  public static final String s4335 = "4335";
+  public static final String s4336 = "4336";
+  public static final String s4337 = "4337";
+  public static final String s4338 = "4338";
+  public static final String s4339 = "4339";
+  public static final String s4340 = "4340";
+  public static final String s4341 = "4341";
+  public static final String s4342 = "4342";
+  public static final String s4343 = "4343";
+  public static final String s4344 = "4344";
+  public static final String s4345 = "4345";
+  public static final String s4346 = "4346";
+  public static final String s4347 = "4347";
+  public static final String s4348 = "4348";
+  public static final String s4349 = "4349";
+  public static final String s4350 = "4350";
+  public static final String s4351 = "4351";
+  public static final String s4352 = "4352";
+  public static final String s4353 = "4353";
+  public static final String s4354 = "4354";
+  public static final String s4355 = "4355";
+  public static final String s4356 = "4356";
+  public static final String s4357 = "4357";
+  public static final String s4358 = "4358";
+  public static final String s4359 = "4359";
+  public static final String s4360 = "4360";
+  public static final String s4361 = "4361";
+  public static final String s4362 = "4362";
+  public static final String s4363 = "4363";
+  public static final String s4364 = "4364";
+  public static final String s4365 = "4365";
+  public static final String s4366 = "4366";
+  public static final String s4367 = "4367";
+  public static final String s4368 = "4368";
+  public static final String s4369 = "4369";
+  public static final String s4370 = "4370";
+  public static final String s4371 = "4371";
+  public static final String s4372 = "4372";
+  public static final String s4373 = "4373";
+  public static final String s4374 = "4374";
+  public static final String s4375 = "4375";
+  public static final String s4376 = "4376";
+  public static final String s4377 = "4377";
+  public static final String s4378 = "4378";
+  public static final String s4379 = "4379";
+  public static final String s4380 = "4380";
+  public static final String s4381 = "4381";
+  public static final String s4382 = "4382";
+  public static final String s4383 = "4383";
+  public static final String s4384 = "4384";
+  public static final String s4385 = "4385";
+  public static final String s4386 = "4386";
+  public static final String s4387 = "4387";
+  public static final String s4388 = "4388";
+  public static final String s4389 = "4389";
+  public static final String s4390 = "4390";
+  public static final String s4391 = "4391";
+  public static final String s4392 = "4392";
+  public static final String s4393 = "4393";
+  public static final String s4394 = "4394";
+  public static final String s4395 = "4395";
+  public static final String s4396 = "4396";
+  public static final String s4397 = "4397";
+  public static final String s4398 = "4398";
+  public static final String s4399 = "4399";
+  public static final String s4400 = "4400";
+  public static final String s4401 = "4401";
+  public static final String s4402 = "4402";
+  public static final String s4403 = "4403";
+  public static final String s4404 = "4404";
+  public static final String s4405 = "4405";
+  public static final String s4406 = "4406";
+  public static final String s4407 = "4407";
+  public static final String s4408 = "4408";
+  public static final String s4409 = "4409";
+  public static final String s4410 = "4410";
+  public static final String s4411 = "4411";
+  public static final String s4412 = "4412";
+  public static final String s4413 = "4413";
+  public static final String s4414 = "4414";
+  public static final String s4415 = "4415";
+  public static final String s4416 = "4416";
+  public static final String s4417 = "4417";
+  public static final String s4418 = "4418";
+  public static final String s4419 = "4419";
+  public static final String s4420 = "4420";
+  public static final String s4421 = "4421";
+  public static final String s4422 = "4422";
+  public static final String s4423 = "4423";
+  public static final String s4424 = "4424";
+  public static final String s4425 = "4425";
+  public static final String s4426 = "4426";
+  public static final String s4427 = "4427";
+  public static final String s4428 = "4428";
+  public static final String s4429 = "4429";
+  public static final String s4430 = "4430";
+  public static final String s4431 = "4431";
+  public static final String s4432 = "4432";
+  public static final String s4433 = "4433";
+  public static final String s4434 = "4434";
+  public static final String s4435 = "4435";
+  public static final String s4436 = "4436";
+  public static final String s4437 = "4437";
+  public static final String s4438 = "4438";
+  public static final String s4439 = "4439";
+  public static final String s4440 = "4440";
+  public static final String s4441 = "4441";
+  public static final String s4442 = "4442";
+  public static final String s4443 = "4443";
+  public static final String s4444 = "4444";
+  public static final String s4445 = "4445";
+  public static final String s4446 = "4446";
+  public static final String s4447 = "4447";
+  public static final String s4448 = "4448";
+  public static final String s4449 = "4449";
+  public static final String s4450 = "4450";
+  public static final String s4451 = "4451";
+  public static final String s4452 = "4452";
+  public static final String s4453 = "4453";
+  public static final String s4454 = "4454";
+  public static final String s4455 = "4455";
+  public static final String s4456 = "4456";
+  public static final String s4457 = "4457";
+  public static final String s4458 = "4458";
+  public static final String s4459 = "4459";
+  public static final String s4460 = "4460";
+  public static final String s4461 = "4461";
+  public static final String s4462 = "4462";
+  public static final String s4463 = "4463";
+  public static final String s4464 = "4464";
+  public static final String s4465 = "4465";
+  public static final String s4466 = "4466";
+  public static final String s4467 = "4467";
+  public static final String s4468 = "4468";
+  public static final String s4469 = "4469";
+  public static final String s4470 = "4470";
+  public static final String s4471 = "4471";
+  public static final String s4472 = "4472";
+  public static final String s4473 = "4473";
+  public static final String s4474 = "4474";
+  public static final String s4475 = "4475";
+  public static final String s4476 = "4476";
+  public static final String s4477 = "4477";
+  public static final String s4478 = "4478";
+  public static final String s4479 = "4479";
+  public static final String s4480 = "4480";
+  public static final String s4481 = "4481";
+  public static final String s4482 = "4482";
+  public static final String s4483 = "4483";
+  public static final String s4484 = "4484";
+  public static final String s4485 = "4485";
+  public static final String s4486 = "4486";
+  public static final String s4487 = "4487";
+  public static final String s4488 = "4488";
+  public static final String s4489 = "4489";
+  public static final String s4490 = "4490";
+  public static final String s4491 = "4491";
+  public static final String s4492 = "4492";
+  public static final String s4493 = "4493";
+  public static final String s4494 = "4494";
+  public static final String s4495 = "4495";
+  public static final String s4496 = "4496";
+  public static final String s4497 = "4497";
+  public static final String s4498 = "4498";
+  public static final String s4499 = "4499";
+  public static final String s4500 = "4500";
+  public static final String s4501 = "4501";
+  public static final String s4502 = "4502";
+  public static final String s4503 = "4503";
+  public static final String s4504 = "4504";
+  public static final String s4505 = "4505";
+  public static final String s4506 = "4506";
+  public static final String s4507 = "4507";
+  public static final String s4508 = "4508";
+  public static final String s4509 = "4509";
+  public static final String s4510 = "4510";
+  public static final String s4511 = "4511";
+  public static final String s4512 = "4512";
+  public static final String s4513 = "4513";
+  public static final String s4514 = "4514";
+  public static final String s4515 = "4515";
+  public static final String s4516 = "4516";
+  public static final String s4517 = "4517";
+  public static final String s4518 = "4518";
+  public static final String s4519 = "4519";
+  public static final String s4520 = "4520";
+  public static final String s4521 = "4521";
+  public static final String s4522 = "4522";
+  public static final String s4523 = "4523";
+  public static final String s4524 = "4524";
+  public static final String s4525 = "4525";
+  public static final String s4526 = "4526";
+  public static final String s4527 = "4527";
+  public static final String s4528 = "4528";
+  public static final String s4529 = "4529";
+  public static final String s4530 = "4530";
+  public static final String s4531 = "4531";
+  public static final String s4532 = "4532";
+  public static final String s4533 = "4533";
+  public static final String s4534 = "4534";
+  public static final String s4535 = "4535";
+  public static final String s4536 = "4536";
+  public static final String s4537 = "4537";
+  public static final String s4538 = "4538";
+  public static final String s4539 = "4539";
+  public static final String s4540 = "4540";
+  public static final String s4541 = "4541";
+  public static final String s4542 = "4542";
+  public static final String s4543 = "4543";
+  public static final String s4544 = "4544";
+  public static final String s4545 = "4545";
+  public static final String s4546 = "4546";
+  public static final String s4547 = "4547";
+  public static final String s4548 = "4548";
+  public static final String s4549 = "4549";
+  public static final String s4550 = "4550";
+  public static final String s4551 = "4551";
+  public static final String s4552 = "4552";
+  public static final String s4553 = "4553";
+  public static final String s4554 = "4554";
+  public static final String s4555 = "4555";
+  public static final String s4556 = "4556";
+  public static final String s4557 = "4557";
+  public static final String s4558 = "4558";
+  public static final String s4559 = "4559";
+  public static final String s4560 = "4560";
+  public static final String s4561 = "4561";
+  public static final String s4562 = "4562";
+  public static final String s4563 = "4563";
+  public static final String s4564 = "4564";
+  public static final String s4565 = "4565";
+  public static final String s4566 = "4566";
+  public static final String s4567 = "4567";
+  public static final String s4568 = "4568";
+  public static final String s4569 = "4569";
+  public static final String s4570 = "4570";
+  public static final String s4571 = "4571";
+  public static final String s4572 = "4572";
+  public static final String s4573 = "4573";
+  public static final String s4574 = "4574";
+  public static final String s4575 = "4575";
+  public static final String s4576 = "4576";
+  public static final String s4577 = "4577";
+  public static final String s4578 = "4578";
+  public static final String s4579 = "4579";
+  public static final String s4580 = "4580";
+  public static final String s4581 = "4581";
+  public static final String s4582 = "4582";
+  public static final String s4583 = "4583";
+  public static final String s4584 = "4584";
+  public static final String s4585 = "4585";
+  public static final String s4586 = "4586";
+  public static final String s4587 = "4587";
+  public static final String s4588 = "4588";
+  public static final String s4589 = "4589";
+  public static final String s4590 = "4590";
+  public static final String s4591 = "4591";
+  public static final String s4592 = "4592";
+  public static final String s4593 = "4593";
+  public static final String s4594 = "4594";
+  public static final String s4595 = "4595";
+  public static final String s4596 = "4596";
+  public static final String s4597 = "4597";
+  public static final String s4598 = "4598";
+  public static final String s4599 = "4599";
+  public static final String s4600 = "4600";
+  public static final String s4601 = "4601";
+  public static final String s4602 = "4602";
+  public static final String s4603 = "4603";
+  public static final String s4604 = "4604";
+  public static final String s4605 = "4605";
+  public static final String s4606 = "4606";
+  public static final String s4607 = "4607";
+  public static final String s4608 = "4608";
+  public static final String s4609 = "4609";
+  public static final String s4610 = "4610";
+  public static final String s4611 = "4611";
+  public static final String s4612 = "4612";
+  public static final String s4613 = "4613";
+  public static final String s4614 = "4614";
+  public static final String s4615 = "4615";
+  public static final String s4616 = "4616";
+  public static final String s4617 = "4617";
+  public static final String s4618 = "4618";
+  public static final String s4619 = "4619";
+  public static final String s4620 = "4620";
+  public static final String s4621 = "4621";
+  public static final String s4622 = "4622";
+  public static final String s4623 = "4623";
+  public static final String s4624 = "4624";
+  public static final String s4625 = "4625";
+  public static final String s4626 = "4626";
+  public static final String s4627 = "4627";
+  public static final String s4628 = "4628";
+  public static final String s4629 = "4629";
+  public static final String s4630 = "4630";
+  public static final String s4631 = "4631";
+  public static final String s4632 = "4632";
+  public static final String s4633 = "4633";
+  public static final String s4634 = "4634";
+  public static final String s4635 = "4635";
+  public static final String s4636 = "4636";
+  public static final String s4637 = "4637";
+  public static final String s4638 = "4638";
+  public static final String s4639 = "4639";
+  public static final String s4640 = "4640";
+  public static final String s4641 = "4641";
+  public static final String s4642 = "4642";
+  public static final String s4643 = "4643";
+  public static final String s4644 = "4644";
+  public static final String s4645 = "4645";
+  public static final String s4646 = "4646";
+  public static final String s4647 = "4647";
+  public static final String s4648 = "4648";
+  public static final String s4649 = "4649";
+  public static final String s4650 = "4650";
+  public static final String s4651 = "4651";
+  public static final String s4652 = "4652";
+  public static final String s4653 = "4653";
+  public static final String s4654 = "4654";
+  public static final String s4655 = "4655";
+  public static final String s4656 = "4656";
+  public static final String s4657 = "4657";
+  public static final String s4658 = "4658";
+  public static final String s4659 = "4659";
+  public static final String s4660 = "4660";
+  public static final String s4661 = "4661";
+  public static final String s4662 = "4662";
+  public static final String s4663 = "4663";
+  public static final String s4664 = "4664";
+  public static final String s4665 = "4665";
+  public static final String s4666 = "4666";
+  public static final String s4667 = "4667";
+  public static final String s4668 = "4668";
+  public static final String s4669 = "4669";
+  public static final String s4670 = "4670";
+  public static final String s4671 = "4671";
+  public static final String s4672 = "4672";
+  public static final String s4673 = "4673";
+  public static final String s4674 = "4674";
+  public static final String s4675 = "4675";
+  public static final String s4676 = "4676";
+  public static final String s4677 = "4677";
+  public static final String s4678 = "4678";
+  public static final String s4679 = "4679";
+  public static final String s4680 = "4680";
+  public static final String s4681 = "4681";
+  public static final String s4682 = "4682";
+  public static final String s4683 = "4683";
+  public static final String s4684 = "4684";
+  public static final String s4685 = "4685";
+  public static final String s4686 = "4686";
+  public static final String s4687 = "4687";
+  public static final String s4688 = "4688";
+  public static final String s4689 = "4689";
+  public static final String s4690 = "4690";
+  public static final String s4691 = "4691";
+  public static final String s4692 = "4692";
+  public static final String s4693 = "4693";
+  public static final String s4694 = "4694";
+  public static final String s4695 = "4695";
+  public static final String s4696 = "4696";
+  public static final String s4697 = "4697";
+  public static final String s4698 = "4698";
+  public static final String s4699 = "4699";
+  public static final String s4700 = "4700";
+  public static final String s4701 = "4701";
+  public static final String s4702 = "4702";
+  public static final String s4703 = "4703";
+  public static final String s4704 = "4704";
+  public static final String s4705 = "4705";
+  public static final String s4706 = "4706";
+  public static final String s4707 = "4707";
+  public static final String s4708 = "4708";
+  public static final String s4709 = "4709";
+  public static final String s4710 = "4710";
+  public static final String s4711 = "4711";
+  public static final String s4712 = "4712";
+  public static final String s4713 = "4713";
+  public static final String s4714 = "4714";
+  public static final String s4715 = "4715";
+  public static final String s4716 = "4716";
+  public static final String s4717 = "4717";
+  public static final String s4718 = "4718";
+  public static final String s4719 = "4719";
+  public static final String s4720 = "4720";
+  public static final String s4721 = "4721";
+  public static final String s4722 = "4722";
+  public static final String s4723 = "4723";
+  public static final String s4724 = "4724";
+  public static final String s4725 = "4725";
+  public static final String s4726 = "4726";
+  public static final String s4727 = "4727";
+  public static final String s4728 = "4728";
+  public static final String s4729 = "4729";
+  public static final String s4730 = "4730";
+  public static final String s4731 = "4731";
+  public static final String s4732 = "4732";
+  public static final String s4733 = "4733";
+  public static final String s4734 = "4734";
+  public static final String s4735 = "4735";
+  public static final String s4736 = "4736";
+  public static final String s4737 = "4737";
+  public static final String s4738 = "4738";
+  public static final String s4739 = "4739";
+  public static final String s4740 = "4740";
+  public static final String s4741 = "4741";
+  public static final String s4742 = "4742";
+  public static final String s4743 = "4743";
+  public static final String s4744 = "4744";
+  public static final String s4745 = "4745";
+  public static final String s4746 = "4746";
+  public static final String s4747 = "4747";
+  public static final String s4748 = "4748";
+  public static final String s4749 = "4749";
+  public static final String s4750 = "4750";
+  public static final String s4751 = "4751";
+  public static final String s4752 = "4752";
+  public static final String s4753 = "4753";
+  public static final String s4754 = "4754";
+  public static final String s4755 = "4755";
+  public static final String s4756 = "4756";
+  public static final String s4757 = "4757";
+  public static final String s4758 = "4758";
+  public static final String s4759 = "4759";
+  public static final String s4760 = "4760";
+  public static final String s4761 = "4761";
+  public static final String s4762 = "4762";
+  public static final String s4763 = "4763";
+  public static final String s4764 = "4764";
+  public static final String s4765 = "4765";
+  public static final String s4766 = "4766";
+  public static final String s4767 = "4767";
+  public static final String s4768 = "4768";
+  public static final String s4769 = "4769";
+  public static final String s4770 = "4770";
+  public static final String s4771 = "4771";
+  public static final String s4772 = "4772";
+  public static final String s4773 = "4773";
+  public static final String s4774 = "4774";
+  public static final String s4775 = "4775";
+  public static final String s4776 = "4776";
+  public static final String s4777 = "4777";
+  public static final String s4778 = "4778";
+  public static final String s4779 = "4779";
+  public static final String s4780 = "4780";
+  public static final String s4781 = "4781";
+  public static final String s4782 = "4782";
+  public static final String s4783 = "4783";
+  public static final String s4784 = "4784";
+  public static final String s4785 = "4785";
+  public static final String s4786 = "4786";
+  public static final String s4787 = "4787";
+  public static final String s4788 = "4788";
+  public static final String s4789 = "4789";
+  public static final String s4790 = "4790";
+  public static final String s4791 = "4791";
+  public static final String s4792 = "4792";
+  public static final String s4793 = "4793";
+  public static final String s4794 = "4794";
+  public static final String s4795 = "4795";
+  public static final String s4796 = "4796";
+  public static final String s4797 = "4797";
+  public static final String s4798 = "4798";
+  public static final String s4799 = "4799";
+  public static final String s4800 = "4800";
+  public static final String s4801 = "4801";
+  public static final String s4802 = "4802";
+  public static final String s4803 = "4803";
+  public static final String s4804 = "4804";
+  public static final String s4805 = "4805";
+  public static final String s4806 = "4806";
+  public static final String s4807 = "4807";
+  public static final String s4808 = "4808";
+  public static final String s4809 = "4809";
+  public static final String s4810 = "4810";
+  public static final String s4811 = "4811";
+  public static final String s4812 = "4812";
+  public static final String s4813 = "4813";
+  public static final String s4814 = "4814";
+  public static final String s4815 = "4815";
+  public static final String s4816 = "4816";
+  public static final String s4817 = "4817";
+  public static final String s4818 = "4818";
+  public static final String s4819 = "4819";
+  public static final String s4820 = "4820";
+  public static final String s4821 = "4821";
+  public static final String s4822 = "4822";
+  public static final String s4823 = "4823";
+  public static final String s4824 = "4824";
+  public static final String s4825 = "4825";
+  public static final String s4826 = "4826";
+  public static final String s4827 = "4827";
+  public static final String s4828 = "4828";
+  public static final String s4829 = "4829";
+  public static final String s4830 = "4830";
+  public static final String s4831 = "4831";
+  public static final String s4832 = "4832";
+  public static final String s4833 = "4833";
+  public static final String s4834 = "4834";
+  public static final String s4835 = "4835";
+  public static final String s4836 = "4836";
+  public static final String s4837 = "4837";
+  public static final String s4838 = "4838";
+  public static final String s4839 = "4839";
+  public static final String s4840 = "4840";
+  public static final String s4841 = "4841";
+  public static final String s4842 = "4842";
+  public static final String s4843 = "4843";
+  public static final String s4844 = "4844";
+  public static final String s4845 = "4845";
+  public static final String s4846 = "4846";
+  public static final String s4847 = "4847";
+  public static final String s4848 = "4848";
+  public static final String s4849 = "4849";
+  public static final String s4850 = "4850";
+  public static final String s4851 = "4851";
+  public static final String s4852 = "4852";
+  public static final String s4853 = "4853";
+  public static final String s4854 = "4854";
+  public static final String s4855 = "4855";
+  public static final String s4856 = "4856";
+  public static final String s4857 = "4857";
+  public static final String s4858 = "4858";
+  public static final String s4859 = "4859";
+  public static final String s4860 = "4860";
+  public static final String s4861 = "4861";
+  public static final String s4862 = "4862";
+  public static final String s4863 = "4863";
+  public static final String s4864 = "4864";
+  public static final String s4865 = "4865";
+  public static final String s4866 = "4866";
+  public static final String s4867 = "4867";
+  public static final String s4868 = "4868";
+  public static final String s4869 = "4869";
+  public static final String s4870 = "4870";
+  public static final String s4871 = "4871";
+  public static final String s4872 = "4872";
+  public static final String s4873 = "4873";
+  public static final String s4874 = "4874";
+  public static final String s4875 = "4875";
+  public static final String s4876 = "4876";
+  public static final String s4877 = "4877";
+  public static final String s4878 = "4878";
+  public static final String s4879 = "4879";
+  public static final String s4880 = "4880";
+  public static final String s4881 = "4881";
+  public static final String s4882 = "4882";
+  public static final String s4883 = "4883";
+  public static final String s4884 = "4884";
+  public static final String s4885 = "4885";
+  public static final String s4886 = "4886";
+  public static final String s4887 = "4887";
+  public static final String s4888 = "4888";
+  public static final String s4889 = "4889";
+  public static final String s4890 = "4890";
+  public static final String s4891 = "4891";
+  public static final String s4892 = "4892";
+  public static final String s4893 = "4893";
+  public static final String s4894 = "4894";
+  public static final String s4895 = "4895";
+  public static final String s4896 = "4896";
+  public static final String s4897 = "4897";
+  public static final String s4898 = "4898";
+  public static final String s4899 = "4899";
+  public static final String s4900 = "4900";
+  public static final String s4901 = "4901";
+  public static final String s4902 = "4902";
+  public static final String s4903 = "4903";
+  public static final String s4904 = "4904";
+  public static final String s4905 = "4905";
+  public static final String s4906 = "4906";
+  public static final String s4907 = "4907";
+  public static final String s4908 = "4908";
+  public static final String s4909 = "4909";
+  public static final String s4910 = "4910";
+  public static final String s4911 = "4911";
+  public static final String s4912 = "4912";
+  public static final String s4913 = "4913";
+  public static final String s4914 = "4914";
+  public static final String s4915 = "4915";
+  public static final String s4916 = "4916";
+  public static final String s4917 = "4917";
+  public static final String s4918 = "4918";
+  public static final String s4919 = "4919";
+  public static final String s4920 = "4920";
+  public static final String s4921 = "4921";
+  public static final String s4922 = "4922";
+  public static final String s4923 = "4923";
+  public static final String s4924 = "4924";
+  public static final String s4925 = "4925";
+  public static final String s4926 = "4926";
+  public static final String s4927 = "4927";
+  public static final String s4928 = "4928";
+  public static final String s4929 = "4929";
+  public static final String s4930 = "4930";
+  public static final String s4931 = "4931";
+  public static final String s4932 = "4932";
+  public static final String s4933 = "4933";
+  public static final String s4934 = "4934";
+  public static final String s4935 = "4935";
+  public static final String s4936 = "4936";
+  public static final String s4937 = "4937";
+  public static final String s4938 = "4938";
+  public static final String s4939 = "4939";
+  public static final String s4940 = "4940";
+  public static final String s4941 = "4941";
+  public static final String s4942 = "4942";
+  public static final String s4943 = "4943";
+  public static final String s4944 = "4944";
+  public static final String s4945 = "4945";
+  public static final String s4946 = "4946";
+  public static final String s4947 = "4947";
+  public static final String s4948 = "4948";
+  public static final String s4949 = "4949";
+  public static final String s4950 = "4950";
+  public static final String s4951 = "4951";
+  public static final String s4952 = "4952";
+  public static final String s4953 = "4953";
+  public static final String s4954 = "4954";
+  public static final String s4955 = "4955";
+  public static final String s4956 = "4956";
+  public static final String s4957 = "4957";
+  public static final String s4958 = "4958";
+  public static final String s4959 = "4959";
+  public static final String s4960 = "4960";
+  public static final String s4961 = "4961";
+  public static final String s4962 = "4962";
+  public static final String s4963 = "4963";
+  public static final String s4964 = "4964";
+  public static final String s4965 = "4965";
+  public static final String s4966 = "4966";
+  public static final String s4967 = "4967";
+  public static final String s4968 = "4968";
+  public static final String s4969 = "4969";
+  public static final String s4970 = "4970";
+  public static final String s4971 = "4971";
+  public static final String s4972 = "4972";
+  public static final String s4973 = "4973";
+  public static final String s4974 = "4974";
+  public static final String s4975 = "4975";
+  public static final String s4976 = "4976";
+  public static final String s4977 = "4977";
+  public static final String s4978 = "4978";
+  public static final String s4979 = "4979";
+  public static final String s4980 = "4980";
+  public static final String s4981 = "4981";
+  public static final String s4982 = "4982";
+  public static final String s4983 = "4983";
+  public static final String s4984 = "4984";
+  public static final String s4985 = "4985";
+  public static final String s4986 = "4986";
+  public static final String s4987 = "4987";
+  public static final String s4988 = "4988";
+  public static final String s4989 = "4989";
+  public static final String s4990 = "4990";
+  public static final String s4991 = "4991";
+  public static final String s4992 = "4992";
+  public static final String s4993 = "4993";
+  public static final String s4994 = "4994";
+  public static final String s4995 = "4995";
+  public static final String s4996 = "4996";
+  public static final String s4997 = "4997";
+  public static final String s4998 = "4998";
+  public static final String s4999 = "4999";
+  public static final String s5000 = "5000";
+  public static final String s5001 = "5001";
+  public static final String s5002 = "5002";
+  public static final String s5003 = "5003";
+  public static final String s5004 = "5004";
+  public static final String s5005 = "5005";
+  public static final String s5006 = "5006";
+  public static final String s5007 = "5007";
+  public static final String s5008 = "5008";
+  public static final String s5009 = "5009";
+  public static final String s5010 = "5010";
+  public static final String s5011 = "5011";
+  public static final String s5012 = "5012";
+  public static final String s5013 = "5013";
+  public static final String s5014 = "5014";
+  public static final String s5015 = "5015";
+  public static final String s5016 = "5016";
+  public static final String s5017 = "5017";
+  public static final String s5018 = "5018";
+  public static final String s5019 = "5019";
+  public static final String s5020 = "5020";
+  public static final String s5021 = "5021";
+  public static final String s5022 = "5022";
+  public static final String s5023 = "5023";
+  public static final String s5024 = "5024";
+  public static final String s5025 = "5025";
+  public static final String s5026 = "5026";
+  public static final String s5027 = "5027";
+  public static final String s5028 = "5028";
+  public static final String s5029 = "5029";
+  public static final String s5030 = "5030";
+  public static final String s5031 = "5031";
+  public static final String s5032 = "5032";
+  public static final String s5033 = "5033";
+  public static final String s5034 = "5034";
+  public static final String s5035 = "5035";
+  public static final String s5036 = "5036";
+  public static final String s5037 = "5037";
+  public static final String s5038 = "5038";
+  public static final String s5039 = "5039";
+  public static final String s5040 = "5040";
+  public static final String s5041 = "5041";
+  public static final String s5042 = "5042";
+  public static final String s5043 = "5043";
+  public static final String s5044 = "5044";
+  public static final String s5045 = "5045";
+  public static final String s5046 = "5046";
+  public static final String s5047 = "5047";
+  public static final String s5048 = "5048";
+  public static final String s5049 = "5049";
+  public static final String s5050 = "5050";
+  public static final String s5051 = "5051";
+  public static final String s5052 = "5052";
+  public static final String s5053 = "5053";
+  public static final String s5054 = "5054";
+  public static final String s5055 = "5055";
+  public static final String s5056 = "5056";
+  public static final String s5057 = "5057";
+  public static final String s5058 = "5058";
+  public static final String s5059 = "5059";
+  public static final String s5060 = "5060";
+  public static final String s5061 = "5061";
+  public static final String s5062 = "5062";
+  public static final String s5063 = "5063";
+  public static final String s5064 = "5064";
+  public static final String s5065 = "5065";
+  public static final String s5066 = "5066";
+  public static final String s5067 = "5067";
+  public static final String s5068 = "5068";
+  public static final String s5069 = "5069";
+  public static final String s5070 = "5070";
+  public static final String s5071 = "5071";
+  public static final String s5072 = "5072";
+  public static final String s5073 = "5073";
+  public static final String s5074 = "5074";
+  public static final String s5075 = "5075";
+  public static final String s5076 = "5076";
+  public static final String s5077 = "5077";
+  public static final String s5078 = "5078";
+  public static final String s5079 = "5079";
+  public static final String s5080 = "5080";
+  public static final String s5081 = "5081";
+  public static final String s5082 = "5082";
+  public static final String s5083 = "5083";
+  public static final String s5084 = "5084";
+  public static final String s5085 = "5085";
+  public static final String s5086 = "5086";
+  public static final String s5087 = "5087";
+  public static final String s5088 = "5088";
+  public static final String s5089 = "5089";
+  public static final String s5090 = "5090";
+  public static final String s5091 = "5091";
+  public static final String s5092 = "5092";
+  public static final String s5093 = "5093";
+  public static final String s5094 = "5094";
+  public static final String s5095 = "5095";
+  public static final String s5096 = "5096";
+  public static final String s5097 = "5097";
+  public static final String s5098 = "5098";
+  public static final String s5099 = "5099";
+  public static final String s5100 = "5100";
+  public static final String s5101 = "5101";
+  public static final String s5102 = "5102";
+  public static final String s5103 = "5103";
+  public static final String s5104 = "5104";
+  public static final String s5105 = "5105";
+  public static final String s5106 = "5106";
+  public static final String s5107 = "5107";
+  public static final String s5108 = "5108";
+  public static final String s5109 = "5109";
+  public static final String s5110 = "5110";
+  public static final String s5111 = "5111";
+  public static final String s5112 = "5112";
+  public static final String s5113 = "5113";
+  public static final String s5114 = "5114";
+  public static final String s5115 = "5115";
+  public static final String s5116 = "5116";
+  public static final String s5117 = "5117";
+  public static final String s5118 = "5118";
+  public static final String s5119 = "5119";
+  public static final String s5120 = "5120";
+  public static final String s5121 = "5121";
+  public static final String s5122 = "5122";
+  public static final String s5123 = "5123";
+  public static final String s5124 = "5124";
+  public static final String s5125 = "5125";
+  public static final String s5126 = "5126";
+  public static final String s5127 = "5127";
+  public static final String s5128 = "5128";
+  public static final String s5129 = "5129";
+  public static final String s5130 = "5130";
+  public static final String s5131 = "5131";
+  public static final String s5132 = "5132";
+  public static final String s5133 = "5133";
+  public static final String s5134 = "5134";
+  public static final String s5135 = "5135";
+  public static final String s5136 = "5136";
+  public static final String s5137 = "5137";
+  public static final String s5138 = "5138";
+  public static final String s5139 = "5139";
+  public static final String s5140 = "5140";
+  public static final String s5141 = "5141";
+  public static final String s5142 = "5142";
+  public static final String s5143 = "5143";
+  public static final String s5144 = "5144";
+  public static final String s5145 = "5145";
+  public static final String s5146 = "5146";
+  public static final String s5147 = "5147";
+  public static final String s5148 = "5148";
+  public static final String s5149 = "5149";
+  public static final String s5150 = "5150";
+  public static final String s5151 = "5151";
+  public static final String s5152 = "5152";
+  public static final String s5153 = "5153";
+  public static final String s5154 = "5154";
+  public static final String s5155 = "5155";
+  public static final String s5156 = "5156";
+  public static final String s5157 = "5157";
+  public static final String s5158 = "5158";
+  public static final String s5159 = "5159";
+  public static final String s5160 = "5160";
+  public static final String s5161 = "5161";
+  public static final String s5162 = "5162";
+  public static final String s5163 = "5163";
+  public static final String s5164 = "5164";
+  public static final String s5165 = "5165";
+  public static final String s5166 = "5166";
+  public static final String s5167 = "5167";
+  public static final String s5168 = "5168";
+  public static final String s5169 = "5169";
+  public static final String s5170 = "5170";
+  public static final String s5171 = "5171";
+  public static final String s5172 = "5172";
+  public static final String s5173 = "5173";
+  public static final String s5174 = "5174";
+  public static final String s5175 = "5175";
+  public static final String s5176 = "5176";
+  public static final String s5177 = "5177";
+  public static final String s5178 = "5178";
+  public static final String s5179 = "5179";
+  public static final String s5180 = "5180";
+  public static final String s5181 = "5181";
+  public static final String s5182 = "5182";
+  public static final String s5183 = "5183";
+  public static final String s5184 = "5184";
+  public static final String s5185 = "5185";
+  public static final String s5186 = "5186";
+  public static final String s5187 = "5187";
+  public static final String s5188 = "5188";
+  public static final String s5189 = "5189";
+  public static final String s5190 = "5190";
+  public static final String s5191 = "5191";
+  public static final String s5192 = "5192";
+  public static final String s5193 = "5193";
+  public static final String s5194 = "5194";
+  public static final String s5195 = "5195";
+  public static final String s5196 = "5196";
+  public static final String s5197 = "5197";
+  public static final String s5198 = "5198";
+  public static final String s5199 = "5199";
+  public static final String s5200 = "5200";
+  public static final String s5201 = "5201";
+  public static final String s5202 = "5202";
+  public static final String s5203 = "5203";
+  public static final String s5204 = "5204";
+  public static final String s5205 = "5205";
+  public static final String s5206 = "5206";
+  public static final String s5207 = "5207";
+  public static final String s5208 = "5208";
+  public static final String s5209 = "5209";
+  public static final String s5210 = "5210";
+  public static final String s5211 = "5211";
+  public static final String s5212 = "5212";
+  public static final String s5213 = "5213";
+  public static final String s5214 = "5214";
+  public static final String s5215 = "5215";
+  public static final String s5216 = "5216";
+  public static final String s5217 = "5217";
+  public static final String s5218 = "5218";
+  public static final String s5219 = "5219";
+  public static final String s5220 = "5220";
+  public static final String s5221 = "5221";
+  public static final String s5222 = "5222";
+  public static final String s5223 = "5223";
+  public static final String s5224 = "5224";
+  public static final String s5225 = "5225";
+  public static final String s5226 = "5226";
+  public static final String s5227 = "5227";
+  public static final String s5228 = "5228";
+  public static final String s5229 = "5229";
+  public static final String s5230 = "5230";
+  public static final String s5231 = "5231";
+  public static final String s5232 = "5232";
+  public static final String s5233 = "5233";
+  public static final String s5234 = "5234";
+  public static final String s5235 = "5235";
+  public static final String s5236 = "5236";
+  public static final String s5237 = "5237";
+  public static final String s5238 = "5238";
+  public static final String s5239 = "5239";
+  public static final String s5240 = "5240";
+  public static final String s5241 = "5241";
+  public static final String s5242 = "5242";
+  public static final String s5243 = "5243";
+  public static final String s5244 = "5244";
+  public static final String s5245 = "5245";
+  public static final String s5246 = "5246";
+  public static final String s5247 = "5247";
+  public static final String s5248 = "5248";
+  public static final String s5249 = "5249";
+  public static final String s5250 = "5250";
+  public static final String s5251 = "5251";
+  public static final String s5252 = "5252";
+  public static final String s5253 = "5253";
+  public static final String s5254 = "5254";
+  public static final String s5255 = "5255";
+  public static final String s5256 = "5256";
+  public static final String s5257 = "5257";
+  public static final String s5258 = "5258";
+  public static final String s5259 = "5259";
+  public static final String s5260 = "5260";
+  public static final String s5261 = "5261";
+  public static final String s5262 = "5262";
+  public static final String s5263 = "5263";
+  public static final String s5264 = "5264";
+  public static final String s5265 = "5265";
+  public static final String s5266 = "5266";
+  public static final String s5267 = "5267";
+  public static final String s5268 = "5268";
+  public static final String s5269 = "5269";
+  public static final String s5270 = "5270";
+  public static final String s5271 = "5271";
+  public static final String s5272 = "5272";
+  public static final String s5273 = "5273";
+  public static final String s5274 = "5274";
+  public static final String s5275 = "5275";
+  public static final String s5276 = "5276";
+  public static final String s5277 = "5277";
+  public static final String s5278 = "5278";
+  public static final String s5279 = "5279";
+  public static final String s5280 = "5280";
+  public static final String s5281 = "5281";
+  public static final String s5282 = "5282";
+  public static final String s5283 = "5283";
+  public static final String s5284 = "5284";
+  public static final String s5285 = "5285";
+  public static final String s5286 = "5286";
+  public static final String s5287 = "5287";
+  public static final String s5288 = "5288";
+  public static final String s5289 = "5289";
+  public static final String s5290 = "5290";
+  public static final String s5291 = "5291";
+  public static final String s5292 = "5292";
+  public static final String s5293 = "5293";
+  public static final String s5294 = "5294";
+  public static final String s5295 = "5295";
+  public static final String s5296 = "5296";
+  public static final String s5297 = "5297";
+  public static final String s5298 = "5298";
+  public static final String s5299 = "5299";
+  public static final String s5300 = "5300";
+  public static final String s5301 = "5301";
+  public static final String s5302 = "5302";
+  public static final String s5303 = "5303";
+  public static final String s5304 = "5304";
+  public static final String s5305 = "5305";
+  public static final String s5306 = "5306";
+  public static final String s5307 = "5307";
+  public static final String s5308 = "5308";
+  public static final String s5309 = "5309";
+  public static final String s5310 = "5310";
+  public static final String s5311 = "5311";
+  public static final String s5312 = "5312";
+  public static final String s5313 = "5313";
+  public static final String s5314 = "5314";
+  public static final String s5315 = "5315";
+  public static final String s5316 = "5316";
+  public static final String s5317 = "5317";
+  public static final String s5318 = "5318";
+  public static final String s5319 = "5319";
+  public static final String s5320 = "5320";
+  public static final String s5321 = "5321";
+  public static final String s5322 = "5322";
+  public static final String s5323 = "5323";
+  public static final String s5324 = "5324";
+  public static final String s5325 = "5325";
+  public static final String s5326 = "5326";
+  public static final String s5327 = "5327";
+  public static final String s5328 = "5328";
+  public static final String s5329 = "5329";
+  public static final String s5330 = "5330";
+  public static final String s5331 = "5331";
+  public static final String s5332 = "5332";
+  public static final String s5333 = "5333";
+  public static final String s5334 = "5334";
+  public static final String s5335 = "5335";
+  public static final String s5336 = "5336";
+  public static final String s5337 = "5337";
+  public static final String s5338 = "5338";
+  public static final String s5339 = "5339";
+  public static final String s5340 = "5340";
+  public static final String s5341 = "5341";
+  public static final String s5342 = "5342";
+  public static final String s5343 = "5343";
+  public static final String s5344 = "5344";
+  public static final String s5345 = "5345";
+  public static final String s5346 = "5346";
+  public static final String s5347 = "5347";
+  public static final String s5348 = "5348";
+  public static final String s5349 = "5349";
+  public static final String s5350 = "5350";
+  public static final String s5351 = "5351";
+  public static final String s5352 = "5352";
+  public static final String s5353 = "5353";
+  public static final String s5354 = "5354";
+  public static final String s5355 = "5355";
+  public static final String s5356 = "5356";
+  public static final String s5357 = "5357";
+  public static final String s5358 = "5358";
+  public static final String s5359 = "5359";
+  public static final String s5360 = "5360";
+  public static final String s5361 = "5361";
+  public static final String s5362 = "5362";
+  public static final String s5363 = "5363";
+  public static final String s5364 = "5364";
+  public static final String s5365 = "5365";
+  public static final String s5366 = "5366";
+  public static final String s5367 = "5367";
+  public static final String s5368 = "5368";
+  public static final String s5369 = "5369";
+  public static final String s5370 = "5370";
+  public static final String s5371 = "5371";
+  public static final String s5372 = "5372";
+  public static final String s5373 = "5373";
+  public static final String s5374 = "5374";
+  public static final String s5375 = "5375";
+  public static final String s5376 = "5376";
+  public static final String s5377 = "5377";
+  public static final String s5378 = "5378";
+  public static final String s5379 = "5379";
+  public static final String s5380 = "5380";
+  public static final String s5381 = "5381";
+  public static final String s5382 = "5382";
+  public static final String s5383 = "5383";
+  public static final String s5384 = "5384";
+  public static final String s5385 = "5385";
+  public static final String s5386 = "5386";
+  public static final String s5387 = "5387";
+  public static final String s5388 = "5388";
+  public static final String s5389 = "5389";
+  public static final String s5390 = "5390";
+  public static final String s5391 = "5391";
+  public static final String s5392 = "5392";
+  public static final String s5393 = "5393";
+  public static final String s5394 = "5394";
+  public static final String s5395 = "5395";
+  public static final String s5396 = "5396";
+  public static final String s5397 = "5397";
+  public static final String s5398 = "5398";
+  public static final String s5399 = "5399";
+  public static final String s5400 = "5400";
+  public static final String s5401 = "5401";
+  public static final String s5402 = "5402";
+  public static final String s5403 = "5403";
+  public static final String s5404 = "5404";
+  public static final String s5405 = "5405";
+  public static final String s5406 = "5406";
+  public static final String s5407 = "5407";
+  public static final String s5408 = "5408";
+  public static final String s5409 = "5409";
+  public static final String s5410 = "5410";
+  public static final String s5411 = "5411";
+  public static final String s5412 = "5412";
+  public static final String s5413 = "5413";
+  public static final String s5414 = "5414";
+  public static final String s5415 = "5415";
+  public static final String s5416 = "5416";
+  public static final String s5417 = "5417";
+  public static final String s5418 = "5418";
+  public static final String s5419 = "5419";
+  public static final String s5420 = "5420";
+  public static final String s5421 = "5421";
+  public static final String s5422 = "5422";
+  public static final String s5423 = "5423";
+  public static final String s5424 = "5424";
+  public static final String s5425 = "5425";
+  public static final String s5426 = "5426";
+  public static final String s5427 = "5427";
+  public static final String s5428 = "5428";
+  public static final String s5429 = "5429";
+  public static final String s5430 = "5430";
+  public static final String s5431 = "5431";
+  public static final String s5432 = "5432";
+  public static final String s5433 = "5433";
+  public static final String s5434 = "5434";
+  public static final String s5435 = "5435";
+  public static final String s5436 = "5436";
+  public static final String s5437 = "5437";
+  public static final String s5438 = "5438";
+  public static final String s5439 = "5439";
+  public static final String s5440 = "5440";
+  public static final String s5441 = "5441";
+  public static final String s5442 = "5442";
+  public static final String s5443 = "5443";
+  public static final String s5444 = "5444";
+  public static final String s5445 = "5445";
+  public static final String s5446 = "5446";
+  public static final String s5447 = "5447";
+  public static final String s5448 = "5448";
+  public static final String s5449 = "5449";
+  public static final String s5450 = "5450";
+  public static final String s5451 = "5451";
+  public static final String s5452 = "5452";
+  public static final String s5453 = "5453";
+  public static final String s5454 = "5454";
+  public static final String s5455 = "5455";
+  public static final String s5456 = "5456";
+  public static final String s5457 = "5457";
+  public static final String s5458 = "5458";
+  public static final String s5459 = "5459";
+  public static final String s5460 = "5460";
+  public static final String s5461 = "5461";
+  public static final String s5462 = "5462";
+  public static final String s5463 = "5463";
+  public static final String s5464 = "5464";
+  public static final String s5465 = "5465";
+  public static final String s5466 = "5466";
+  public static final String s5467 = "5467";
+  public static final String s5468 = "5468";
+  public static final String s5469 = "5469";
+  public static final String s5470 = "5470";
+  public static final String s5471 = "5471";
+  public static final String s5472 = "5472";
+  public static final String s5473 = "5473";
+  public static final String s5474 = "5474";
+  public static final String s5475 = "5475";
+  public static final String s5476 = "5476";
+  public static final String s5477 = "5477";
+  public static final String s5478 = "5478";
+  public static final String s5479 = "5479";
+  public static final String s5480 = "5480";
+  public static final String s5481 = "5481";
+  public static final String s5482 = "5482";
+  public static final String s5483 = "5483";
+  public static final String s5484 = "5484";
+  public static final String s5485 = "5485";
+  public static final String s5486 = "5486";
+  public static final String s5487 = "5487";
+  public static final String s5488 = "5488";
+  public static final String s5489 = "5489";
+  public static final String s5490 = "5490";
+  public static final String s5491 = "5491";
+  public static final String s5492 = "5492";
+  public static final String s5493 = "5493";
+  public static final String s5494 = "5494";
+  public static final String s5495 = "5495";
+  public static final String s5496 = "5496";
+  public static final String s5497 = "5497";
+  public static final String s5498 = "5498";
+  public static final String s5499 = "5499";
+  public static final String s5500 = "5500";
+  public static final String s5501 = "5501";
+  public static final String s5502 = "5502";
+  public static final String s5503 = "5503";
+  public static final String s5504 = "5504";
+  public static final String s5505 = "5505";
+  public static final String s5506 = "5506";
+  public static final String s5507 = "5507";
+  public static final String s5508 = "5508";
+  public static final String s5509 = "5509";
+  public static final String s5510 = "5510";
+  public static final String s5511 = "5511";
+  public static final String s5512 = "5512";
+  public static final String s5513 = "5513";
+  public static final String s5514 = "5514";
+  public static final String s5515 = "5515";
+  public static final String s5516 = "5516";
+  public static final String s5517 = "5517";
+  public static final String s5518 = "5518";
+  public static final String s5519 = "5519";
+  public static final String s5520 = "5520";
+  public static final String s5521 = "5521";
+  public static final String s5522 = "5522";
+  public static final String s5523 = "5523";
+  public static final String s5524 = "5524";
+  public static final String s5525 = "5525";
+  public static final String s5526 = "5526";
+  public static final String s5527 = "5527";
+  public static final String s5528 = "5528";
+  public static final String s5529 = "5529";
+  public static final String s5530 = "5530";
+  public static final String s5531 = "5531";
+  public static final String s5532 = "5532";
+  public static final String s5533 = "5533";
+  public static final String s5534 = "5534";
+  public static final String s5535 = "5535";
+  public static final String s5536 = "5536";
+  public static final String s5537 = "5537";
+  public static final String s5538 = "5538";
+  public static final String s5539 = "5539";
+  public static final String s5540 = "5540";
+  public static final String s5541 = "5541";
+  public static final String s5542 = "5542";
+  public static final String s5543 = "5543";
+  public static final String s5544 = "5544";
+  public static final String s5545 = "5545";
+  public static final String s5546 = "5546";
+  public static final String s5547 = "5547";
+  public static final String s5548 = "5548";
+  public static final String s5549 = "5549";
+  public static final String s5550 = "5550";
+  public static final String s5551 = "5551";
+  public static final String s5552 = "5552";
+  public static final String s5553 = "5553";
+  public static final String s5554 = "5554";
+  public static final String s5555 = "5555";
+  public static final String s5556 = "5556";
+  public static final String s5557 = "5557";
+  public static final String s5558 = "5558";
+  public static final String s5559 = "5559";
+  public static final String s5560 = "5560";
+  public static final String s5561 = "5561";
+  public static final String s5562 = "5562";
+  public static final String s5563 = "5563";
+  public static final String s5564 = "5564";
+  public static final String s5565 = "5565";
+  public static final String s5566 = "5566";
+  public static final String s5567 = "5567";
+  public static final String s5568 = "5568";
+  public static final String s5569 = "5569";
+  public static final String s5570 = "5570";
+  public static final String s5571 = "5571";
+  public static final String s5572 = "5572";
+  public static final String s5573 = "5573";
+  public static final String s5574 = "5574";
+  public static final String s5575 = "5575";
+  public static final String s5576 = "5576";
+  public static final String s5577 = "5577";
+  public static final String s5578 = "5578";
+  public static final String s5579 = "5579";
+  public static final String s5580 = "5580";
+  public static final String s5581 = "5581";
+  public static final String s5582 = "5582";
+  public static final String s5583 = "5583";
+  public static final String s5584 = "5584";
+  public static final String s5585 = "5585";
+  public static final String s5586 = "5586";
+  public static final String s5587 = "5587";
+  public static final String s5588 = "5588";
+  public static final String s5589 = "5589";
+  public static final String s5590 = "5590";
+  public static final String s5591 = "5591";
+  public static final String s5592 = "5592";
+  public static final String s5593 = "5593";
+  public static final String s5594 = "5594";
+  public static final String s5595 = "5595";
+  public static final String s5596 = "5596";
+  public static final String s5597 = "5597";
+  public static final String s5598 = "5598";
+  public static final String s5599 = "5599";
+  public static final String s5600 = "5600";
+  public static final String s5601 = "5601";
+  public static final String s5602 = "5602";
+  public static final String s5603 = "5603";
+  public static final String s5604 = "5604";
+  public static final String s5605 = "5605";
+  public static final String s5606 = "5606";
+  public static final String s5607 = "5607";
+  public static final String s5608 = "5608";
+  public static final String s5609 = "5609";
+  public static final String s5610 = "5610";
+  public static final String s5611 = "5611";
+  public static final String s5612 = "5612";
+  public static final String s5613 = "5613";
+  public static final String s5614 = "5614";
+  public static final String s5615 = "5615";
+  public static final String s5616 = "5616";
+  public static final String s5617 = "5617";
+  public static final String s5618 = "5618";
+  public static final String s5619 = "5619";
+  public static final String s5620 = "5620";
+  public static final String s5621 = "5621";
+  public static final String s5622 = "5622";
+  public static final String s5623 = "5623";
+  public static final String s5624 = "5624";
+  public static final String s5625 = "5625";
+  public static final String s5626 = "5626";
+  public static final String s5627 = "5627";
+  public static final String s5628 = "5628";
+  public static final String s5629 = "5629";
+  public static final String s5630 = "5630";
+  public static final String s5631 = "5631";
+  public static final String s5632 = "5632";
+  public static final String s5633 = "5633";
+  public static final String s5634 = "5634";
+  public static final String s5635 = "5635";
+  public static final String s5636 = "5636";
+  public static final String s5637 = "5637";
+  public static final String s5638 = "5638";
+  public static final String s5639 = "5639";
+  public static final String s5640 = "5640";
+  public static final String s5641 = "5641";
+  public static final String s5642 = "5642";
+  public static final String s5643 = "5643";
+  public static final String s5644 = "5644";
+  public static final String s5645 = "5645";
+  public static final String s5646 = "5646";
+  public static final String s5647 = "5647";
+  public static final String s5648 = "5648";
+  public static final String s5649 = "5649";
+  public static final String s5650 = "5650";
+  public static final String s5651 = "5651";
+  public static final String s5652 = "5652";
+  public static final String s5653 = "5653";
+  public static final String s5654 = "5654";
+  public static final String s5655 = "5655";
+  public static final String s5656 = "5656";
+  public static final String s5657 = "5657";
+  public static final String s5658 = "5658";
+  public static final String s5659 = "5659";
+  public static final String s5660 = "5660";
+  public static final String s5661 = "5661";
+  public static final String s5662 = "5662";
+  public static final String s5663 = "5663";
+  public static final String s5664 = "5664";
+  public static final String s5665 = "5665";
+  public static final String s5666 = "5666";
+  public static final String s5667 = "5667";
+  public static final String s5668 = "5668";
+  public static final String s5669 = "5669";
+  public static final String s5670 = "5670";
+  public static final String s5671 = "5671";
+  public static final String s5672 = "5672";
+  public static final String s5673 = "5673";
+  public static final String s5674 = "5674";
+  public static final String s5675 = "5675";
+  public static final String s5676 = "5676";
+  public static final String s5677 = "5677";
+  public static final String s5678 = "5678";
+  public static final String s5679 = "5679";
+  public static final String s5680 = "5680";
+  public static final String s5681 = "5681";
+  public static final String s5682 = "5682";
+  public static final String s5683 = "5683";
+  public static final String s5684 = "5684";
+  public static final String s5685 = "5685";
+  public static final String s5686 = "5686";
+  public static final String s5687 = "5687";
+  public static final String s5688 = "5688";
+  public static final String s5689 = "5689";
+  public static final String s5690 = "5690";
+  public static final String s5691 = "5691";
+  public static final String s5692 = "5692";
+  public static final String s5693 = "5693";
+  public static final String s5694 = "5694";
+  public static final String s5695 = "5695";
+  public static final String s5696 = "5696";
+  public static final String s5697 = "5697";
+  public static final String s5698 = "5698";
+  public static final String s5699 = "5699";
+  public static final String s5700 = "5700";
+  public static final String s5701 = "5701";
+  public static final String s5702 = "5702";
+  public static final String s5703 = "5703";
+  public static final String s5704 = "5704";
+  public static final String s5705 = "5705";
+  public static final String s5706 = "5706";
+  public static final String s5707 = "5707";
+  public static final String s5708 = "5708";
+  public static final String s5709 = "5709";
+  public static final String s5710 = "5710";
+  public static final String s5711 = "5711";
+  public static final String s5712 = "5712";
+  public static final String s5713 = "5713";
+  public static final String s5714 = "5714";
+  public static final String s5715 = "5715";
+  public static final String s5716 = "5716";
+  public static final String s5717 = "5717";
+  public static final String s5718 = "5718";
+  public static final String s5719 = "5719";
+  public static final String s5720 = "5720";
+  public static final String s5721 = "5721";
+  public static final String s5722 = "5722";
+  public static final String s5723 = "5723";
+  public static final String s5724 = "5724";
+  public static final String s5725 = "5725";
+  public static final String s5726 = "5726";
+  public static final String s5727 = "5727";
+  public static final String s5728 = "5728";
+  public static final String s5729 = "5729";
+  public static final String s5730 = "5730";
+  public static final String s5731 = "5731";
+  public static final String s5732 = "5732";
+  public static final String s5733 = "5733";
+  public static final String s5734 = "5734";
+  public static final String s5735 = "5735";
+  public static final String s5736 = "5736";
+  public static final String s5737 = "5737";
+  public static final String s5738 = "5738";
+  public static final String s5739 = "5739";
+  public static final String s5740 = "5740";
+  public static final String s5741 = "5741";
+  public static final String s5742 = "5742";
+  public static final String s5743 = "5743";
+  public static final String s5744 = "5744";
+  public static final String s5745 = "5745";
+  public static final String s5746 = "5746";
+  public static final String s5747 = "5747";
+  public static final String s5748 = "5748";
+  public static final String s5749 = "5749";
+  public static final String s5750 = "5750";
+  public static final String s5751 = "5751";
+  public static final String s5752 = "5752";
+  public static final String s5753 = "5753";
+  public static final String s5754 = "5754";
+  public static final String s5755 = "5755";
+  public static final String s5756 = "5756";
+  public static final String s5757 = "5757";
+  public static final String s5758 = "5758";
+  public static final String s5759 = "5759";
+  public static final String s5760 = "5760";
+  public static final String s5761 = "5761";
+  public static final String s5762 = "5762";
+  public static final String s5763 = "5763";
+  public static final String s5764 = "5764";
+  public static final String s5765 = "5765";
+  public static final String s5766 = "5766";
+  public static final String s5767 = "5767";
+  public static final String s5768 = "5768";
+  public static final String s5769 = "5769";
+  public static final String s5770 = "5770";
+  public static final String s5771 = "5771";
+  public static final String s5772 = "5772";
+  public static final String s5773 = "5773";
+  public static final String s5774 = "5774";
+  public static final String s5775 = "5775";
+  public static final String s5776 = "5776";
+  public static final String s5777 = "5777";
+  public static final String s5778 = "5778";
+  public static final String s5779 = "5779";
+  public static final String s5780 = "5780";
+  public static final String s5781 = "5781";
+  public static final String s5782 = "5782";
+  public static final String s5783 = "5783";
+  public static final String s5784 = "5784";
+  public static final String s5785 = "5785";
+  public static final String s5786 = "5786";
+  public static final String s5787 = "5787";
+  public static final String s5788 = "5788";
+  public static final String s5789 = "5789";
+  public static final String s5790 = "5790";
+  public static final String s5791 = "5791";
+  public static final String s5792 = "5792";
+  public static final String s5793 = "5793";
+  public static final String s5794 = "5794";
+  public static final String s5795 = "5795";
+  public static final String s5796 = "5796";
+  public static final String s5797 = "5797";
+  public static final String s5798 = "5798";
+  public static final String s5799 = "5799";
+  public static final String s5800 = "5800";
+  public static final String s5801 = "5801";
+  public static final String s5802 = "5802";
+  public static final String s5803 = "5803";
+  public static final String s5804 = "5804";
+  public static final String s5805 = "5805";
+  public static final String s5806 = "5806";
+  public static final String s5807 = "5807";
+  public static final String s5808 = "5808";
+  public static final String s5809 = "5809";
+  public static final String s5810 = "5810";
+  public static final String s5811 = "5811";
+  public static final String s5812 = "5812";
+  public static final String s5813 = "5813";
+  public static final String s5814 = "5814";
+  public static final String s5815 = "5815";
+  public static final String s5816 = "5816";
+  public static final String s5817 = "5817";
+  public static final String s5818 = "5818";
+  public static final String s5819 = "5819";
+  public static final String s5820 = "5820";
+  public static final String s5821 = "5821";
+  public static final String s5822 = "5822";
+  public static final String s5823 = "5823";
+  public static final String s5824 = "5824";
+  public static final String s5825 = "5825";
+  public static final String s5826 = "5826";
+  public static final String s5827 = "5827";
+  public static final String s5828 = "5828";
+  public static final String s5829 = "5829";
+  public static final String s5830 = "5830";
+  public static final String s5831 = "5831";
+  public static final String s5832 = "5832";
+  public static final String s5833 = "5833";
+  public static final String s5834 = "5834";
+  public static final String s5835 = "5835";
+  public static final String s5836 = "5836";
+  public static final String s5837 = "5837";
+  public static final String s5838 = "5838";
+  public static final String s5839 = "5839";
+  public static final String s5840 = "5840";
+  public static final String s5841 = "5841";
+  public static final String s5842 = "5842";
+  public static final String s5843 = "5843";
+  public static final String s5844 = "5844";
+  public static final String s5845 = "5845";
+  public static final String s5846 = "5846";
+  public static final String s5847 = "5847";
+  public static final String s5848 = "5848";
+  public static final String s5849 = "5849";
+  public static final String s5850 = "5850";
+  public static final String s5851 = "5851";
+  public static final String s5852 = "5852";
+  public static final String s5853 = "5853";
+  public static final String s5854 = "5854";
+  public static final String s5855 = "5855";
+  public static final String s5856 = "5856";
+  public static final String s5857 = "5857";
+  public static final String s5858 = "5858";
+  public static final String s5859 = "5859";
+  public static final String s5860 = "5860";
+  public static final String s5861 = "5861";
+  public static final String s5862 = "5862";
+  public static final String s5863 = "5863";
+  public static final String s5864 = "5864";
+  public static final String s5865 = "5865";
+  public static final String s5866 = "5866";
+  public static final String s5867 = "5867";
+  public static final String s5868 = "5868";
+  public static final String s5869 = "5869";
+  public static final String s5870 = "5870";
+  public static final String s5871 = "5871";
+  public static final String s5872 = "5872";
+  public static final String s5873 = "5873";
+  public static final String s5874 = "5874";
+  public static final String s5875 = "5875";
+  public static final String s5876 = "5876";
+  public static final String s5877 = "5877";
+  public static final String s5878 = "5878";
+  public static final String s5879 = "5879";
+  public static final String s5880 = "5880";
+  public static final String s5881 = "5881";
+  public static final String s5882 = "5882";
+  public static final String s5883 = "5883";
+  public static final String s5884 = "5884";
+  public static final String s5885 = "5885";
+  public static final String s5886 = "5886";
+  public static final String s5887 = "5887";
+  public static final String s5888 = "5888";
+  public static final String s5889 = "5889";
+  public static final String s5890 = "5890";
+  public static final String s5891 = "5891";
+  public static final String s5892 = "5892";
+  public static final String s5893 = "5893";
+  public static final String s5894 = "5894";
+  public static final String s5895 = "5895";
+  public static final String s5896 = "5896";
+  public static final String s5897 = "5897";
+  public static final String s5898 = "5898";
+  public static final String s5899 = "5899";
+  public static final String s5900 = "5900";
+  public static final String s5901 = "5901";
+  public static final String s5902 = "5902";
+  public static final String s5903 = "5903";
+  public static final String s5904 = "5904";
+  public static final String s5905 = "5905";
+  public static final String s5906 = "5906";
+  public static final String s5907 = "5907";
+  public static final String s5908 = "5908";
+  public static final String s5909 = "5909";
+  public static final String s5910 = "5910";
+  public static final String s5911 = "5911";
+  public static final String s5912 = "5912";
+  public static final String s5913 = "5913";
+  public static final String s5914 = "5914";
+  public static final String s5915 = "5915";
+  public static final String s5916 = "5916";
+  public static final String s5917 = "5917";
+  public static final String s5918 = "5918";
+  public static final String s5919 = "5919";
+  public static final String s5920 = "5920";
+  public static final String s5921 = "5921";
+  public static final String s5922 = "5922";
+  public static final String s5923 = "5923";
+  public static final String s5924 = "5924";
+  public static final String s5925 = "5925";
+  public static final String s5926 = "5926";
+  public static final String s5927 = "5927";
+  public static final String s5928 = "5928";
+  public static final String s5929 = "5929";
+  public static final String s5930 = "5930";
+  public static final String s5931 = "5931";
+  public static final String s5932 = "5932";
+  public static final String s5933 = "5933";
+  public static final String s5934 = "5934";
+  public static final String s5935 = "5935";
+  public static final String s5936 = "5936";
+  public static final String s5937 = "5937";
+  public static final String s5938 = "5938";
+  public static final String s5939 = "5939";
+  public static final String s5940 = "5940";
+  public static final String s5941 = "5941";
+  public static final String s5942 = "5942";
+  public static final String s5943 = "5943";
+  public static final String s5944 = "5944";
+  public static final String s5945 = "5945";
+  public static final String s5946 = "5946";
+  public static final String s5947 = "5947";
+  public static final String s5948 = "5948";
+  public static final String s5949 = "5949";
+  public static final String s5950 = "5950";
+  public static final String s5951 = "5951";
+  public static final String s5952 = "5952";
+  public static final String s5953 = "5953";
+  public static final String s5954 = "5954";
+  public static final String s5955 = "5955";
+  public static final String s5956 = "5956";
+  public static final String s5957 = "5957";
+  public static final String s5958 = "5958";
+  public static final String s5959 = "5959";
+  public static final String s5960 = "5960";
+  public static final String s5961 = "5961";
+  public static final String s5962 = "5962";
+  public static final String s5963 = "5963";
+  public static final String s5964 = "5964";
+  public static final String s5965 = "5965";
+  public static final String s5966 = "5966";
+  public static final String s5967 = "5967";
+  public static final String s5968 = "5968";
+  public static final String s5969 = "5969";
+  public static final String s5970 = "5970";
+  public static final String s5971 = "5971";
+  public static final String s5972 = "5972";
+  public static final String s5973 = "5973";
+  public static final String s5974 = "5974";
+  public static final String s5975 = "5975";
+  public static final String s5976 = "5976";
+  public static final String s5977 = "5977";
+  public static final String s5978 = "5978";
+  public static final String s5979 = "5979";
+  public static final String s5980 = "5980";
+  public static final String s5981 = "5981";
+  public static final String s5982 = "5982";
+  public static final String s5983 = "5983";
+  public static final String s5984 = "5984";
+  public static final String s5985 = "5985";
+  public static final String s5986 = "5986";
+  public static final String s5987 = "5987";
+  public static final String s5988 = "5988";
+  public static final String s5989 = "5989";
+  public static final String s5990 = "5990";
+  public static final String s5991 = "5991";
+  public static final String s5992 = "5992";
+  public static final String s5993 = "5993";
+  public static final String s5994 = "5994";
+  public static final String s5995 = "5995";
+  public static final String s5996 = "5996";
+  public static final String s5997 = "5997";
+  public static final String s5998 = "5998";
+  public static final String s5999 = "5999";
+  public static final String s6000 = "6000";
+  public static final String s6001 = "6001";
+  public static final String s6002 = "6002";
+  public static final String s6003 = "6003";
+  public static final String s6004 = "6004";
+  public static final String s6005 = "6005";
+  public static final String s6006 = "6006";
+  public static final String s6007 = "6007";
+  public static final String s6008 = "6008";
+  public static final String s6009 = "6009";
+  public static final String s6010 = "6010";
+  public static final String s6011 = "6011";
+  public static final String s6012 = "6012";
+  public static final String s6013 = "6013";
+  public static final String s6014 = "6014";
+  public static final String s6015 = "6015";
+  public static final String s6016 = "6016";
+  public static final String s6017 = "6017";
+  public static final String s6018 = "6018";
+  public static final String s6019 = "6019";
+  public static final String s6020 = "6020";
+  public static final String s6021 = "6021";
+  public static final String s6022 = "6022";
+  public static final String s6023 = "6023";
+  public static final String s6024 = "6024";
+  public static final String s6025 = "6025";
+  public static final String s6026 = "6026";
+  public static final String s6027 = "6027";
+  public static final String s6028 = "6028";
+  public static final String s6029 = "6029";
+  public static final String s6030 = "6030";
+  public static final String s6031 = "6031";
+  public static final String s6032 = "6032";
+  public static final String s6033 = "6033";
+  public static final String s6034 = "6034";
+  public static final String s6035 = "6035";
+  public static final String s6036 = "6036";
+  public static final String s6037 = "6037";
+  public static final String s6038 = "6038";
+  public static final String s6039 = "6039";
+  public static final String s6040 = "6040";
+  public static final String s6041 = "6041";
+  public static final String s6042 = "6042";
+  public static final String s6043 = "6043";
+  public static final String s6044 = "6044";
+  public static final String s6045 = "6045";
+  public static final String s6046 = "6046";
+  public static final String s6047 = "6047";
+  public static final String s6048 = "6048";
+  public static final String s6049 = "6049";
+  public static final String s6050 = "6050";
+  public static final String s6051 = "6051";
+  public static final String s6052 = "6052";
+  public static final String s6053 = "6053";
+  public static final String s6054 = "6054";
+  public static final String s6055 = "6055";
+  public static final String s6056 = "6056";
+  public static final String s6057 = "6057";
+  public static final String s6058 = "6058";
+  public static final String s6059 = "6059";
+  public static final String s6060 = "6060";
+  public static final String s6061 = "6061";
+  public static final String s6062 = "6062";
+  public static final String s6063 = "6063";
+  public static final String s6064 = "6064";
+  public static final String s6065 = "6065";
+  public static final String s6066 = "6066";
+  public static final String s6067 = "6067";
+  public static final String s6068 = "6068";
+  public static final String s6069 = "6069";
+  public static final String s6070 = "6070";
+  public static final String s6071 = "6071";
+  public static final String s6072 = "6072";
+  public static final String s6073 = "6073";
+  public static final String s6074 = "6074";
+  public static final String s6075 = "6075";
+  public static final String s6076 = "6076";
+  public static final String s6077 = "6077";
+  public static final String s6078 = "6078";
+  public static final String s6079 = "6079";
+  public static final String s6080 = "6080";
+  public static final String s6081 = "6081";
+  public static final String s6082 = "6082";
+  public static final String s6083 = "6083";
+  public static final String s6084 = "6084";
+  public static final String s6085 = "6085";
+  public static final String s6086 = "6086";
+  public static final String s6087 = "6087";
+  public static final String s6088 = "6088";
+  public static final String s6089 = "6089";
+  public static final String s6090 = "6090";
+  public static final String s6091 = "6091";
+  public static final String s6092 = "6092";
+  public static final String s6093 = "6093";
+  public static final String s6094 = "6094";
+  public static final String s6095 = "6095";
+  public static final String s6096 = "6096";
+  public static final String s6097 = "6097";
+  public static final String s6098 = "6098";
+  public static final String s6099 = "6099";
+  public static final String s6100 = "6100";
+  public static final String s6101 = "6101";
+  public static final String s6102 = "6102";
+  public static final String s6103 = "6103";
+  public static final String s6104 = "6104";
+  public static final String s6105 = "6105";
+  public static final String s6106 = "6106";
+  public static final String s6107 = "6107";
+  public static final String s6108 = "6108";
+  public static final String s6109 = "6109";
+  public static final String s6110 = "6110";
+  public static final String s6111 = "6111";
+  public static final String s6112 = "6112";
+  public static final String s6113 = "6113";
+  public static final String s6114 = "6114";
+  public static final String s6115 = "6115";
+  public static final String s6116 = "6116";
+  public static final String s6117 = "6117";
+  public static final String s6118 = "6118";
+  public static final String s6119 = "6119";
+  public static final String s6120 = "6120";
+  public static final String s6121 = "6121";
+  public static final String s6122 = "6122";
+  public static final String s6123 = "6123";
+  public static final String s6124 = "6124";
+  public static final String s6125 = "6125";
+  public static final String s6126 = "6126";
+  public static final String s6127 = "6127";
+  public static final String s6128 = "6128";
+  public static final String s6129 = "6129";
+  public static final String s6130 = "6130";
+  public static final String s6131 = "6131";
+  public static final String s6132 = "6132";
+  public static final String s6133 = "6133";
+  public static final String s6134 = "6134";
+  public static final String s6135 = "6135";
+  public static final String s6136 = "6136";
+  public static final String s6137 = "6137";
+  public static final String s6138 = "6138";
+  public static final String s6139 = "6139";
+  public static final String s6140 = "6140";
+  public static final String s6141 = "6141";
+  public static final String s6142 = "6142";
+  public static final String s6143 = "6143";
+  public static final String s6144 = "6144";
+  public static final String s6145 = "6145";
+  public static final String s6146 = "6146";
+  public static final String s6147 = "6147";
+  public static final String s6148 = "6148";
+  public static final String s6149 = "6149";
+  public static final String s6150 = "6150";
+  public static final String s6151 = "6151";
+  public static final String s6152 = "6152";
+  public static final String s6153 = "6153";
+  public static final String s6154 = "6154";
+  public static final String s6155 = "6155";
+  public static final String s6156 = "6156";
+  public static final String s6157 = "6157";
+  public static final String s6158 = "6158";
+  public static final String s6159 = "6159";
+  public static final String s6160 = "6160";
+  public static final String s6161 = "6161";
+  public static final String s6162 = "6162";
+  public static final String s6163 = "6163";
+  public static final String s6164 = "6164";
+  public static final String s6165 = "6165";
+  public static final String s6166 = "6166";
+  public static final String s6167 = "6167";
+  public static final String s6168 = "6168";
+  public static final String s6169 = "6169";
+  public static final String s6170 = "6170";
+  public static final String s6171 = "6171";
+  public static final String s6172 = "6172";
+  public static final String s6173 = "6173";
+  public static final String s6174 = "6174";
+  public static final String s6175 = "6175";
+  public static final String s6176 = "6176";
+  public static final String s6177 = "6177";
+  public static final String s6178 = "6178";
+  public static final String s6179 = "6179";
+  public static final String s6180 = "6180";
+  public static final String s6181 = "6181";
+  public static final String s6182 = "6182";
+  public static final String s6183 = "6183";
+  public static final String s6184 = "6184";
+  public static final String s6185 = "6185";
+  public static final String s6186 = "6186";
+  public static final String s6187 = "6187";
+  public static final String s6188 = "6188";
+  public static final String s6189 = "6189";
+  public static final String s6190 = "6190";
+  public static final String s6191 = "6191";
+  public static final String s6192 = "6192";
+  public static final String s6193 = "6193";
+  public static final String s6194 = "6194";
+  public static final String s6195 = "6195";
+  public static final String s6196 = "6196";
+  public static final String s6197 = "6197";
+  public static final String s6198 = "6198";
+  public static final String s6199 = "6199";
+  public static final String s6200 = "6200";
+  public static final String s6201 = "6201";
+  public static final String s6202 = "6202";
+  public static final String s6203 = "6203";
+  public static final String s6204 = "6204";
+  public static final String s6205 = "6205";
+  public static final String s6206 = "6206";
+  public static final String s6207 = "6207";
+  public static final String s6208 = "6208";
+  public static final String s6209 = "6209";
+  public static final String s6210 = "6210";
+  public static final String s6211 = "6211";
+  public static final String s6212 = "6212";
+  public static final String s6213 = "6213";
+  public static final String s6214 = "6214";
+  public static final String s6215 = "6215";
+  public static final String s6216 = "6216";
+  public static final String s6217 = "6217";
+  public static final String s6218 = "6218";
+  public static final String s6219 = "6219";
+  public static final String s6220 = "6220";
+  public static final String s6221 = "6221";
+  public static final String s6222 = "6222";
+  public static final String s6223 = "6223";
+  public static final String s6224 = "6224";
+  public static final String s6225 = "6225";
+  public static final String s6226 = "6226";
+  public static final String s6227 = "6227";
+  public static final String s6228 = "6228";
+  public static final String s6229 = "6229";
+  public static final String s6230 = "6230";
+  public static final String s6231 = "6231";
+  public static final String s6232 = "6232";
+  public static final String s6233 = "6233";
+  public static final String s6234 = "6234";
+  public static final String s6235 = "6235";
+  public static final String s6236 = "6236";
+  public static final String s6237 = "6237";
+  public static final String s6238 = "6238";
+  public static final String s6239 = "6239";
+  public static final String s6240 = "6240";
+  public static final String s6241 = "6241";
+  public static final String s6242 = "6242";
+  public static final String s6243 = "6243";
+  public static final String s6244 = "6244";
+  public static final String s6245 = "6245";
+  public static final String s6246 = "6246";
+  public static final String s6247 = "6247";
+  public static final String s6248 = "6248";
+  public static final String s6249 = "6249";
+  public static final String s6250 = "6250";
+  public static final String s6251 = "6251";
+  public static final String s6252 = "6252";
+  public static final String s6253 = "6253";
+  public static final String s6254 = "6254";
+  public static final String s6255 = "6255";
+  public static final String s6256 = "6256";
+  public static final String s6257 = "6257";
+  public static final String s6258 = "6258";
+  public static final String s6259 = "6259";
+  public static final String s6260 = "6260";
+  public static final String s6261 = "6261";
+  public static final String s6262 = "6262";
+  public static final String s6263 = "6263";
+  public static final String s6264 = "6264";
+  public static final String s6265 = "6265";
+  public static final String s6266 = "6266";
+  public static final String s6267 = "6267";
+  public static final String s6268 = "6268";
+  public static final String s6269 = "6269";
+  public static final String s6270 = "6270";
+  public static final String s6271 = "6271";
+  public static final String s6272 = "6272";
+  public static final String s6273 = "6273";
+  public static final String s6274 = "6274";
+  public static final String s6275 = "6275";
+  public static final String s6276 = "6276";
+  public static final String s6277 = "6277";
+  public static final String s6278 = "6278";
+  public static final String s6279 = "6279";
+  public static final String s6280 = "6280";
+  public static final String s6281 = "6281";
+  public static final String s6282 = "6282";
+  public static final String s6283 = "6283";
+  public static final String s6284 = "6284";
+  public static final String s6285 = "6285";
+  public static final String s6286 = "6286";
+  public static final String s6287 = "6287";
+  public static final String s6288 = "6288";
+  public static final String s6289 = "6289";
+  public static final String s6290 = "6290";
+  public static final String s6291 = "6291";
+  public static final String s6292 = "6292";
+  public static final String s6293 = "6293";
+  public static final String s6294 = "6294";
+  public static final String s6295 = "6295";
+  public static final String s6296 = "6296";
+  public static final String s6297 = "6297";
+  public static final String s6298 = "6298";
+  public static final String s6299 = "6299";
+  public static final String s6300 = "6300";
+  public static final String s6301 = "6301";
+  public static final String s6302 = "6302";
+  public static final String s6303 = "6303";
+  public static final String s6304 = "6304";
+  public static final String s6305 = "6305";
+  public static final String s6306 = "6306";
+  public static final String s6307 = "6307";
+  public static final String s6308 = "6308";
+  public static final String s6309 = "6309";
+  public static final String s6310 = "6310";
+  public static final String s6311 = "6311";
+  public static final String s6312 = "6312";
+  public static final String s6313 = "6313";
+  public static final String s6314 = "6314";
+  public static final String s6315 = "6315";
+  public static final String s6316 = "6316";
+  public static final String s6317 = "6317";
+  public static final String s6318 = "6318";
+  public static final String s6319 = "6319";
+  public static final String s6320 = "6320";
+  public static final String s6321 = "6321";
+  public static final String s6322 = "6322";
+  public static final String s6323 = "6323";
+  public static final String s6324 = "6324";
+  public static final String s6325 = "6325";
+  public static final String s6326 = "6326";
+  public static final String s6327 = "6327";
+  public static final String s6328 = "6328";
+  public static final String s6329 = "6329";
+  public static final String s6330 = "6330";
+  public static final String s6331 = "6331";
+  public static final String s6332 = "6332";
+  public static final String s6333 = "6333";
+  public static final String s6334 = "6334";
+  public static final String s6335 = "6335";
+  public static final String s6336 = "6336";
+  public static final String s6337 = "6337";
+  public static final String s6338 = "6338";
+  public static final String s6339 = "6339";
+  public static final String s6340 = "6340";
+  public static final String s6341 = "6341";
+  public static final String s6342 = "6342";
+  public static final String s6343 = "6343";
+  public static final String s6344 = "6344";
+  public static final String s6345 = "6345";
+  public static final String s6346 = "6346";
+  public static final String s6347 = "6347";
+  public static final String s6348 = "6348";
+  public static final String s6349 = "6349";
+  public static final String s6350 = "6350";
+  public static final String s6351 = "6351";
+  public static final String s6352 = "6352";
+  public static final String s6353 = "6353";
+  public static final String s6354 = "6354";
+  public static final String s6355 = "6355";
+  public static final String s6356 = "6356";
+  public static final String s6357 = "6357";
+  public static final String s6358 = "6358";
+  public static final String s6359 = "6359";
+  public static final String s6360 = "6360";
+  public static final String s6361 = "6361";
+  public static final String s6362 = "6362";
+  public static final String s6363 = "6363";
+  public static final String s6364 = "6364";
+  public static final String s6365 = "6365";
+  public static final String s6366 = "6366";
+  public static final String s6367 = "6367";
+  public static final String s6368 = "6368";
+  public static final String s6369 = "6369";
+  public static final String s6370 = "6370";
+  public static final String s6371 = "6371";
+  public static final String s6372 = "6372";
+  public static final String s6373 = "6373";
+  public static final String s6374 = "6374";
+  public static final String s6375 = "6375";
+  public static final String s6376 = "6376";
+  public static final String s6377 = "6377";
+  public static final String s6378 = "6378";
+  public static final String s6379 = "6379";
+  public static final String s6380 = "6380";
+  public static final String s6381 = "6381";
+  public static final String s6382 = "6382";
+  public static final String s6383 = "6383";
+  public static final String s6384 = "6384";
+  public static final String s6385 = "6385";
+  public static final String s6386 = "6386";
+  public static final String s6387 = "6387";
+  public static final String s6388 = "6388";
+  public static final String s6389 = "6389";
+  public static final String s6390 = "6390";
+  public static final String s6391 = "6391";
+  public static final String s6392 = "6392";
+  public static final String s6393 = "6393";
+  public static final String s6394 = "6394";
+  public static final String s6395 = "6395";
+  public static final String s6396 = "6396";
+  public static final String s6397 = "6397";
+  public static final String s6398 = "6398";
+  public static final String s6399 = "6399";
+  public static final String s6400 = "6400";
+  public static final String s6401 = "6401";
+  public static final String s6402 = "6402";
+  public static final String s6403 = "6403";
+  public static final String s6404 = "6404";
+  public static final String s6405 = "6405";
+  public static final String s6406 = "6406";
+  public static final String s6407 = "6407";
+  public static final String s6408 = "6408";
+  public static final String s6409 = "6409";
+  public static final String s6410 = "6410";
+  public static final String s6411 = "6411";
+  public static final String s6412 = "6412";
+  public static final String s6413 = "6413";
+  public static final String s6414 = "6414";
+  public static final String s6415 = "6415";
+  public static final String s6416 = "6416";
+  public static final String s6417 = "6417";
+  public static final String s6418 = "6418";
+  public static final String s6419 = "6419";
+  public static final String s6420 = "6420";
+  public static final String s6421 = "6421";
+  public static final String s6422 = "6422";
+  public static final String s6423 = "6423";
+  public static final String s6424 = "6424";
+  public static final String s6425 = "6425";
+  public static final String s6426 = "6426";
+  public static final String s6427 = "6427";
+  public static final String s6428 = "6428";
+  public static final String s6429 = "6429";
+  public static final String s6430 = "6430";
+  public static final String s6431 = "6431";
+  public static final String s6432 = "6432";
+  public static final String s6433 = "6433";
+  public static final String s6434 = "6434";
+  public static final String s6435 = "6435";
+  public static final String s6436 = "6436";
+  public static final String s6437 = "6437";
+  public static final String s6438 = "6438";
+  public static final String s6439 = "6439";
+  public static final String s6440 = "6440";
+  public static final String s6441 = "6441";
+  public static final String s6442 = "6442";
+  public static final String s6443 = "6443";
+  public static final String s6444 = "6444";
+  public static final String s6445 = "6445";
+  public static final String s6446 = "6446";
+  public static final String s6447 = "6447";
+  public static final String s6448 = "6448";
+  public static final String s6449 = "6449";
+  public static final String s6450 = "6450";
+  public static final String s6451 = "6451";
+  public static final String s6452 = "6452";
+  public static final String s6453 = "6453";
+  public static final String s6454 = "6454";
+  public static final String s6455 = "6455";
+  public static final String s6456 = "6456";
+  public static final String s6457 = "6457";
+  public static final String s6458 = "6458";
+  public static final String s6459 = "6459";
+  public static final String s6460 = "6460";
+  public static final String s6461 = "6461";
+  public static final String s6462 = "6462";
+  public static final String s6463 = "6463";
+  public static final String s6464 = "6464";
+  public static final String s6465 = "6465";
+  public static final String s6466 = "6466";
+  public static final String s6467 = "6467";
+  public static final String s6468 = "6468";
+  public static final String s6469 = "6469";
+  public static final String s6470 = "6470";
+  public static final String s6471 = "6471";
+  public static final String s6472 = "6472";
+  public static final String s6473 = "6473";
+  public static final String s6474 = "6474";
+  public static final String s6475 = "6475";
+  public static final String s6476 = "6476";
+  public static final String s6477 = "6477";
+  public static final String s6478 = "6478";
+  public static final String s6479 = "6479";
+  public static final String s6480 = "6480";
+  public static final String s6481 = "6481";
+  public static final String s6482 = "6482";
+  public static final String s6483 = "6483";
+  public static final String s6484 = "6484";
+  public static final String s6485 = "6485";
+  public static final String s6486 = "6486";
+  public static final String s6487 = "6487";
+  public static final String s6488 = "6488";
+  public static final String s6489 = "6489";
+  public static final String s6490 = "6490";
+  public static final String s6491 = "6491";
+  public static final String s6492 = "6492";
+  public static final String s6493 = "6493";
+  public static final String s6494 = "6494";
+  public static final String s6495 = "6495";
+  public static final String s6496 = "6496";
+  public static final String s6497 = "6497";
+  public static final String s6498 = "6498";
+  public static final String s6499 = "6499";
+  public static final String s6500 = "6500";
+  public static final String s6501 = "6501";
+  public static final String s6502 = "6502";
+  public static final String s6503 = "6503";
+  public static final String s6504 = "6504";
+  public static final String s6505 = "6505";
+  public static final String s6506 = "6506";
+  public static final String s6507 = "6507";
+  public static final String s6508 = "6508";
+  public static final String s6509 = "6509";
+  public static final String s6510 = "6510";
+  public static final String s6511 = "6511";
+  public static final String s6512 = "6512";
+  public static final String s6513 = "6513";
+  public static final String s6514 = "6514";
+  public static final String s6515 = "6515";
+  public static final String s6516 = "6516";
+  public static final String s6517 = "6517";
+  public static final String s6518 = "6518";
+  public static final String s6519 = "6519";
+  public static final String s6520 = "6520";
+  public static final String s6521 = "6521";
+  public static final String s6522 = "6522";
+  public static final String s6523 = "6523";
+  public static final String s6524 = "6524";
+  public static final String s6525 = "6525";
+  public static final String s6526 = "6526";
+  public static final String s6527 = "6527";
+  public static final String s6528 = "6528";
+  public static final String s6529 = "6529";
+  public static final String s6530 = "6530";
+  public static final String s6531 = "6531";
+  public static final String s6532 = "6532";
+  public static final String s6533 = "6533";
+  public static final String s6534 = "6534";
+  public static final String s6535 = "6535";
+  public static final String s6536 = "6536";
+  public static final String s6537 = "6537";
+  public static final String s6538 = "6538";
+  public static final String s6539 = "6539";
+  public static final String s6540 = "6540";
+  public static final String s6541 = "6541";
+  public static final String s6542 = "6542";
+  public static final String s6543 = "6543";
+  public static final String s6544 = "6544";
+  public static final String s6545 = "6545";
+  public static final String s6546 = "6546";
+  public static final String s6547 = "6547";
+  public static final String s6548 = "6548";
+  public static final String s6549 = "6549";
+  public static final String s6550 = "6550";
+  public static final String s6551 = "6551";
+  public static final String s6552 = "6552";
+  public static final String s6553 = "6553";
+  public static final String s6554 = "6554";
+  public static final String s6555 = "6555";
+  public static final String s6556 = "6556";
+  public static final String s6557 = "6557";
+  public static final String s6558 = "6558";
+  public static final String s6559 = "6559";
+  public static final String s6560 = "6560";
+  public static final String s6561 = "6561";
+  public static final String s6562 = "6562";
+  public static final String s6563 = "6563";
+  public static final String s6564 = "6564";
+  public static final String s6565 = "6565";
+  public static final String s6566 = "6566";
+  public static final String s6567 = "6567";
+  public static final String s6568 = "6568";
+  public static final String s6569 = "6569";
+  public static final String s6570 = "6570";
+  public static final String s6571 = "6571";
+  public static final String s6572 = "6572";
+  public static final String s6573 = "6573";
+  public static final String s6574 = "6574";
+  public static final String s6575 = "6575";
+  public static final String s6576 = "6576";
+  public static final String s6577 = "6577";
+  public static final String s6578 = "6578";
+  public static final String s6579 = "6579";
+  public static final String s6580 = "6580";
+  public static final String s6581 = "6581";
+  public static final String s6582 = "6582";
+  public static final String s6583 = "6583";
+  public static final String s6584 = "6584";
+  public static final String s6585 = "6585";
+  public static final String s6586 = "6586";
+  public static final String s6587 = "6587";
+  public static final String s6588 = "6588";
+  public static final String s6589 = "6589";
+  public static final String s6590 = "6590";
+  public static final String s6591 = "6591";
+  public static final String s6592 = "6592";
+  public static final String s6593 = "6593";
+  public static final String s6594 = "6594";
+  public static final String s6595 = "6595";
+  public static final String s6596 = "6596";
+  public static final String s6597 = "6597";
+  public static final String s6598 = "6598";
+  public static final String s6599 = "6599";
+  public static final String s6600 = "6600";
+  public static final String s6601 = "6601";
+  public static final String s6602 = "6602";
+  public static final String s6603 = "6603";
+  public static final String s6604 = "6604";
+  public static final String s6605 = "6605";
+  public static final String s6606 = "6606";
+  public static final String s6607 = "6607";
+  public static final String s6608 = "6608";
+  public static final String s6609 = "6609";
+  public static final String s6610 = "6610";
+  public static final String s6611 = "6611";
+  public static final String s6612 = "6612";
+  public static final String s6613 = "6613";
+  public static final String s6614 = "6614";
+  public static final String s6615 = "6615";
+  public static final String s6616 = "6616";
+  public static final String s6617 = "6617";
+  public static final String s6618 = "6618";
+  public static final String s6619 = "6619";
+  public static final String s6620 = "6620";
+  public static final String s6621 = "6621";
+  public static final String s6622 = "6622";
+  public static final String s6623 = "6623";
+  public static final String s6624 = "6624";
+  public static final String s6625 = "6625";
+  public static final String s6626 = "6626";
+  public static final String s6627 = "6627";
+  public static final String s6628 = "6628";
+  public static final String s6629 = "6629";
+  public static final String s6630 = "6630";
+  public static final String s6631 = "6631";
+  public static final String s6632 = "6632";
+  public static final String s6633 = "6633";
+  public static final String s6634 = "6634";
+  public static final String s6635 = "6635";
+  public static final String s6636 = "6636";
+  public static final String s6637 = "6637";
+  public static final String s6638 = "6638";
+  public static final String s6639 = "6639";
+  public static final String s6640 = "6640";
+  public static final String s6641 = "6641";
+  public static final String s6642 = "6642";
+  public static final String s6643 = "6643";
+  public static final String s6644 = "6644";
+  public static final String s6645 = "6645";
+  public static final String s6646 = "6646";
+  public static final String s6647 = "6647";
+  public static final String s6648 = "6648";
+  public static final String s6649 = "6649";
+  public static final String s6650 = "6650";
+  public static final String s6651 = "6651";
+  public static final String s6652 = "6652";
+  public static final String s6653 = "6653";
+  public static final String s6654 = "6654";
+  public static final String s6655 = "6655";
+  public static final String s6656 = "6656";
+  public static final String s6657 = "6657";
+  public static final String s6658 = "6658";
+  public static final String s6659 = "6659";
+  public static final String s6660 = "6660";
+  public static final String s6661 = "6661";
+  public static final String s6662 = "6662";
+  public static final String s6663 = "6663";
+  public static final String s6664 = "6664";
+  public static final String s6665 = "6665";
+  public static final String s6666 = "6666";
+  public static final String s6667 = "6667";
+  public static final String s6668 = "6668";
+  public static final String s6669 = "6669";
+  public static final String s6670 = "6670";
+  public static final String s6671 = "6671";
+  public static final String s6672 = "6672";
+  public static final String s6673 = "6673";
+  public static final String s6674 = "6674";
+  public static final String s6675 = "6675";
+  public static final String s6676 = "6676";
+  public static final String s6677 = "6677";
+  public static final String s6678 = "6678";
+  public static final String s6679 = "6679";
+  public static final String s6680 = "6680";
+  public static final String s6681 = "6681";
+  public static final String s6682 = "6682";
+  public static final String s6683 = "6683";
+  public static final String s6684 = "6684";
+  public static final String s6685 = "6685";
+  public static final String s6686 = "6686";
+  public static final String s6687 = "6687";
+  public static final String s6688 = "6688";
+  public static final String s6689 = "6689";
+  public static final String s6690 = "6690";
+  public static final String s6691 = "6691";
+  public static final String s6692 = "6692";
+  public static final String s6693 = "6693";
+  public static final String s6694 = "6694";
+  public static final String s6695 = "6695";
+  public static final String s6696 = "6696";
+  public static final String s6697 = "6697";
+  public static final String s6698 = "6698";
+  public static final String s6699 = "6699";
+  public static final String s6700 = "6700";
+  public static final String s6701 = "6701";
+  public static final String s6702 = "6702";
+  public static final String s6703 = "6703";
+  public static final String s6704 = "6704";
+  public static final String s6705 = "6705";
+  public static final String s6706 = "6706";
+  public static final String s6707 = "6707";
+  public static final String s6708 = "6708";
+  public static final String s6709 = "6709";
+  public static final String s6710 = "6710";
+  public static final String s6711 = "6711";
+  public static final String s6712 = "6712";
+  public static final String s6713 = "6713";
+  public static final String s6714 = "6714";
+  public static final String s6715 = "6715";
+  public static final String s6716 = "6716";
+  public static final String s6717 = "6717";
+  public static final String s6718 = "6718";
+  public static final String s6719 = "6719";
+  public static final String s6720 = "6720";
+  public static final String s6721 = "6721";
+  public static final String s6722 = "6722";
+  public static final String s6723 = "6723";
+  public static final String s6724 = "6724";
+  public static final String s6725 = "6725";
+  public static final String s6726 = "6726";
+  public static final String s6727 = "6727";
+  public static final String s6728 = "6728";
+  public static final String s6729 = "6729";
+  public static final String s6730 = "6730";
+  public static final String s6731 = "6731";
+  public static final String s6732 = "6732";
+  public static final String s6733 = "6733";
+  public static final String s6734 = "6734";
+  public static final String s6735 = "6735";
+  public static final String s6736 = "6736";
+  public static final String s6737 = "6737";
+  public static final String s6738 = "6738";
+  public static final String s6739 = "6739";
+  public static final String s6740 = "6740";
+  public static final String s6741 = "6741";
+  public static final String s6742 = "6742";
+  public static final String s6743 = "6743";
+  public static final String s6744 = "6744";
+  public static final String s6745 = "6745";
+  public static final String s6746 = "6746";
+  public static final String s6747 = "6747";
+  public static final String s6748 = "6748";
+  public static final String s6749 = "6749";
+  public static final String s6750 = "6750";
+  public static final String s6751 = "6751";
+  public static final String s6752 = "6752";
+  public static final String s6753 = "6753";
+  public static final String s6754 = "6754";
+  public static final String s6755 = "6755";
+  public static final String s6756 = "6756";
+  public static final String s6757 = "6757";
+  public static final String s6758 = "6758";
+  public static final String s6759 = "6759";
+  public static final String s6760 = "6760";
+  public static final String s6761 = "6761";
+  public static final String s6762 = "6762";
+  public static final String s6763 = "6763";
+  public static final String s6764 = "6764";
+  public static final String s6765 = "6765";
+  public static final String s6766 = "6766";
+  public static final String s6767 = "6767";
+  public static final String s6768 = "6768";
+  public static final String s6769 = "6769";
+  public static final String s6770 = "6770";
+  public static final String s6771 = "6771";
+  public static final String s6772 = "6772";
+  public static final String s6773 = "6773";
+  public static final String s6774 = "6774";
+  public static final String s6775 = "6775";
+  public static final String s6776 = "6776";
+  public static final String s6777 = "6777";
+  public static final String s6778 = "6778";
+  public static final String s6779 = "6779";
+  public static final String s6780 = "6780";
+  public static final String s6781 = "6781";
+  public static final String s6782 = "6782";
+  public static final String s6783 = "6783";
+  public static final String s6784 = "6784";
+  public static final String s6785 = "6785";
+  public static final String s6786 = "6786";
+  public static final String s6787 = "6787";
+  public static final String s6788 = "6788";
+  public static final String s6789 = "6789";
+  public static final String s6790 = "6790";
+  public static final String s6791 = "6791";
+  public static final String s6792 = "6792";
+  public static final String s6793 = "6793";
+  public static final String s6794 = "6794";
+  public static final String s6795 = "6795";
+  public static final String s6796 = "6796";
+  public static final String s6797 = "6797";
+  public static final String s6798 = "6798";
+  public static final String s6799 = "6799";
+  public static final String s6800 = "6800";
+  public static final String s6801 = "6801";
+  public static final String s6802 = "6802";
+  public static final String s6803 = "6803";
+  public static final String s6804 = "6804";
+  public static final String s6805 = "6805";
+  public static final String s6806 = "6806";
+  public static final String s6807 = "6807";
+  public static final String s6808 = "6808";
+  public static final String s6809 = "6809";
+  public static final String s6810 = "6810";
+  public static final String s6811 = "6811";
+  public static final String s6812 = "6812";
+  public static final String s6813 = "6813";
+  public static final String s6814 = "6814";
+  public static final String s6815 = "6815";
+  public static final String s6816 = "6816";
+  public static final String s6817 = "6817";
+  public static final String s6818 = "6818";
+  public static final String s6819 = "6819";
+  public static final String s6820 = "6820";
+  public static final String s6821 = "6821";
+  public static final String s6822 = "6822";
+  public static final String s6823 = "6823";
+  public static final String s6824 = "6824";
+  public static final String s6825 = "6825";
+  public static final String s6826 = "6826";
+  public static final String s6827 = "6827";
+  public static final String s6828 = "6828";
+  public static final String s6829 = "6829";
+  public static final String s6830 = "6830";
+  public static final String s6831 = "6831";
+  public static final String s6832 = "6832";
+  public static final String s6833 = "6833";
+  public static final String s6834 = "6834";
+  public static final String s6835 = "6835";
+  public static final String s6836 = "6836";
+  public static final String s6837 = "6837";
+  public static final String s6838 = "6838";
+  public static final String s6839 = "6839";
+  public static final String s6840 = "6840";
+  public static final String s6841 = "6841";
+  public static final String s6842 = "6842";
+  public static final String s6843 = "6843";
+  public static final String s6844 = "6844";
+  public static final String s6845 = "6845";
+  public static final String s6846 = "6846";
+  public static final String s6847 = "6847";
+  public static final String s6848 = "6848";
+  public static final String s6849 = "6849";
+  public static final String s6850 = "6850";
+  public static final String s6851 = "6851";
+  public static final String s6852 = "6852";
+  public static final String s6853 = "6853";
+  public static final String s6854 = "6854";
+  public static final String s6855 = "6855";
+  public static final String s6856 = "6856";
+  public static final String s6857 = "6857";
+  public static final String s6858 = "6858";
+  public static final String s6859 = "6859";
+  public static final String s6860 = "6860";
+  public static final String s6861 = "6861";
+  public static final String s6862 = "6862";
+  public static final String s6863 = "6863";
+  public static final String s6864 = "6864";
+  public static final String s6865 = "6865";
+  public static final String s6866 = "6866";
+  public static final String s6867 = "6867";
+  public static final String s6868 = "6868";
+  public static final String s6869 = "6869";
+  public static final String s6870 = "6870";
+  public static final String s6871 = "6871";
+  public static final String s6872 = "6872";
+  public static final String s6873 = "6873";
+  public static final String s6874 = "6874";
+  public static final String s6875 = "6875";
+  public static final String s6876 = "6876";
+  public static final String s6877 = "6877";
+  public static final String s6878 = "6878";
+  public static final String s6879 = "6879";
+  public static final String s6880 = "6880";
+  public static final String s6881 = "6881";
+  public static final String s6882 = "6882";
+  public static final String s6883 = "6883";
+  public static final String s6884 = "6884";
+  public static final String s6885 = "6885";
+  public static final String s6886 = "6886";
+  public static final String s6887 = "6887";
+  public static final String s6888 = "6888";
+  public static final String s6889 = "6889";
+  public static final String s6890 = "6890";
+  public static final String s6891 = "6891";
+  public static final String s6892 = "6892";
+  public static final String s6893 = "6893";
+  public static final String s6894 = "6894";
+  public static final String s6895 = "6895";
+  public static final String s6896 = "6896";
+  public static final String s6897 = "6897";
+  public static final String s6898 = "6898";
+  public static final String s6899 = "6899";
+  public static final String s6900 = "6900";
+  public static final String s6901 = "6901";
+  public static final String s6902 = "6902";
+  public static final String s6903 = "6903";
+  public static final String s6904 = "6904";
+  public static final String s6905 = "6905";
+  public static final String s6906 = "6906";
+  public static final String s6907 = "6907";
+  public static final String s6908 = "6908";
+  public static final String s6909 = "6909";
+  public static final String s6910 = "6910";
+  public static final String s6911 = "6911";
+  public static final String s6912 = "6912";
+  public static final String s6913 = "6913";
+  public static final String s6914 = "6914";
+  public static final String s6915 = "6915";
+  public static final String s6916 = "6916";
+  public static final String s6917 = "6917";
+  public static final String s6918 = "6918";
+  public static final String s6919 = "6919";
+  public static final String s6920 = "6920";
+  public static final String s6921 = "6921";
+  public static final String s6922 = "6922";
+  public static final String s6923 = "6923";
+  public static final String s6924 = "6924";
+  public static final String s6925 = "6925";
+  public static final String s6926 = "6926";
+  public static final String s6927 = "6927";
+  public static final String s6928 = "6928";
+  public static final String s6929 = "6929";
+  public static final String s6930 = "6930";
+  public static final String s6931 = "6931";
+  public static final String s6932 = "6932";
+  public static final String s6933 = "6933";
+  public static final String s6934 = "6934";
+  public static final String s6935 = "6935";
+  public static final String s6936 = "6936";
+  public static final String s6937 = "6937";
+  public static final String s6938 = "6938";
+  public static final String s6939 = "6939";
+  public static final String s6940 = "6940";
+  public static final String s6941 = "6941";
+  public static final String s6942 = "6942";
+  public static final String s6943 = "6943";
+  public static final String s6944 = "6944";
+  public static final String s6945 = "6945";
+  public static final String s6946 = "6946";
+  public static final String s6947 = "6947";
+  public static final String s6948 = "6948";
+  public static final String s6949 = "6949";
+  public static final String s6950 = "6950";
+  public static final String s6951 = "6951";
+  public static final String s6952 = "6952";
+  public static final String s6953 = "6953";
+  public static final String s6954 = "6954";
+  public static final String s6955 = "6955";
+  public static final String s6956 = "6956";
+  public static final String s6957 = "6957";
+  public static final String s6958 = "6958";
+  public static final String s6959 = "6959";
+  public static final String s6960 = "6960";
+  public static final String s6961 = "6961";
+  public static final String s6962 = "6962";
+  public static final String s6963 = "6963";
+  public static final String s6964 = "6964";
+  public static final String s6965 = "6965";
+  public static final String s6966 = "6966";
+  public static final String s6967 = "6967";
+  public static final String s6968 = "6968";
+  public static final String s6969 = "6969";
+  public static final String s6970 = "6970";
+  public static final String s6971 = "6971";
+  public static final String s6972 = "6972";
+  public static final String s6973 = "6973";
+  public static final String s6974 = "6974";
+  public static final String s6975 = "6975";
+  public static final String s6976 = "6976";
+  public static final String s6977 = "6977";
+  public static final String s6978 = "6978";
+  public static final String s6979 = "6979";
+  public static final String s6980 = "6980";
+  public static final String s6981 = "6981";
+  public static final String s6982 = "6982";
+  public static final String s6983 = "6983";
+  public static final String s6984 = "6984";
+  public static final String s6985 = "6985";
+  public static final String s6986 = "6986";
+  public static final String s6987 = "6987";
+  public static final String s6988 = "6988";
+  public static final String s6989 = "6989";
+  public static final String s6990 = "6990";
+  public static final String s6991 = "6991";
+  public static final String s6992 = "6992";
+  public static final String s6993 = "6993";
+  public static final String s6994 = "6994";
+  public static final String s6995 = "6995";
+  public static final String s6996 = "6996";
+  public static final String s6997 = "6997";
+  public static final String s6998 = "6998";
+  public static final String s6999 = "6999";
+  public static final String s7000 = "7000";
+  public static final String s7001 = "7001";
+  public static final String s7002 = "7002";
+  public static final String s7003 = "7003";
+  public static final String s7004 = "7004";
+  public static final String s7005 = "7005";
+  public static final String s7006 = "7006";
+  public static final String s7007 = "7007";
+  public static final String s7008 = "7008";
+  public static final String s7009 = "7009";
+  public static final String s7010 = "7010";
+  public static final String s7011 = "7011";
+  public static final String s7012 = "7012";
+  public static final String s7013 = "7013";
+  public static final String s7014 = "7014";
+  public static final String s7015 = "7015";
+  public static final String s7016 = "7016";
+  public static final String s7017 = "7017";
+  public static final String s7018 = "7018";
+  public static final String s7019 = "7019";
+  public static final String s7020 = "7020";
+  public static final String s7021 = "7021";
+  public static final String s7022 = "7022";
+  public static final String s7023 = "7023";
+  public static final String s7024 = "7024";
+  public static final String s7025 = "7025";
+  public static final String s7026 = "7026";
+  public static final String s7027 = "7027";
+  public static final String s7028 = "7028";
+  public static final String s7029 = "7029";
+  public static final String s7030 = "7030";
+  public static final String s7031 = "7031";
+  public static final String s7032 = "7032";
+  public static final String s7033 = "7033";
+  public static final String s7034 = "7034";
+  public static final String s7035 = "7035";
+  public static final String s7036 = "7036";
+  public static final String s7037 = "7037";
+  public static final String s7038 = "7038";
+  public static final String s7039 = "7039";
+  public static final String s7040 = "7040";
+  public static final String s7041 = "7041";
+  public static final String s7042 = "7042";
+  public static final String s7043 = "7043";
+  public static final String s7044 = "7044";
+  public static final String s7045 = "7045";
+  public static final String s7046 = "7046";
+  public static final String s7047 = "7047";
+  public static final String s7048 = "7048";
+  public static final String s7049 = "7049";
+  public static final String s7050 = "7050";
+  public static final String s7051 = "7051";
+  public static final String s7052 = "7052";
+  public static final String s7053 = "7053";
+  public static final String s7054 = "7054";
+  public static final String s7055 = "7055";
+  public static final String s7056 = "7056";
+  public static final String s7057 = "7057";
+  public static final String s7058 = "7058";
+  public static final String s7059 = "7059";
+  public static final String s7060 = "7060";
+  public static final String s7061 = "7061";
+  public static final String s7062 = "7062";
+  public static final String s7063 = "7063";
+  public static final String s7064 = "7064";
+  public static final String s7065 = "7065";
+  public static final String s7066 = "7066";
+  public static final String s7067 = "7067";
+  public static final String s7068 = "7068";
+  public static final String s7069 = "7069";
+  public static final String s7070 = "7070";
+  public static final String s7071 = "7071";
+  public static final String s7072 = "7072";
+  public static final String s7073 = "7073";
+  public static final String s7074 = "7074";
+  public static final String s7075 = "7075";
+  public static final String s7076 = "7076";
+  public static final String s7077 = "7077";
+  public static final String s7078 = "7078";
+  public static final String s7079 = "7079";
+  public static final String s7080 = "7080";
+  public static final String s7081 = "7081";
+  public static final String s7082 = "7082";
+  public static final String s7083 = "7083";
+  public static final String s7084 = "7084";
+  public static final String s7085 = "7085";
+  public static final String s7086 = "7086";
+  public static final String s7087 = "7087";
+  public static final String s7088 = "7088";
+  public static final String s7089 = "7089";
+  public static final String s7090 = "7090";
+  public static final String s7091 = "7091";
+  public static final String s7092 = "7092";
+  public static final String s7093 = "7093";
+  public static final String s7094 = "7094";
+  public static final String s7095 = "7095";
+  public static final String s7096 = "7096";
+  public static final String s7097 = "7097";
+  public static final String s7098 = "7098";
+  public static final String s7099 = "7099";
+  public static final String s7100 = "7100";
+  public static final String s7101 = "7101";
+  public static final String s7102 = "7102";
+  public static final String s7103 = "7103";
+  public static final String s7104 = "7104";
+  public static final String s7105 = "7105";
+  public static final String s7106 = "7106";
+  public static final String s7107 = "7107";
+  public static final String s7108 = "7108";
+  public static final String s7109 = "7109";
+  public static final String s7110 = "7110";
+  public static final String s7111 = "7111";
+  public static final String s7112 = "7112";
+  public static final String s7113 = "7113";
+  public static final String s7114 = "7114";
+  public static final String s7115 = "7115";
+  public static final String s7116 = "7116";
+  public static final String s7117 = "7117";
+  public static final String s7118 = "7118";
+  public static final String s7119 = "7119";
+  public static final String s7120 = "7120";
+  public static final String s7121 = "7121";
+  public static final String s7122 = "7122";
+  public static final String s7123 = "7123";
+  public static final String s7124 = "7124";
+  public static final String s7125 = "7125";
+  public static final String s7126 = "7126";
+  public static final String s7127 = "7127";
+  public static final String s7128 = "7128";
+  public static final String s7129 = "7129";
+  public static final String s7130 = "7130";
+  public static final String s7131 = "7131";
+  public static final String s7132 = "7132";
+  public static final String s7133 = "7133";
+  public static final String s7134 = "7134";
+  public static final String s7135 = "7135";
+  public static final String s7136 = "7136";
+  public static final String s7137 = "7137";
+  public static final String s7138 = "7138";
+  public static final String s7139 = "7139";
+  public static final String s7140 = "7140";
+  public static final String s7141 = "7141";
+  public static final String s7142 = "7142";
+  public static final String s7143 = "7143";
+  public static final String s7144 = "7144";
+  public static final String s7145 = "7145";
+  public static final String s7146 = "7146";
+  public static final String s7147 = "7147";
+  public static final String s7148 = "7148";
+  public static final String s7149 = "7149";
+  public static final String s7150 = "7150";
+  public static final String s7151 = "7151";
+  public static final String s7152 = "7152";
+  public static final String s7153 = "7153";
+  public static final String s7154 = "7154";
+  public static final String s7155 = "7155";
+  public static final String s7156 = "7156";
+  public static final String s7157 = "7157";
+  public static final String s7158 = "7158";
+  public static final String s7159 = "7159";
+  public static final String s7160 = "7160";
+  public static final String s7161 = "7161";
+  public static final String s7162 = "7162";
+  public static final String s7163 = "7163";
+  public static final String s7164 = "7164";
+  public static final String s7165 = "7165";
+  public static final String s7166 = "7166";
+  public static final String s7167 = "7167";
+  public static final String s7168 = "7168";
+  public static final String s7169 = "7169";
+  public static final String s7170 = "7170";
+  public static final String s7171 = "7171";
+  public static final String s7172 = "7172";
+  public static final String s7173 = "7173";
+  public static final String s7174 = "7174";
+  public static final String s7175 = "7175";
+  public static final String s7176 = "7176";
+  public static final String s7177 = "7177";
+  public static final String s7178 = "7178";
+  public static final String s7179 = "7179";
+  public static final String s7180 = "7180";
+  public static final String s7181 = "7181";
+  public static final String s7182 = "7182";
+  public static final String s7183 = "7183";
+  public static final String s7184 = "7184";
+  public static final String s7185 = "7185";
+  public static final String s7186 = "7186";
+  public static final String s7187 = "7187";
+  public static final String s7188 = "7188";
+  public static final String s7189 = "7189";
+  public static final String s7190 = "7190";
+  public static final String s7191 = "7191";
+  public static final String s7192 = "7192";
+  public static final String s7193 = "7193";
+  public static final String s7194 = "7194";
+  public static final String s7195 = "7195";
+  public static final String s7196 = "7196";
+  public static final String s7197 = "7197";
+  public static final String s7198 = "7198";
+  public static final String s7199 = "7199";
+  public static final String s7200 = "7200";
+  public static final String s7201 = "7201";
+  public static final String s7202 = "7202";
+  public static final String s7203 = "7203";
+  public static final String s7204 = "7204";
+  public static final String s7205 = "7205";
+  public static final String s7206 = "7206";
+  public static final String s7207 = "7207";
+  public static final String s7208 = "7208";
+  public static final String s7209 = "7209";
+  public static final String s7210 = "7210";
+  public static final String s7211 = "7211";
+  public static final String s7212 = "7212";
+  public static final String s7213 = "7213";
+  public static final String s7214 = "7214";
+  public static final String s7215 = "7215";
+  public static final String s7216 = "7216";
+  public static final String s7217 = "7217";
+  public static final String s7218 = "7218";
+  public static final String s7219 = "7219";
+  public static final String s7220 = "7220";
+  public static final String s7221 = "7221";
+  public static final String s7222 = "7222";
+  public static final String s7223 = "7223";
+  public static final String s7224 = "7224";
+  public static final String s7225 = "7225";
+  public static final String s7226 = "7226";
+  public static final String s7227 = "7227";
+  public static final String s7228 = "7228";
+  public static final String s7229 = "7229";
+  public static final String s7230 = "7230";
+  public static final String s7231 = "7231";
+  public static final String s7232 = "7232";
+  public static final String s7233 = "7233";
+  public static final String s7234 = "7234";
+  public static final String s7235 = "7235";
+  public static final String s7236 = "7236";
+  public static final String s7237 = "7237";
+  public static final String s7238 = "7238";
+  public static final String s7239 = "7239";
+  public static final String s7240 = "7240";
+  public static final String s7241 = "7241";
+  public static final String s7242 = "7242";
+  public static final String s7243 = "7243";
+  public static final String s7244 = "7244";
+  public static final String s7245 = "7245";
+  public static final String s7246 = "7246";
+  public static final String s7247 = "7247";
+  public static final String s7248 = "7248";
+  public static final String s7249 = "7249";
+  public static final String s7250 = "7250";
+  public static final String s7251 = "7251";
+  public static final String s7252 = "7252";
+  public static final String s7253 = "7253";
+  public static final String s7254 = "7254";
+  public static final String s7255 = "7255";
+  public static final String s7256 = "7256";
+  public static final String s7257 = "7257";
+  public static final String s7258 = "7258";
+  public static final String s7259 = "7259";
+  public static final String s7260 = "7260";
+  public static final String s7261 = "7261";
+  public static final String s7262 = "7262";
+  public static final String s7263 = "7263";
+  public static final String s7264 = "7264";
+  public static final String s7265 = "7265";
+  public static final String s7266 = "7266";
+  public static final String s7267 = "7267";
+  public static final String s7268 = "7268";
+  public static final String s7269 = "7269";
+  public static final String s7270 = "7270";
+  public static final String s7271 = "7271";
+  public static final String s7272 = "7272";
+  public static final String s7273 = "7273";
+  public static final String s7274 = "7274";
+  public static final String s7275 = "7275";
+  public static final String s7276 = "7276";
+  public static final String s7277 = "7277";
+  public static final String s7278 = "7278";
+  public static final String s7279 = "7279";
+  public static final String s7280 = "7280";
+  public static final String s7281 = "7281";
+  public static final String s7282 = "7282";
+  public static final String s7283 = "7283";
+  public static final String s7284 = "7284";
+  public static final String s7285 = "7285";
+  public static final String s7286 = "7286";
+  public static final String s7287 = "7287";
+  public static final String s7288 = "7288";
+  public static final String s7289 = "7289";
+  public static final String s7290 = "7290";
+  public static final String s7291 = "7291";
+  public static final String s7292 = "7292";
+  public static final String s7293 = "7293";
+  public static final String s7294 = "7294";
+  public static final String s7295 = "7295";
+  public static final String s7296 = "7296";
+  public static final String s7297 = "7297";
+  public static final String s7298 = "7298";
+  public static final String s7299 = "7299";
+  public static final String s7300 = "7300";
+  public static final String s7301 = "7301";
+  public static final String s7302 = "7302";
+  public static final String s7303 = "7303";
+  public static final String s7304 = "7304";
+  public static final String s7305 = "7305";
+  public static final String s7306 = "7306";
+  public static final String s7307 = "7307";
+  public static final String s7308 = "7308";
+  public static final String s7309 = "7309";
+  public static final String s7310 = "7310";
+  public static final String s7311 = "7311";
+  public static final String s7312 = "7312";
+  public static final String s7313 = "7313";
+  public static final String s7314 = "7314";
+  public static final String s7315 = "7315";
+  public static final String s7316 = "7316";
+  public static final String s7317 = "7317";
+  public static final String s7318 = "7318";
+  public static final String s7319 = "7319";
+  public static final String s7320 = "7320";
+  public static final String s7321 = "7321";
+  public static final String s7322 = "7322";
+  public static final String s7323 = "7323";
+  public static final String s7324 = "7324";
+  public static final String s7325 = "7325";
+  public static final String s7326 = "7326";
+  public static final String s7327 = "7327";
+  public static final String s7328 = "7328";
+  public static final String s7329 = "7329";
+  public static final String s7330 = "7330";
+  public static final String s7331 = "7331";
+  public static final String s7332 = "7332";
+  public static final String s7333 = "7333";
+  public static final String s7334 = "7334";
+  public static final String s7335 = "7335";
+  public static final String s7336 = "7336";
+  public static final String s7337 = "7337";
+  public static final String s7338 = "7338";
+  public static final String s7339 = "7339";
+  public static final String s7340 = "7340";
+  public static final String s7341 = "7341";
+  public static final String s7342 = "7342";
+  public static final String s7343 = "7343";
+  public static final String s7344 = "7344";
+  public static final String s7345 = "7345";
+  public static final String s7346 = "7346";
+  public static final String s7347 = "7347";
+  public static final String s7348 = "7348";
+  public static final String s7349 = "7349";
+  public static final String s7350 = "7350";
+  public static final String s7351 = "7351";
+  public static final String s7352 = "7352";
+  public static final String s7353 = "7353";
+  public static final String s7354 = "7354";
+  public static final String s7355 = "7355";
+  public static final String s7356 = "7356";
+  public static final String s7357 = "7357";
+  public static final String s7358 = "7358";
+  public static final String s7359 = "7359";
+  public static final String s7360 = "7360";
+  public static final String s7361 = "7361";
+  public static final String s7362 = "7362";
+  public static final String s7363 = "7363";
+  public static final String s7364 = "7364";
+  public static final String s7365 = "7365";
+  public static final String s7366 = "7366";
+  public static final String s7367 = "7367";
+  public static final String s7368 = "7368";
+  public static final String s7369 = "7369";
+  public static final String s7370 = "7370";
+  public static final String s7371 = "7371";
+  public static final String s7372 = "7372";
+  public static final String s7373 = "7373";
+  public static final String s7374 = "7374";
+  public static final String s7375 = "7375";
+  public static final String s7376 = "7376";
+  public static final String s7377 = "7377";
+  public static final String s7378 = "7378";
+  public static final String s7379 = "7379";
+  public static final String s7380 = "7380";
+  public static final String s7381 = "7381";
+  public static final String s7382 = "7382";
+  public static final String s7383 = "7383";
+  public static final String s7384 = "7384";
+  public static final String s7385 = "7385";
+  public static final String s7386 = "7386";
+  public static final String s7387 = "7387";
+  public static final String s7388 = "7388";
+  public static final String s7389 = "7389";
+  public static final String s7390 = "7390";
+  public static final String s7391 = "7391";
+  public static final String s7392 = "7392";
+  public static final String s7393 = "7393";
+  public static final String s7394 = "7394";
+  public static final String s7395 = "7395";
+  public static final String s7396 = "7396";
+  public static final String s7397 = "7397";
+  public static final String s7398 = "7398";
+  public static final String s7399 = "7399";
+  public static final String s7400 = "7400";
+  public static final String s7401 = "7401";
+  public static final String s7402 = "7402";
+  public static final String s7403 = "7403";
+  public static final String s7404 = "7404";
+  public static final String s7405 = "7405";
+  public static final String s7406 = "7406";
+  public static final String s7407 = "7407";
+  public static final String s7408 = "7408";
+  public static final String s7409 = "7409";
+  public static final String s7410 = "7410";
+  public static final String s7411 = "7411";
+  public static final String s7412 = "7412";
+  public static final String s7413 = "7413";
+  public static final String s7414 = "7414";
+  public static final String s7415 = "7415";
+  public static final String s7416 = "7416";
+  public static final String s7417 = "7417";
+  public static final String s7418 = "7418";
+  public static final String s7419 = "7419";
+  public static final String s7420 = "7420";
+  public static final String s7421 = "7421";
+  public static final String s7422 = "7422";
+  public static final String s7423 = "7423";
+  public static final String s7424 = "7424";
+  public static final String s7425 = "7425";
+  public static final String s7426 = "7426";
+  public static final String s7427 = "7427";
+  public static final String s7428 = "7428";
+  public static final String s7429 = "7429";
+  public static final String s7430 = "7430";
+  public static final String s7431 = "7431";
+  public static final String s7432 = "7432";
+  public static final String s7433 = "7433";
+  public static final String s7434 = "7434";
+  public static final String s7435 = "7435";
+  public static final String s7436 = "7436";
+  public static final String s7437 = "7437";
+  public static final String s7438 = "7438";
+  public static final String s7439 = "7439";
+  public static final String s7440 = "7440";
+  public static final String s7441 = "7441";
+  public static final String s7442 = "7442";
+  public static final String s7443 = "7443";
+  public static final String s7444 = "7444";
+  public static final String s7445 = "7445";
+  public static final String s7446 = "7446";
+  public static final String s7447 = "7447";
+  public static final String s7448 = "7448";
+  public static final String s7449 = "7449";
+  public static final String s7450 = "7450";
+  public static final String s7451 = "7451";
+  public static final String s7452 = "7452";
+  public static final String s7453 = "7453";
+  public static final String s7454 = "7454";
+  public static final String s7455 = "7455";
+  public static final String s7456 = "7456";
+  public static final String s7457 = "7457";
+  public static final String s7458 = "7458";
+  public static final String s7459 = "7459";
+  public static final String s7460 = "7460";
+  public static final String s7461 = "7461";
+  public static final String s7462 = "7462";
+  public static final String s7463 = "7463";
+  public static final String s7464 = "7464";
+  public static final String s7465 = "7465";
+  public static final String s7466 = "7466";
+  public static final String s7467 = "7467";
+  public static final String s7468 = "7468";
+  public static final String s7469 = "7469";
+  public static final String s7470 = "7470";
+  public static final String s7471 = "7471";
+  public static final String s7472 = "7472";
+  public static final String s7473 = "7473";
+  public static final String s7474 = "7474";
+  public static final String s7475 = "7475";
+  public static final String s7476 = "7476";
+  public static final String s7477 = "7477";
+  public static final String s7478 = "7478";
+  public static final String s7479 = "7479";
+  public static final String s7480 = "7480";
+  public static final String s7481 = "7481";
+  public static final String s7482 = "7482";
+  public static final String s7483 = "7483";
+  public static final String s7484 = "7484";
+  public static final String s7485 = "7485";
+  public static final String s7486 = "7486";
+  public static final String s7487 = "7487";
+  public static final String s7488 = "7488";
+  public static final String s7489 = "7489";
+  public static final String s7490 = "7490";
+  public static final String s7491 = "7491";
+  public static final String s7492 = "7492";
+  public static final String s7493 = "7493";
+  public static final String s7494 = "7494";
+  public static final String s7495 = "7495";
+  public static final String s7496 = "7496";
+  public static final String s7497 = "7497";
+  public static final String s7498 = "7498";
+  public static final String s7499 = "7499";
+  public static final String s7500 = "7500";
+  public static final String s7501 = "7501";
+  public static final String s7502 = "7502";
+  public static final String s7503 = "7503";
+  public static final String s7504 = "7504";
+  public static final String s7505 = "7505";
+  public static final String s7506 = "7506";
+  public static final String s7507 = "7507";
+  public static final String s7508 = "7508";
+  public static final String s7509 = "7509";
+  public static final String s7510 = "7510";
+  public static final String s7511 = "7511";
+  public static final String s7512 = "7512";
+  public static final String s7513 = "7513";
+  public static final String s7514 = "7514";
+  public static final String s7515 = "7515";
+  public static final String s7516 = "7516";
+  public static final String s7517 = "7517";
+  public static final String s7518 = "7518";
+  public static final String s7519 = "7519";
+  public static final String s7520 = "7520";
+  public static final String s7521 = "7521";
+  public static final String s7522 = "7522";
+  public static final String s7523 = "7523";
+  public static final String s7524 = "7524";
+  public static final String s7525 = "7525";
+  public static final String s7526 = "7526";
+  public static final String s7527 = "7527";
+  public static final String s7528 = "7528";
+  public static final String s7529 = "7529";
+  public static final String s7530 = "7530";
+  public static final String s7531 = "7531";
+  public static final String s7532 = "7532";
+  public static final String s7533 = "7533";
+  public static final String s7534 = "7534";
+  public static final String s7535 = "7535";
+  public static final String s7536 = "7536";
+  public static final String s7537 = "7537";
+  public static final String s7538 = "7538";
+  public static final String s7539 = "7539";
+  public static final String s7540 = "7540";
+  public static final String s7541 = "7541";
+  public static final String s7542 = "7542";
+  public static final String s7543 = "7543";
+  public static final String s7544 = "7544";
+  public static final String s7545 = "7545";
+  public static final String s7546 = "7546";
+  public static final String s7547 = "7547";
+  public static final String s7548 = "7548";
+  public static final String s7549 = "7549";
+  public static final String s7550 = "7550";
+  public static final String s7551 = "7551";
+  public static final String s7552 = "7552";
+  public static final String s7553 = "7553";
+  public static final String s7554 = "7554";
+  public static final String s7555 = "7555";
+  public static final String s7556 = "7556";
+  public static final String s7557 = "7557";
+  public static final String s7558 = "7558";
+  public static final String s7559 = "7559";
+  public static final String s7560 = "7560";
+  public static final String s7561 = "7561";
+  public static final String s7562 = "7562";
+  public static final String s7563 = "7563";
+  public static final String s7564 = "7564";
+  public static final String s7565 = "7565";
+  public static final String s7566 = "7566";
+  public static final String s7567 = "7567";
+  public static final String s7568 = "7568";
+  public static final String s7569 = "7569";
+  public static final String s7570 = "7570";
+  public static final String s7571 = "7571";
+  public static final String s7572 = "7572";
+  public static final String s7573 = "7573";
+  public static final String s7574 = "7574";
+  public static final String s7575 = "7575";
+  public static final String s7576 = "7576";
+  public static final String s7577 = "7577";
+  public static final String s7578 = "7578";
+  public static final String s7579 = "7579";
+  public static final String s7580 = "7580";
+  public static final String s7581 = "7581";
+  public static final String s7582 = "7582";
+  public static final String s7583 = "7583";
+  public static final String s7584 = "7584";
+  public static final String s7585 = "7585";
+  public static final String s7586 = "7586";
+  public static final String s7587 = "7587";
+  public static final String s7588 = "7588";
+  public static final String s7589 = "7589";
+  public static final String s7590 = "7590";
+  public static final String s7591 = "7591";
+  public static final String s7592 = "7592";
+  public static final String s7593 = "7593";
+  public static final String s7594 = "7594";
+  public static final String s7595 = "7595";
+  public static final String s7596 = "7596";
+  public static final String s7597 = "7597";
+  public static final String s7598 = "7598";
+  public static final String s7599 = "7599";
+  public static final String s7600 = "7600";
+  public static final String s7601 = "7601";
+  public static final String s7602 = "7602";
+  public static final String s7603 = "7603";
+  public static final String s7604 = "7604";
+  public static final String s7605 = "7605";
+  public static final String s7606 = "7606";
+  public static final String s7607 = "7607";
+  public static final String s7608 = "7608";
+  public static final String s7609 = "7609";
+  public static final String s7610 = "7610";
+  public static final String s7611 = "7611";
+  public static final String s7612 = "7612";
+  public static final String s7613 = "7613";
+  public static final String s7614 = "7614";
+  public static final String s7615 = "7615";
+  public static final String s7616 = "7616";
+  public static final String s7617 = "7617";
+  public static final String s7618 = "7618";
+  public static final String s7619 = "7619";
+  public static final String s7620 = "7620";
+  public static final String s7621 = "7621";
+  public static final String s7622 = "7622";
+  public static final String s7623 = "7623";
+  public static final String s7624 = "7624";
+  public static final String s7625 = "7625";
+  public static final String s7626 = "7626";
+  public static final String s7627 = "7627";
+  public static final String s7628 = "7628";
+  public static final String s7629 = "7629";
+  public static final String s7630 = "7630";
+  public static final String s7631 = "7631";
+  public static final String s7632 = "7632";
+  public static final String s7633 = "7633";
+  public static final String s7634 = "7634";
+  public static final String s7635 = "7635";
+  public static final String s7636 = "7636";
+  public static final String s7637 = "7637";
+  public static final String s7638 = "7638";
+  public static final String s7639 = "7639";
+  public static final String s7640 = "7640";
+  public static final String s7641 = "7641";
+  public static final String s7642 = "7642";
+  public static final String s7643 = "7643";
+  public static final String s7644 = "7644";
+  public static final String s7645 = "7645";
+  public static final String s7646 = "7646";
+  public static final String s7647 = "7647";
+  public static final String s7648 = "7648";
+  public static final String s7649 = "7649";
+  public static final String s7650 = "7650";
+  public static final String s7651 = "7651";
+  public static final String s7652 = "7652";
+  public static final String s7653 = "7653";
+  public static final String s7654 = "7654";
+  public static final String s7655 = "7655";
+  public static final String s7656 = "7656";
+  public static final String s7657 = "7657";
+  public static final String s7658 = "7658";
+  public static final String s7659 = "7659";
+  public static final String s7660 = "7660";
+  public static final String s7661 = "7661";
+  public static final String s7662 = "7662";
+  public static final String s7663 = "7663";
+  public static final String s7664 = "7664";
+  public static final String s7665 = "7665";
+  public static final String s7666 = "7666";
+  public static final String s7667 = "7667";
+  public static final String s7668 = "7668";
+  public static final String s7669 = "7669";
+  public static final String s7670 = "7670";
+  public static final String s7671 = "7671";
+  public static final String s7672 = "7672";
+  public static final String s7673 = "7673";
+  public static final String s7674 = "7674";
+  public static final String s7675 = "7675";
+  public static final String s7676 = "7676";
+  public static final String s7677 = "7677";
+  public static final String s7678 = "7678";
+  public static final String s7679 = "7679";
+  public static final String s7680 = "7680";
+  public static final String s7681 = "7681";
+  public static final String s7682 = "7682";
+  public static final String s7683 = "7683";
+  public static final String s7684 = "7684";
+  public static final String s7685 = "7685";
+  public static final String s7686 = "7686";
+  public static final String s7687 = "7687";
+  public static final String s7688 = "7688";
+  public static final String s7689 = "7689";
+  public static final String s7690 = "7690";
+  public static final String s7691 = "7691";
+  public static final String s7692 = "7692";
+  public static final String s7693 = "7693";
+  public static final String s7694 = "7694";
+  public static final String s7695 = "7695";
+  public static final String s7696 = "7696";
+  public static final String s7697 = "7697";
+  public static final String s7698 = "7698";
+  public static final String s7699 = "7699";
+  public static final String s7700 = "7700";
+  public static final String s7701 = "7701";
+  public static final String s7702 = "7702";
+  public static final String s7703 = "7703";
+  public static final String s7704 = "7704";
+  public static final String s7705 = "7705";
+  public static final String s7706 = "7706";
+  public static final String s7707 = "7707";
+  public static final String s7708 = "7708";
+  public static final String s7709 = "7709";
+  public static final String s7710 = "7710";
+  public static final String s7711 = "7711";
+  public static final String s7712 = "7712";
+  public static final String s7713 = "7713";
+  public static final String s7714 = "7714";
+  public static final String s7715 = "7715";
+  public static final String s7716 = "7716";
+  public static final String s7717 = "7717";
+  public static final String s7718 = "7718";
+  public static final String s7719 = "7719";
+  public static final String s7720 = "7720";
+  public static final String s7721 = "7721";
+  public static final String s7722 = "7722";
+  public static final String s7723 = "7723";
+  public static final String s7724 = "7724";
+  public static final String s7725 = "7725";
+  public static final String s7726 = "7726";
+  public static final String s7727 = "7727";
+  public static final String s7728 = "7728";
+  public static final String s7729 = "7729";
+  public static final String s7730 = "7730";
+  public static final String s7731 = "7731";
+  public static final String s7732 = "7732";
+  public static final String s7733 = "7733";
+  public static final String s7734 = "7734";
+  public static final String s7735 = "7735";
+  public static final String s7736 = "7736";
+  public static final String s7737 = "7737";
+  public static final String s7738 = "7738";
+  public static final String s7739 = "7739";
+  public static final String s7740 = "7740";
+  public static final String s7741 = "7741";
+  public static final String s7742 = "7742";
+  public static final String s7743 = "7743";
+  public static final String s7744 = "7744";
+  public static final String s7745 = "7745";
+  public static final String s7746 = "7746";
+  public static final String s7747 = "7747";
+  public static final String s7748 = "7748";
+  public static final String s7749 = "7749";
+  public static final String s7750 = "7750";
+  public static final String s7751 = "7751";
+  public static final String s7752 = "7752";
+  public static final String s7753 = "7753";
+  public static final String s7754 = "7754";
+  public static final String s7755 = "7755";
+  public static final String s7756 = "7756";
+  public static final String s7757 = "7757";
+  public static final String s7758 = "7758";
+  public static final String s7759 = "7759";
+  public static final String s7760 = "7760";
+  public static final String s7761 = "7761";
+  public static final String s7762 = "7762";
+  public static final String s7763 = "7763";
+  public static final String s7764 = "7764";
+  public static final String s7765 = "7765";
+  public static final String s7766 = "7766";
+  public static final String s7767 = "7767";
+  public static final String s7768 = "7768";
+  public static final String s7769 = "7769";
+  public static final String s7770 = "7770";
+  public static final String s7771 = "7771";
+  public static final String s7772 = "7772";
+  public static final String s7773 = "7773";
+  public static final String s7774 = "7774";
+  public static final String s7775 = "7775";
+  public static final String s7776 = "7776";
+  public static final String s7777 = "7777";
+  public static final String s7778 = "7778";
+  public static final String s7779 = "7779";
+  public static final String s7780 = "7780";
+  public static final String s7781 = "7781";
+  public static final String s7782 = "7782";
+  public static final String s7783 = "7783";
+  public static final String s7784 = "7784";
+  public static final String s7785 = "7785";
+  public static final String s7786 = "7786";
+  public static final String s7787 = "7787";
+  public static final String s7788 = "7788";
+  public static final String s7789 = "7789";
+  public static final String s7790 = "7790";
+  public static final String s7791 = "7791";
+  public static final String s7792 = "7792";
+  public static final String s7793 = "7793";
+  public static final String s7794 = "7794";
+  public static final String s7795 = "7795";
+  public static final String s7796 = "7796";
+  public static final String s7797 = "7797";
+  public static final String s7798 = "7798";
+  public static final String s7799 = "7799";
+  public static final String s7800 = "7800";
+  public static final String s7801 = "7801";
+  public static final String s7802 = "7802";
+  public static final String s7803 = "7803";
+  public static final String s7804 = "7804";
+  public static final String s7805 = "7805";
+  public static final String s7806 = "7806";
+  public static final String s7807 = "7807";
+  public static final String s7808 = "7808";
+  public static final String s7809 = "7809";
+  public static final String s7810 = "7810";
+  public static final String s7811 = "7811";
+  public static final String s7812 = "7812";
+  public static final String s7813 = "7813";
+  public static final String s7814 = "7814";
+  public static final String s7815 = "7815";
+  public static final String s7816 = "7816";
+  public static final String s7817 = "7817";
+  public static final String s7818 = "7818";
+  public static final String s7819 = "7819";
+  public static final String s7820 = "7820";
+  public static final String s7821 = "7821";
+  public static final String s7822 = "7822";
+  public static final String s7823 = "7823";
+  public static final String s7824 = "7824";
+  public static final String s7825 = "7825";
+  public static final String s7826 = "7826";
+  public static final String s7827 = "7827";
+  public static final String s7828 = "7828";
+  public static final String s7829 = "7829";
+  public static final String s7830 = "7830";
+  public static final String s7831 = "7831";
+  public static final String s7832 = "7832";
+  public static final String s7833 = "7833";
+  public static final String s7834 = "7834";
+  public static final String s7835 = "7835";
+  public static final String s7836 = "7836";
+  public static final String s7837 = "7837";
+  public static final String s7838 = "7838";
+  public static final String s7839 = "7839";
+  public static final String s7840 = "7840";
+  public static final String s7841 = "7841";
+  public static final String s7842 = "7842";
+  public static final String s7843 = "7843";
+  public static final String s7844 = "7844";
+  public static final String s7845 = "7845";
+  public static final String s7846 = "7846";
+  public static final String s7847 = "7847";
+  public static final String s7848 = "7848";
+  public static final String s7849 = "7849";
+  public static final String s7850 = "7850";
+  public static final String s7851 = "7851";
+  public static final String s7852 = "7852";
+  public static final String s7853 = "7853";
+  public static final String s7854 = "7854";
+  public static final String s7855 = "7855";
+  public static final String s7856 = "7856";
+  public static final String s7857 = "7857";
+  public static final String s7858 = "7858";
+  public static final String s7859 = "7859";
+  public static final String s7860 = "7860";
+  public static final String s7861 = "7861";
+  public static final String s7862 = "7862";
+  public static final String s7863 = "7863";
+  public static final String s7864 = "7864";
+  public static final String s7865 = "7865";
+  public static final String s7866 = "7866";
+  public static final String s7867 = "7867";
+  public static final String s7868 = "7868";
+  public static final String s7869 = "7869";
+  public static final String s7870 = "7870";
+  public static final String s7871 = "7871";
+  public static final String s7872 = "7872";
+  public static final String s7873 = "7873";
+  public static final String s7874 = "7874";
+  public static final String s7875 = "7875";
+  public static final String s7876 = "7876";
+  public static final String s7877 = "7877";
+  public static final String s7878 = "7878";
+  public static final String s7879 = "7879";
+  public static final String s7880 = "7880";
+  public static final String s7881 = "7881";
+  public static final String s7882 = "7882";
+  public static final String s7883 = "7883";
+  public static final String s7884 = "7884";
+  public static final String s7885 = "7885";
+  public static final String s7886 = "7886";
+  public static final String s7887 = "7887";
+  public static final String s7888 = "7888";
+  public static final String s7889 = "7889";
+  public static final String s7890 = "7890";
+  public static final String s7891 = "7891";
+  public static final String s7892 = "7892";
+  public static final String s7893 = "7893";
+  public static final String s7894 = "7894";
+  public static final String s7895 = "7895";
+  public static final String s7896 = "7896";
+  public static final String s7897 = "7897";
+  public static final String s7898 = "7898";
+  public static final String s7899 = "7899";
+  public static final String s7900 = "7900";
+  public static final String s7901 = "7901";
+  public static final String s7902 = "7902";
+  public static final String s7903 = "7903";
+  public static final String s7904 = "7904";
+  public static final String s7905 = "7905";
+  public static final String s7906 = "7906";
+  public static final String s7907 = "7907";
+  public static final String s7908 = "7908";
+  public static final String s7909 = "7909";
+  public static final String s7910 = "7910";
+  public static final String s7911 = "7911";
+  public static final String s7912 = "7912";
+  public static final String s7913 = "7913";
+  public static final String s7914 = "7914";
+  public static final String s7915 = "7915";
+  public static final String s7916 = "7916";
+  public static final String s7917 = "7917";
+  public static final String s7918 = "7918";
+  public static final String s7919 = "7919";
+  public static final String s7920 = "7920";
+  public static final String s7921 = "7921";
+  public static final String s7922 = "7922";
+  public static final String s7923 = "7923";
+  public static final String s7924 = "7924";
+  public static final String s7925 = "7925";
+  public static final String s7926 = "7926";
+  public static final String s7927 = "7927";
+  public static final String s7928 = "7928";
+  public static final String s7929 = "7929";
+  public static final String s7930 = "7930";
+  public static final String s7931 = "7931";
+  public static final String s7932 = "7932";
+  public static final String s7933 = "7933";
+  public static final String s7934 = "7934";
+  public static final String s7935 = "7935";
+  public static final String s7936 = "7936";
+  public static final String s7937 = "7937";
+  public static final String s7938 = "7938";
+  public static final String s7939 = "7939";
+  public static final String s7940 = "7940";
+  public static final String s7941 = "7941";
+  public static final String s7942 = "7942";
+  public static final String s7943 = "7943";
+  public static final String s7944 = "7944";
+  public static final String s7945 = "7945";
+  public static final String s7946 = "7946";
+  public static final String s7947 = "7947";
+  public static final String s7948 = "7948";
+  public static final String s7949 = "7949";
+  public static final String s7950 = "7950";
+  public static final String s7951 = "7951";
+  public static final String s7952 = "7952";
+  public static final String s7953 = "7953";
+  public static final String s7954 = "7954";
+  public static final String s7955 = "7955";
+  public static final String s7956 = "7956";
+  public static final String s7957 = "7957";
+  public static final String s7958 = "7958";
+  public static final String s7959 = "7959";
+  public static final String s7960 = "7960";
+  public static final String s7961 = "7961";
+  public static final String s7962 = "7962";
+  public static final String s7963 = "7963";
+  public static final String s7964 = "7964";
+  public static final String s7965 = "7965";
+  public static final String s7966 = "7966";
+  public static final String s7967 = "7967";
+  public static final String s7968 = "7968";
+  public static final String s7969 = "7969";
+  public static final String s7970 = "7970";
+  public static final String s7971 = "7971";
+  public static final String s7972 = "7972";
+  public static final String s7973 = "7973";
+  public static final String s7974 = "7974";
+  public static final String s7975 = "7975";
+  public static final String s7976 = "7976";
+  public static final String s7977 = "7977";
+  public static final String s7978 = "7978";
+  public static final String s7979 = "7979";
+  public static final String s7980 = "7980";
+  public static final String s7981 = "7981";
+  public static final String s7982 = "7982";
+  public static final String s7983 = "7983";
+  public static final String s7984 = "7984";
+  public static final String s7985 = "7985";
+  public static final String s7986 = "7986";
+  public static final String s7987 = "7987";
+  public static final String s7988 = "7988";
+  public static final String s7989 = "7989";
+  public static final String s7990 = "7990";
+  public static final String s7991 = "7991";
+  public static final String s7992 = "7992";
+  public static final String s7993 = "7993";
+  public static final String s7994 = "7994";
+  public static final String s7995 = "7995";
+  public static final String s7996 = "7996";
+  public static final String s7997 = "7997";
+  public static final String s7998 = "7998";
+  public static final String s7999 = "7999";
+  public static final String s8000 = "8000";
+  public static final String s8001 = "8001";
+  public static final String s8002 = "8002";
+  public static final String s8003 = "8003";
+  public static final String s8004 = "8004";
+  public static final String s8005 = "8005";
+  public static final String s8006 = "8006";
+  public static final String s8007 = "8007";
+  public static final String s8008 = "8008";
+  public static final String s8009 = "8009";
+  public static final String s8010 = "8010";
+  public static final String s8011 = "8011";
+  public static final String s8012 = "8012";
+  public static final String s8013 = "8013";
+  public static final String s8014 = "8014";
+  public static final String s8015 = "8015";
+  public static final String s8016 = "8016";
+  public static final String s8017 = "8017";
+  public static final String s8018 = "8018";
+  public static final String s8019 = "8019";
+  public static final String s8020 = "8020";
+  public static final String s8021 = "8021";
+  public static final String s8022 = "8022";
+  public static final String s8023 = "8023";
+  public static final String s8024 = "8024";
+  public static final String s8025 = "8025";
+  public static final String s8026 = "8026";
+  public static final String s8027 = "8027";
+  public static final String s8028 = "8028";
+  public static final String s8029 = "8029";
+  public static final String s8030 = "8030";
+  public static final String s8031 = "8031";
+  public static final String s8032 = "8032";
+  public static final String s8033 = "8033";
+  public static final String s8034 = "8034";
+  public static final String s8035 = "8035";
+  public static final String s8036 = "8036";
+  public static final String s8037 = "8037";
+  public static final String s8038 = "8038";
+  public static final String s8039 = "8039";
+  public static final String s8040 = "8040";
+  public static final String s8041 = "8041";
+  public static final String s8042 = "8042";
+  public static final String s8043 = "8043";
+  public static final String s8044 = "8044";
+  public static final String s8045 = "8045";
+  public static final String s8046 = "8046";
+  public static final String s8047 = "8047";
+  public static final String s8048 = "8048";
+  public static final String s8049 = "8049";
+  public static final String s8050 = "8050";
+  public static final String s8051 = "8051";
+  public static final String s8052 = "8052";
+  public static final String s8053 = "8053";
+  public static final String s8054 = "8054";
+  public static final String s8055 = "8055";
+  public static final String s8056 = "8056";
+  public static final String s8057 = "8057";
+  public static final String s8058 = "8058";
+  public static final String s8059 = "8059";
+  public static final String s8060 = "8060";
+  public static final String s8061 = "8061";
+  public static final String s8062 = "8062";
+  public static final String s8063 = "8063";
+  public static final String s8064 = "8064";
+  public static final String s8065 = "8065";
+  public static final String s8066 = "8066";
+  public static final String s8067 = "8067";
+  public static final String s8068 = "8068";
+  public static final String s8069 = "8069";
+  public static final String s8070 = "8070";
+  public static final String s8071 = "8071";
+  public static final String s8072 = "8072";
+  public static final String s8073 = "8073";
+  public static final String s8074 = "8074";
+  public static final String s8075 = "8075";
+  public static final String s8076 = "8076";
+  public static final String s8077 = "8077";
+  public static final String s8078 = "8078";
+  public static final String s8079 = "8079";
+  public static final String s8080 = "8080";
+  public static final String s8081 = "8081";
+  public static final String s8082 = "8082";
+  public static final String s8083 = "8083";
+  public static final String s8084 = "8084";
+  public static final String s8085 = "8085";
+  public static final String s8086 = "8086";
+  public static final String s8087 = "8087";
+  public static final String s8088 = "8088";
+  public static final String s8089 = "8089";
+  public static final String s8090 = "8090";
+  public static final String s8091 = "8091";
+  public static final String s8092 = "8092";
+  public static final String s8093 = "8093";
+  public static final String s8094 = "8094";
+  public static final String s8095 = "8095";
+  public static final String s8096 = "8096";
+  public static final String s8097 = "8097";
+  public static final String s8098 = "8098";
+  public static final String s8099 = "8099";
+  public static final String s8100 = "8100";
+  public static final String s8101 = "8101";
+  public static final String s8102 = "8102";
+  public static final String s8103 = "8103";
+  public static final String s8104 = "8104";
+  public static final String s8105 = "8105";
+  public static final String s8106 = "8106";
+  public static final String s8107 = "8107";
+  public static final String s8108 = "8108";
+  public static final String s8109 = "8109";
+  public static final String s8110 = "8110";
+  public static final String s8111 = "8111";
+  public static final String s8112 = "8112";
+  public static final String s8113 = "8113";
+  public static final String s8114 = "8114";
+  public static final String s8115 = "8115";
+  public static final String s8116 = "8116";
+  public static final String s8117 = "8117";
+  public static final String s8118 = "8118";
+  public static final String s8119 = "8119";
+  public static final String s8120 = "8120";
+  public static final String s8121 = "8121";
+  public static final String s8122 = "8122";
+  public static final String s8123 = "8123";
+  public static final String s8124 = "8124";
+  public static final String s8125 = "8125";
+  public static final String s8126 = "8126";
+  public static final String s8127 = "8127";
+  public static final String s8128 = "8128";
+  public static final String s8129 = "8129";
+  public static final String s8130 = "8130";
+  public static final String s8131 = "8131";
+  public static final String s8132 = "8132";
+  public static final String s8133 = "8133";
+  public static final String s8134 = "8134";
+  public static final String s8135 = "8135";
+  public static final String s8136 = "8136";
+  public static final String s8137 = "8137";
+  public static final String s8138 = "8138";
+  public static final String s8139 = "8139";
+  public static final String s8140 = "8140";
+  public static final String s8141 = "8141";
+  public static final String s8142 = "8142";
+  public static final String s8143 = "8143";
+  public static final String s8144 = "8144";
+  public static final String s8145 = "8145";
+  public static final String s8146 = "8146";
+  public static final String s8147 = "8147";
+  public static final String s8148 = "8148";
+  public static final String s8149 = "8149";
+  public static final String s8150 = "8150";
+  public static final String s8151 = "8151";
+  public static final String s8152 = "8152";
+  public static final String s8153 = "8153";
+  public static final String s8154 = "8154";
+  public static final String s8155 = "8155";
+  public static final String s8156 = "8156";
+  public static final String s8157 = "8157";
+  public static final String s8158 = "8158";
+  public static final String s8159 = "8159";
+  public static final String s8160 = "8160";
+  public static final String s8161 = "8161";
+  public static final String s8162 = "8162";
+  public static final String s8163 = "8163";
+  public static final String s8164 = "8164";
+  public static final String s8165 = "8165";
+  public static final String s8166 = "8166";
+  public static final String s8167 = "8167";
+  public static final String s8168 = "8168";
+  public static final String s8169 = "8169";
+  public static final String s8170 = "8170";
+  public static final String s8171 = "8171";
+  public static final String s8172 = "8172";
+  public static final String s8173 = "8173";
+  public static final String s8174 = "8174";
+  public static final String s8175 = "8175";
+  public static final String s8176 = "8176";
+  public static final String s8177 = "8177";
+  public static final String s8178 = "8178";
+  public static final String s8179 = "8179";
+  public static final String s8180 = "8180";
+  public static final String s8181 = "8181";
+  public static final String s8182 = "8182";
+  public static final String s8183 = "8183";
+  public static final String s8184 = "8184";
+  public static final String s8185 = "8185";
+  public static final String s8186 = "8186";
+  public static final String s8187 = "8187";
+  public static final String s8188 = "8188";
+  public static final String s8189 = "8189";
+  public static final String s8190 = "8190";
+  public static final String s8191 = "8191";
+  public static final String s8192 = "8192";
+  public static final String s8193 = "8193";
+  public static final String s8194 = "8194";
+  public static final String s8195 = "8195";
+  public static final String s8196 = "8196";
+  public static final String s8197 = "8197";
+  public static final String s8198 = "8198";
+  public static final String s8199 = "8199";
+  public static final String s8200 = "8200";
+  public static final String s8201 = "8201";
+  public static final String s8202 = "8202";
+  public static final String s8203 = "8203";
+  public static final String s8204 = "8204";
+  public static final String s8205 = "8205";
+  public static final String s8206 = "8206";
+  public static final String s8207 = "8207";
+  public static final String s8208 = "8208";
+  public static final String s8209 = "8209";
+  public static final String s8210 = "8210";
+  public static final String s8211 = "8211";
+  public static final String s8212 = "8212";
+  public static final String s8213 = "8213";
+  public static final String s8214 = "8214";
+  public static final String s8215 = "8215";
+  public static final String s8216 = "8216";
+  public static final String s8217 = "8217";
+  public static final String s8218 = "8218";
+  public static final String s8219 = "8219";
+  public static final String s8220 = "8220";
+  public static final String s8221 = "8221";
+  public static final String s8222 = "8222";
+  public static final String s8223 = "8223";
+  public static final String s8224 = "8224";
+  public static final String s8225 = "8225";
+  public static final String s8226 = "8226";
+  public static final String s8227 = "8227";
+  public static final String s8228 = "8228";
+  public static final String s8229 = "8229";
+  public static final String s8230 = "8230";
+  public static final String s8231 = "8231";
+  public static final String s8232 = "8232";
+  public static final String s8233 = "8233";
+  public static final String s8234 = "8234";
+  public static final String s8235 = "8235";
+  public static final String s8236 = "8236";
+  public static final String s8237 = "8237";
+  public static final String s8238 = "8238";
+  public static final String s8239 = "8239";
+  public static final String s8240 = "8240";
+  public static final String s8241 = "8241";
+  public static final String s8242 = "8242";
+  public static final String s8243 = "8243";
+  public static final String s8244 = "8244";
+  public static final String s8245 = "8245";
+  public static final String s8246 = "8246";
+  public static final String s8247 = "8247";
+  public static final String s8248 = "8248";
+  public static final String s8249 = "8249";
+  public static final String s8250 = "8250";
+  public static final String s8251 = "8251";
+  public static final String s8252 = "8252";
+  public static final String s8253 = "8253";
+  public static final String s8254 = "8254";
+  public static final String s8255 = "8255";
+  public static final String s8256 = "8256";
+  public static final String s8257 = "8257";
+  public static final String s8258 = "8258";
+  public static final String s8259 = "8259";
+  public static final String s8260 = "8260";
+  public static final String s8261 = "8261";
+  public static final String s8262 = "8262";
+  public static final String s8263 = "8263";
+  public static final String s8264 = "8264";
+  public static final String s8265 = "8265";
+  public static final String s8266 = "8266";
+  public static final String s8267 = "8267";
+  public static final String s8268 = "8268";
+  public static final String s8269 = "8269";
+  public static final String s8270 = "8270";
+  public static final String s8271 = "8271";
+  public static final String s8272 = "8272";
+  public static final String s8273 = "8273";
+  public static final String s8274 = "8274";
+  public static final String s8275 = "8275";
+  public static final String s8276 = "8276";
+  public static final String s8277 = "8277";
+  public static final String s8278 = "8278";
+  public static final String s8279 = "8279";
+  public static final String s8280 = "8280";
+  public static final String s8281 = "8281";
+  public static final String s8282 = "8282";
+  public static final String s8283 = "8283";
+  public static final String s8284 = "8284";
+  public static final String s8285 = "8285";
+  public static final String s8286 = "8286";
+  public static final String s8287 = "8287";
+  public static final String s8288 = "8288";
+  public static final String s8289 = "8289";
+  public static final String s8290 = "8290";
+  public static final String s8291 = "8291";
+  public static final String s8292 = "8292";
+  public static final String s8293 = "8293";
+  public static final String s8294 = "8294";
+  public static final String s8295 = "8295";
+  public static final String s8296 = "8296";
+  public static final String s8297 = "8297";
+  public static final String s8298 = "8298";
+  public static final String s8299 = "8299";
+  public static final String s8300 = "8300";
+  public static final String s8301 = "8301";
+  public static final String s8302 = "8302";
+  public static final String s8303 = "8303";
+  public static final String s8304 = "8304";
+  public static final String s8305 = "8305";
+  public static final String s8306 = "8306";
+  public static final String s8307 = "8307";
+  public static final String s8308 = "8308";
+  public static final String s8309 = "8309";
+  public static final String s8310 = "8310";
+  public static final String s8311 = "8311";
+  public static final String s8312 = "8312";
+  public static final String s8313 = "8313";
+  public static final String s8314 = "8314";
+  public static final String s8315 = "8315";
+  public static final String s8316 = "8316";
+  public static final String s8317 = "8317";
+  public static final String s8318 = "8318";
+  public static final String s8319 = "8319";
+  public static final String s8320 = "8320";
+  public static final String s8321 = "8321";
+  public static final String s8322 = "8322";
+  public static final String s8323 = "8323";
+  public static final String s8324 = "8324";
+  public static final String s8325 = "8325";
+  public static final String s8326 = "8326";
+  public static final String s8327 = "8327";
+  public static final String s8328 = "8328";
+  public static final String s8329 = "8329";
+  public static final String s8330 = "8330";
+  public static final String s8331 = "8331";
+  public static final String s8332 = "8332";
+  public static final String s8333 = "8333";
+  public static final String s8334 = "8334";
+  public static final String s8335 = "8335";
+  public static final String s8336 = "8336";
+  public static final String s8337 = "8337";
+  public static final String s8338 = "8338";
+  public static final String s8339 = "8339";
+  public static final String s8340 = "8340";
+  public static final String s8341 = "8341";
+  public static final String s8342 = "8342";
+  public static final String s8343 = "8343";
+  public static final String s8344 = "8344";
+  public static final String s8345 = "8345";
+  public static final String s8346 = "8346";
+  public static final String s8347 = "8347";
+  public static final String s8348 = "8348";
+  public static final String s8349 = "8349";
+  public static final String s8350 = "8350";
+  public static final String s8351 = "8351";
+  public static final String s8352 = "8352";
+  public static final String s8353 = "8353";
+  public static final String s8354 = "8354";
+  public static final String s8355 = "8355";
+  public static final String s8356 = "8356";
+  public static final String s8357 = "8357";
+  public static final String s8358 = "8358";
+  public static final String s8359 = "8359";
+  public static final String s8360 = "8360";
+  public static final String s8361 = "8361";
+  public static final String s8362 = "8362";
+  public static final String s8363 = "8363";
+  public static final String s8364 = "8364";
+  public static final String s8365 = "8365";
+  public static final String s8366 = "8366";
+  public static final String s8367 = "8367";
+  public static final String s8368 = "8368";
+  public static final String s8369 = "8369";
+  public static final String s8370 = "8370";
+  public static final String s8371 = "8371";
+  public static final String s8372 = "8372";
+  public static final String s8373 = "8373";
+  public static final String s8374 = "8374";
+  public static final String s8375 = "8375";
+  public static final String s8376 = "8376";
+  public static final String s8377 = "8377";
+  public static final String s8378 = "8378";
+  public static final String s8379 = "8379";
+  public static final String s8380 = "8380";
+  public static final String s8381 = "8381";
+  public static final String s8382 = "8382";
+  public static final String s8383 = "8383";
+  public static final String s8384 = "8384";
+  public static final String s8385 = "8385";
+  public static final String s8386 = "8386";
+  public static final String s8387 = "8387";
+  public static final String s8388 = "8388";
+  public static final String s8389 = "8389";
+  public static final String s8390 = "8390";
+  public static final String s8391 = "8391";
+  public static final String s8392 = "8392";
+  public static final String s8393 = "8393";
+  public static final String s8394 = "8394";
+  public static final String s8395 = "8395";
+  public static final String s8396 = "8396";
+  public static final String s8397 = "8397";
+  public static final String s8398 = "8398";
+  public static final String s8399 = "8399";
+  public static final String s8400 = "8400";
+  public static final String s8401 = "8401";
+  public static final String s8402 = "8402";
+  public static final String s8403 = "8403";
+  public static final String s8404 = "8404";
+  public static final String s8405 = "8405";
+  public static final String s8406 = "8406";
+  public static final String s8407 = "8407";
+  public static final String s8408 = "8408";
+  public static final String s8409 = "8409";
+  public static final String s8410 = "8410";
+  public static final String s8411 = "8411";
+  public static final String s8412 = "8412";
+  public static final String s8413 = "8413";
+  public static final String s8414 = "8414";
+  public static final String s8415 = "8415";
+  public static final String s8416 = "8416";
+  public static final String s8417 = "8417";
+  public static final String s8418 = "8418";
+  public static final String s8419 = "8419";
+  public static final String s8420 = "8420";
+  public static final String s8421 = "8421";
+  public static final String s8422 = "8422";
+  public static final String s8423 = "8423";
+  public static final String s8424 = "8424";
+  public static final String s8425 = "8425";
+  public static final String s8426 = "8426";
+  public static final String s8427 = "8427";
+  public static final String s8428 = "8428";
+  public static final String s8429 = "8429";
+  public static final String s8430 = "8430";
+  public static final String s8431 = "8431";
+  public static final String s8432 = "8432";
+  public static final String s8433 = "8433";
+  public static final String s8434 = "8434";
+  public static final String s8435 = "8435";
+  public static final String s8436 = "8436";
+  public static final String s8437 = "8437";
+  public static final String s8438 = "8438";
+  public static final String s8439 = "8439";
+  public static final String s8440 = "8440";
+  public static final String s8441 = "8441";
+  public static final String s8442 = "8442";
+  public static final String s8443 = "8443";
+  public static final String s8444 = "8444";
+  public static final String s8445 = "8445";
+  public static final String s8446 = "8446";
+  public static final String s8447 = "8447";
+  public static final String s8448 = "8448";
+  public static final String s8449 = "8449";
+  public static final String s8450 = "8450";
+  public static final String s8451 = "8451";
+  public static final String s8452 = "8452";
+  public static final String s8453 = "8453";
+  public static final String s8454 = "8454";
+  public static final String s8455 = "8455";
+  public static final String s8456 = "8456";
+  public static final String s8457 = "8457";
+  public static final String s8458 = "8458";
+  public static final String s8459 = "8459";
+  public static final String s8460 = "8460";
+  public static final String s8461 = "8461";
+  public static final String s8462 = "8462";
+  public static final String s8463 = "8463";
+  public static final String s8464 = "8464";
+  public static final String s8465 = "8465";
+  public static final String s8466 = "8466";
+  public static final String s8467 = "8467";
+  public static final String s8468 = "8468";
+  public static final String s8469 = "8469";
+  public static final String s8470 = "8470";
+  public static final String s8471 = "8471";
+  public static final String s8472 = "8472";
+  public static final String s8473 = "8473";
+  public static final String s8474 = "8474";
+  public static final String s8475 = "8475";
+  public static final String s8476 = "8476";
+  public static final String s8477 = "8477";
+  public static final String s8478 = "8478";
+  public static final String s8479 = "8479";
+  public static final String s8480 = "8480";
+  public static final String s8481 = "8481";
+  public static final String s8482 = "8482";
+  public static final String s8483 = "8483";
+  public static final String s8484 = "8484";
+  public static final String s8485 = "8485";
+  public static final String s8486 = "8486";
+  public static final String s8487 = "8487";
+  public static final String s8488 = "8488";
+  public static final String s8489 = "8489";
+  public static final String s8490 = "8490";
+  public static final String s8491 = "8491";
+  public static final String s8492 = "8492";
+  public static final String s8493 = "8493";
+  public static final String s8494 = "8494";
+  public static final String s8495 = "8495";
+  public static final String s8496 = "8496";
+  public static final String s8497 = "8497";
+  public static final String s8498 = "8498";
+  public static final String s8499 = "8499";
+  public static final String s8500 = "8500";
+  public static final String s8501 = "8501";
+  public static final String s8502 = "8502";
+  public static final String s8503 = "8503";
+  public static final String s8504 = "8504";
+  public static final String s8505 = "8505";
+  public static final String s8506 = "8506";
+  public static final String s8507 = "8507";
+  public static final String s8508 = "8508";
+  public static final String s8509 = "8509";
+  public static final String s8510 = "8510";
+  public static final String s8511 = "8511";
+  public static final String s8512 = "8512";
+  public static final String s8513 = "8513";
+  public static final String s8514 = "8514";
+  public static final String s8515 = "8515";
+  public static final String s8516 = "8516";
+  public static final String s8517 = "8517";
+  public static final String s8518 = "8518";
+  public static final String s8519 = "8519";
+  public static final String s8520 = "8520";
+  public static final String s8521 = "8521";
+  public static final String s8522 = "8522";
+  public static final String s8523 = "8523";
+  public static final String s8524 = "8524";
+  public static final String s8525 = "8525";
+  public static final String s8526 = "8526";
+  public static final String s8527 = "8527";
+  public static final String s8528 = "8528";
+  public static final String s8529 = "8529";
+  public static final String s8530 = "8530";
+  public static final String s8531 = "8531";
+  public static final String s8532 = "8532";
+  public static final String s8533 = "8533";
+  public static final String s8534 = "8534";
+  public static final String s8535 = "8535";
+  public static final String s8536 = "8536";
+  public static final String s8537 = "8537";
+  public static final String s8538 = "8538";
+  public static final String s8539 = "8539";
+  public static final String s8540 = "8540";
+  public static final String s8541 = "8541";
+  public static final String s8542 = "8542";
+  public static final String s8543 = "8543";
+  public static final String s8544 = "8544";
+  public static final String s8545 = "8545";
+  public static final String s8546 = "8546";
+  public static final String s8547 = "8547";
+  public static final String s8548 = "8548";
+  public static final String s8549 = "8549";
+  public static final String s8550 = "8550";
+  public static final String s8551 = "8551";
+  public static final String s8552 = "8552";
+  public static final String s8553 = "8553";
+  public static final String s8554 = "8554";
+  public static final String s8555 = "8555";
+  public static final String s8556 = "8556";
+  public static final String s8557 = "8557";
+  public static final String s8558 = "8558";
+  public static final String s8559 = "8559";
+  public static final String s8560 = "8560";
+  public static final String s8561 = "8561";
+  public static final String s8562 = "8562";
+  public static final String s8563 = "8563";
+  public static final String s8564 = "8564";
+  public static final String s8565 = "8565";
+  public static final String s8566 = "8566";
+  public static final String s8567 = "8567";
+  public static final String s8568 = "8568";
+  public static final String s8569 = "8569";
+  public static final String s8570 = "8570";
+  public static final String s8571 = "8571";
+  public static final String s8572 = "8572";
+  public static final String s8573 = "8573";
+  public static final String s8574 = "8574";
+  public static final String s8575 = "8575";
+  public static final String s8576 = "8576";
+  public static final String s8577 = "8577";
+  public static final String s8578 = "8578";
+  public static final String s8579 = "8579";
+  public static final String s8580 = "8580";
+  public static final String s8581 = "8581";
+  public static final String s8582 = "8582";
+  public static final String s8583 = "8583";
+  public static final String s8584 = "8584";
+  public static final String s8585 = "8585";
+  public static final String s8586 = "8586";
+  public static final String s8587 = "8587";
+  public static final String s8588 = "8588";
+  public static final String s8589 = "8589";
+  public static final String s8590 = "8590";
+  public static final String s8591 = "8591";
+  public static final String s8592 = "8592";
+  public static final String s8593 = "8593";
+  public static final String s8594 = "8594";
+  public static final String s8595 = "8595";
+  public static final String s8596 = "8596";
+  public static final String s8597 = "8597";
+  public static final String s8598 = "8598";
+  public static final String s8599 = "8599";
+  public static final String s8600 = "8600";
+  public static final String s8601 = "8601";
+  public static final String s8602 = "8602";
+  public static final String s8603 = "8603";
+  public static final String s8604 = "8604";
+  public static final String s8605 = "8605";
+  public static final String s8606 = "8606";
+  public static final String s8607 = "8607";
+  public static final String s8608 = "8608";
+  public static final String s8609 = "8609";
+  public static final String s8610 = "8610";
+  public static final String s8611 = "8611";
+  public static final String s8612 = "8612";
+  public static final String s8613 = "8613";
+  public static final String s8614 = "8614";
+  public static final String s8615 = "8615";
+  public static final String s8616 = "8616";
+  public static final String s8617 = "8617";
+  public static final String s8618 = "8618";
+  public static final String s8619 = "8619";
+  public static final String s8620 = "8620";
+  public static final String s8621 = "8621";
+  public static final String s8622 = "8622";
+  public static final String s8623 = "8623";
+  public static final String s8624 = "8624";
+  public static final String s8625 = "8625";
+  public static final String s8626 = "8626";
+  public static final String s8627 = "8627";
+  public static final String s8628 = "8628";
+  public static final String s8629 = "8629";
+  public static final String s8630 = "8630";
+  public static final String s8631 = "8631";
+  public static final String s8632 = "8632";
+  public static final String s8633 = "8633";
+  public static final String s8634 = "8634";
+  public static final String s8635 = "8635";
+  public static final String s8636 = "8636";
+  public static final String s8637 = "8637";
+  public static final String s8638 = "8638";
+  public static final String s8639 = "8639";
+  public static final String s8640 = "8640";
+  public static final String s8641 = "8641";
+  public static final String s8642 = "8642";
+  public static final String s8643 = "8643";
+  public static final String s8644 = "8644";
+  public static final String s8645 = "8645";
+  public static final String s8646 = "8646";
+  public static final String s8647 = "8647";
+  public static final String s8648 = "8648";
+  public static final String s8649 = "8649";
+  public static final String s8650 = "8650";
+  public static final String s8651 = "8651";
+  public static final String s8652 = "8652";
+  public static final String s8653 = "8653";
+  public static final String s8654 = "8654";
+  public static final String s8655 = "8655";
+  public static final String s8656 = "8656";
+  public static final String s8657 = "8657";
+  public static final String s8658 = "8658";
+  public static final String s8659 = "8659";
+  public static final String s8660 = "8660";
+  public static final String s8661 = "8661";
+  public static final String s8662 = "8662";
+  public static final String s8663 = "8663";
+  public static final String s8664 = "8664";
+  public static final String s8665 = "8665";
+  public static final String s8666 = "8666";
+  public static final String s8667 = "8667";
+  public static final String s8668 = "8668";
+  public static final String s8669 = "8669";
+  public static final String s8670 = "8670";
+  public static final String s8671 = "8671";
+  public static final String s8672 = "8672";
+  public static final String s8673 = "8673";
+  public static final String s8674 = "8674";
+  public static final String s8675 = "8675";
+  public static final String s8676 = "8676";
+  public static final String s8677 = "8677";
+  public static final String s8678 = "8678";
+  public static final String s8679 = "8679";
+  public static final String s8680 = "8680";
+  public static final String s8681 = "8681";
+  public static final String s8682 = "8682";
+  public static final String s8683 = "8683";
+  public static final String s8684 = "8684";
+  public static final String s8685 = "8685";
+  public static final String s8686 = "8686";
+  public static final String s8687 = "8687";
+  public static final String s8688 = "8688";
+  public static final String s8689 = "8689";
+  public static final String s8690 = "8690";
+  public static final String s8691 = "8691";
+  public static final String s8692 = "8692";
+  public static final String s8693 = "8693";
+  public static final String s8694 = "8694";
+  public static final String s8695 = "8695";
+  public static final String s8696 = "8696";
+  public static final String s8697 = "8697";
+  public static final String s8698 = "8698";
+  public static final String s8699 = "8699";
+  public static final String s8700 = "8700";
+  public static final String s8701 = "8701";
+  public static final String s8702 = "8702";
+  public static final String s8703 = "8703";
+  public static final String s8704 = "8704";
+  public static final String s8705 = "8705";
+  public static final String s8706 = "8706";
+  public static final String s8707 = "8707";
+  public static final String s8708 = "8708";
+  public static final String s8709 = "8709";
+  public static final String s8710 = "8710";
+  public static final String s8711 = "8711";
+  public static final String s8712 = "8712";
+  public static final String s8713 = "8713";
+  public static final String s8714 = "8714";
+  public static final String s8715 = "8715";
+  public static final String s8716 = "8716";
+  public static final String s8717 = "8717";
+  public static final String s8718 = "8718";
+  public static final String s8719 = "8719";
+  public static final String s8720 = "8720";
+  public static final String s8721 = "8721";
+  public static final String s8722 = "8722";
+  public static final String s8723 = "8723";
+  public static final String s8724 = "8724";
+  public static final String s8725 = "8725";
+  public static final String s8726 = "8726";
+  public static final String s8727 = "8727";
+  public static final String s8728 = "8728";
+  public static final String s8729 = "8729";
+  public static final String s8730 = "8730";
+  public static final String s8731 = "8731";
+  public static final String s8732 = "8732";
+  public static final String s8733 = "8733";
+  public static final String s8734 = "8734";
+  public static final String s8735 = "8735";
+  public static final String s8736 = "8736";
+  public static final String s8737 = "8737";
+  public static final String s8738 = "8738";
+  public static final String s8739 = "8739";
+  public static final String s8740 = "8740";
+  public static final String s8741 = "8741";
+  public static final String s8742 = "8742";
+  public static final String s8743 = "8743";
+  public static final String s8744 = "8744";
+  public static final String s8745 = "8745";
+  public static final String s8746 = "8746";
+  public static final String s8747 = "8747";
+  public static final String s8748 = "8748";
+  public static final String s8749 = "8749";
+  public static final String s8750 = "8750";
+  public static final String s8751 = "8751";
+  public static final String s8752 = "8752";
+  public static final String s8753 = "8753";
+  public static final String s8754 = "8754";
+  public static final String s8755 = "8755";
+  public static final String s8756 = "8756";
+  public static final String s8757 = "8757";
+  public static final String s8758 = "8758";
+  public static final String s8759 = "8759";
+  public static final String s8760 = "8760";
+  public static final String s8761 = "8761";
+  public static final String s8762 = "8762";
+  public static final String s8763 = "8763";
+  public static final String s8764 = "8764";
+  public static final String s8765 = "8765";
+  public static final String s8766 = "8766";
+  public static final String s8767 = "8767";
+  public static final String s8768 = "8768";
+  public static final String s8769 = "8769";
+  public static final String s8770 = "8770";
+  public static final String s8771 = "8771";
+  public static final String s8772 = "8772";
+  public static final String s8773 = "8773";
+  public static final String s8774 = "8774";
+  public static final String s8775 = "8775";
+  public static final String s8776 = "8776";
+  public static final String s8777 = "8777";
+  public static final String s8778 = "8778";
+  public static final String s8779 = "8779";
+  public static final String s8780 = "8780";
+  public static final String s8781 = "8781";
+  public static final String s8782 = "8782";
+  public static final String s8783 = "8783";
+  public static final String s8784 = "8784";
+  public static final String s8785 = "8785";
+  public static final String s8786 = "8786";
+  public static final String s8787 = "8787";
+  public static final String s8788 = "8788";
+  public static final String s8789 = "8789";
+  public static final String s8790 = "8790";
+  public static final String s8791 = "8791";
+  public static final String s8792 = "8792";
+  public static final String s8793 = "8793";
+  public static final String s8794 = "8794";
+  public static final String s8795 = "8795";
+  public static final String s8796 = "8796";
+  public static final String s8797 = "8797";
+  public static final String s8798 = "8798";
+  public static final String s8799 = "8799";
+  public static final String s8800 = "8800";
+  public static final String s8801 = "8801";
+  public static final String s8802 = "8802";
+  public static final String s8803 = "8803";
+  public static final String s8804 = "8804";
+  public static final String s8805 = "8805";
+  public static final String s8806 = "8806";
+  public static final String s8807 = "8807";
+  public static final String s8808 = "8808";
+  public static final String s8809 = "8809";
+  public static final String s8810 = "8810";
+  public static final String s8811 = "8811";
+  public static final String s8812 = "8812";
+  public static final String s8813 = "8813";
+  public static final String s8814 = "8814";
+  public static final String s8815 = "8815";
+  public static final String s8816 = "8816";
+  public static final String s8817 = "8817";
+  public static final String s8818 = "8818";
+  public static final String s8819 = "8819";
+  public static final String s8820 = "8820";
+  public static final String s8821 = "8821";
+  public static final String s8822 = "8822";
+  public static final String s8823 = "8823";
+  public static final String s8824 = "8824";
+  public static final String s8825 = "8825";
+  public static final String s8826 = "8826";
+  public static final String s8827 = "8827";
+  public static final String s8828 = "8828";
+  public static final String s8829 = "8829";
+  public static final String s8830 = "8830";
+  public static final String s8831 = "8831";
+  public static final String s8832 = "8832";
+  public static final String s8833 = "8833";
+  public static final String s8834 = "8834";
+  public static final String s8835 = "8835";
+  public static final String s8836 = "8836";
+  public static final String s8837 = "8837";
+  public static final String s8838 = "8838";
+  public static final String s8839 = "8839";
+  public static final String s8840 = "8840";
+  public static final String s8841 = "8841";
+  public static final String s8842 = "8842";
+  public static final String s8843 = "8843";
+  public static final String s8844 = "8844";
+  public static final String s8845 = "8845";
+  public static final String s8846 = "8846";
+  public static final String s8847 = "8847";
+  public static final String s8848 = "8848";
+  public static final String s8849 = "8849";
+  public static final String s8850 = "8850";
+  public static final String s8851 = "8851";
+  public static final String s8852 = "8852";
+  public static final String s8853 = "8853";
+  public static final String s8854 = "8854";
+  public static final String s8855 = "8855";
+  public static final String s8856 = "8856";
+  public static final String s8857 = "8857";
+  public static final String s8858 = "8858";
+  public static final String s8859 = "8859";
+  public static final String s8860 = "8860";
+  public static final String s8861 = "8861";
+  public static final String s8862 = "8862";
+  public static final String s8863 = "8863";
+  public static final String s8864 = "8864";
+  public static final String s8865 = "8865";
+  public static final String s8866 = "8866";
+  public static final String s8867 = "8867";
+  public static final String s8868 = "8868";
+  public static final String s8869 = "8869";
+  public static final String s8870 = "8870";
+  public static final String s8871 = "8871";
+  public static final String s8872 = "8872";
+  public static final String s8873 = "8873";
+  public static final String s8874 = "8874";
+  public static final String s8875 = "8875";
+  public static final String s8876 = "8876";
+  public static final String s8877 = "8877";
+  public static final String s8878 = "8878";
+  public static final String s8879 = "8879";
+  public static final String s8880 = "8880";
+  public static final String s8881 = "8881";
+  public static final String s8882 = "8882";
+  public static final String s8883 = "8883";
+  public static final String s8884 = "8884";
+  public static final String s8885 = "8885";
+  public static final String s8886 = "8886";
+  public static final String s8887 = "8887";
+  public static final String s8888 = "8888";
+  public static final String s8889 = "8889";
+  public static final String s8890 = "8890";
+  public static final String s8891 = "8891";
+  public static final String s8892 = "8892";
+  public static final String s8893 = "8893";
+  public static final String s8894 = "8894";
+  public static final String s8895 = "8895";
+  public static final String s8896 = "8896";
+  public static final String s8897 = "8897";
+  public static final String s8898 = "8898";
+  public static final String s8899 = "8899";
+  public static final String s8900 = "8900";
+  public static final String s8901 = "8901";
+  public static final String s8902 = "8902";
+  public static final String s8903 = "8903";
+  public static final String s8904 = "8904";
+  public static final String s8905 = "8905";
+  public static final String s8906 = "8906";
+  public static final String s8907 = "8907";
+  public static final String s8908 = "8908";
+  public static final String s8909 = "8909";
+  public static final String s8910 = "8910";
+  public static final String s8911 = "8911";
+  public static final String s8912 = "8912";
+  public static final String s8913 = "8913";
+  public static final String s8914 = "8914";
+  public static final String s8915 = "8915";
+  public static final String s8916 = "8916";
+  public static final String s8917 = "8917";
+  public static final String s8918 = "8918";
+  public static final String s8919 = "8919";
+  public static final String s8920 = "8920";
+  public static final String s8921 = "8921";
+  public static final String s8922 = "8922";
+  public static final String s8923 = "8923";
+  public static final String s8924 = "8924";
+  public static final String s8925 = "8925";
+  public static final String s8926 = "8926";
+  public static final String s8927 = "8927";
+  public static final String s8928 = "8928";
+  public static final String s8929 = "8929";
+  public static final String s8930 = "8930";
+  public static final String s8931 = "8931";
+  public static final String s8932 = "8932";
+  public static final String s8933 = "8933";
+  public static final String s8934 = "8934";
+  public static final String s8935 = "8935";
+  public static final String s8936 = "8936";
+  public static final String s8937 = "8937";
+  public static final String s8938 = "8938";
+  public static final String s8939 = "8939";
+  public static final String s8940 = "8940";
+  public static final String s8941 = "8941";
+  public static final String s8942 = "8942";
+  public static final String s8943 = "8943";
+  public static final String s8944 = "8944";
+  public static final String s8945 = "8945";
+  public static final String s8946 = "8946";
+  public static final String s8947 = "8947";
+  public static final String s8948 = "8948";
+  public static final String s8949 = "8949";
+  public static final String s8950 = "8950";
+  public static final String s8951 = "8951";
+  public static final String s8952 = "8952";
+  public static final String s8953 = "8953";
+  public static final String s8954 = "8954";
+  public static final String s8955 = "8955";
+  public static final String s8956 = "8956";
+  public static final String s8957 = "8957";
+  public static final String s8958 = "8958";
+  public static final String s8959 = "8959";
+  public static final String s8960 = "8960";
+  public static final String s8961 = "8961";
+  public static final String s8962 = "8962";
+  public static final String s8963 = "8963";
+  public static final String s8964 = "8964";
+  public static final String s8965 = "8965";
+  public static final String s8966 = "8966";
+  public static final String s8967 = "8967";
+  public static final String s8968 = "8968";
+  public static final String s8969 = "8969";
+  public static final String s8970 = "8970";
+  public static final String s8971 = "8971";
+  public static final String s8972 = "8972";
+  public static final String s8973 = "8973";
+  public static final String s8974 = "8974";
+  public static final String s8975 = "8975";
+  public static final String s8976 = "8976";
+  public static final String s8977 = "8977";
+  public static final String s8978 = "8978";
+  public static final String s8979 = "8979";
+  public static final String s8980 = "8980";
+  public static final String s8981 = "8981";
+  public static final String s8982 = "8982";
+  public static final String s8983 = "8983";
+  public static final String s8984 = "8984";
+  public static final String s8985 = "8985";
+  public static final String s8986 = "8986";
+  public static final String s8987 = "8987";
+  public static final String s8988 = "8988";
+  public static final String s8989 = "8989";
+  public static final String s8990 = "8990";
+  public static final String s8991 = "8991";
+  public static final String s8992 = "8992";
+  public static final String s8993 = "8993";
+  public static final String s8994 = "8994";
+  public static final String s8995 = "8995";
+  public static final String s8996 = "8996";
+  public static final String s8997 = "8997";
+  public static final String s8998 = "8998";
+  public static final String s8999 = "8999";
+  public static final String s9000 = "9000";
+  public static final String s9001 = "9001";
+  public static final String s9002 = "9002";
+  public static final String s9003 = "9003";
+  public static final String s9004 = "9004";
+  public static final String s9005 = "9005";
+  public static final String s9006 = "9006";
+  public static final String s9007 = "9007";
+  public static final String s9008 = "9008";
+  public static final String s9009 = "9009";
+  public static final String s9010 = "9010";
+  public static final String s9011 = "9011";
+  public static final String s9012 = "9012";
+  public static final String s9013 = "9013";
+  public static final String s9014 = "9014";
+  public static final String s9015 = "9015";
+  public static final String s9016 = "9016";
+  public static final String s9017 = "9017";
+  public static final String s9018 = "9018";
+  public static final String s9019 = "9019";
+  public static final String s9020 = "9020";
+  public static final String s9021 = "9021";
+  public static final String s9022 = "9022";
+  public static final String s9023 = "9023";
+  public static final String s9024 = "9024";
+  public static final String s9025 = "9025";
+  public static final String s9026 = "9026";
+  public static final String s9027 = "9027";
+  public static final String s9028 = "9028";
+  public static final String s9029 = "9029";
+  public static final String s9030 = "9030";
+  public static final String s9031 = "9031";
+  public static final String s9032 = "9032";
+  public static final String s9033 = "9033";
+  public static final String s9034 = "9034";
+  public static final String s9035 = "9035";
+  public static final String s9036 = "9036";
+  public static final String s9037 = "9037";
+  public static final String s9038 = "9038";
+  public static final String s9039 = "9039";
+  public static final String s9040 = "9040";
+  public static final String s9041 = "9041";
+  public static final String s9042 = "9042";
+  public static final String s9043 = "9043";
+  public static final String s9044 = "9044";
+  public static final String s9045 = "9045";
+  public static final String s9046 = "9046";
+  public static final String s9047 = "9047";
+  public static final String s9048 = "9048";
+  public static final String s9049 = "9049";
+  public static final String s9050 = "9050";
+  public static final String s9051 = "9051";
+  public static final String s9052 = "9052";
+  public static final String s9053 = "9053";
+  public static final String s9054 = "9054";
+  public static final String s9055 = "9055";
+  public static final String s9056 = "9056";
+  public static final String s9057 = "9057";
+  public static final String s9058 = "9058";
+  public static final String s9059 = "9059";
+  public static final String s9060 = "9060";
+  public static final String s9061 = "9061";
+  public static final String s9062 = "9062";
+  public static final String s9063 = "9063";
+  public static final String s9064 = "9064";
+  public static final String s9065 = "9065";
+  public static final String s9066 = "9066";
+  public static final String s9067 = "9067";
+  public static final String s9068 = "9068";
+  public static final String s9069 = "9069";
+  public static final String s9070 = "9070";
+  public static final String s9071 = "9071";
+  public static final String s9072 = "9072";
+  public static final String s9073 = "9073";
+  public static final String s9074 = "9074";
+  public static final String s9075 = "9075";
+  public static final String s9076 = "9076";
+  public static final String s9077 = "9077";
+  public static final String s9078 = "9078";
+  public static final String s9079 = "9079";
+  public static final String s9080 = "9080";
+  public static final String s9081 = "9081";
+  public static final String s9082 = "9082";
+  public static final String s9083 = "9083";
+  public static final String s9084 = "9084";
+  public static final String s9085 = "9085";
+  public static final String s9086 = "9086";
+  public static final String s9087 = "9087";
+  public static final String s9088 = "9088";
+  public static final String s9089 = "9089";
+  public static final String s9090 = "9090";
+  public static final String s9091 = "9091";
+  public static final String s9092 = "9092";
+  public static final String s9093 = "9093";
+  public static final String s9094 = "9094";
+  public static final String s9095 = "9095";
+  public static final String s9096 = "9096";
+  public static final String s9097 = "9097";
+  public static final String s9098 = "9098";
+  public static final String s9099 = "9099";
+  public static final String s9100 = "9100";
+  public static final String s9101 = "9101";
+  public static final String s9102 = "9102";
+  public static final String s9103 = "9103";
+  public static final String s9104 = "9104";
+  public static final String s9105 = "9105";
+  public static final String s9106 = "9106";
+  public static final String s9107 = "9107";
+  public static final String s9108 = "9108";
+  public static final String s9109 = "9109";
+  public static final String s9110 = "9110";
+  public static final String s9111 = "9111";
+  public static final String s9112 = "9112";
+  public static final String s9113 = "9113";
+  public static final String s9114 = "9114";
+  public static final String s9115 = "9115";
+  public static final String s9116 = "9116";
+  public static final String s9117 = "9117";
+  public static final String s9118 = "9118";
+  public static final String s9119 = "9119";
+  public static final String s9120 = "9120";
+  public static final String s9121 = "9121";
+  public static final String s9122 = "9122";
+  public static final String s9123 = "9123";
+  public static final String s9124 = "9124";
+  public static final String s9125 = "9125";
+  public static final String s9126 = "9126";
+  public static final String s9127 = "9127";
+  public static final String s9128 = "9128";
+  public static final String s9129 = "9129";
+  public static final String s9130 = "9130";
+  public static final String s9131 = "9131";
+  public static final String s9132 = "9132";
+  public static final String s9133 = "9133";
+  public static final String s9134 = "9134";
+  public static final String s9135 = "9135";
+  public static final String s9136 = "9136";
+  public static final String s9137 = "9137";
+  public static final String s9138 = "9138";
+  public static final String s9139 = "9139";
+  public static final String s9140 = "9140";
+  public static final String s9141 = "9141";
+  public static final String s9142 = "9142";
+  public static final String s9143 = "9143";
+  public static final String s9144 = "9144";
+  public static final String s9145 = "9145";
+  public static final String s9146 = "9146";
+  public static final String s9147 = "9147";
+  public static final String s9148 = "9148";
+  public static final String s9149 = "9149";
+  public static final String s9150 = "9150";
+  public static final String s9151 = "9151";
+  public static final String s9152 = "9152";
+  public static final String s9153 = "9153";
+  public static final String s9154 = "9154";
+  public static final String s9155 = "9155";
+  public static final String s9156 = "9156";
+  public static final String s9157 = "9157";
+  public static final String s9158 = "9158";
+  public static final String s9159 = "9159";
+  public static final String s9160 = "9160";
+  public static final String s9161 = "9161";
+  public static final String s9162 = "9162";
+  public static final String s9163 = "9163";
+  public static final String s9164 = "9164";
+  public static final String s9165 = "9165";
+  public static final String s9166 = "9166";
+  public static final String s9167 = "9167";
+  public static final String s9168 = "9168";
+  public static final String s9169 = "9169";
+  public static final String s9170 = "9170";
+  public static final String s9171 = "9171";
+  public static final String s9172 = "9172";
+  public static final String s9173 = "9173";
+  public static final String s9174 = "9174";
+  public static final String s9175 = "9175";
+  public static final String s9176 = "9176";
+  public static final String s9177 = "9177";
+  public static final String s9178 = "9178";
+  public static final String s9179 = "9179";
+  public static final String s9180 = "9180";
+  public static final String s9181 = "9181";
+  public static final String s9182 = "9182";
+  public static final String s9183 = "9183";
+  public static final String s9184 = "9184";
+  public static final String s9185 = "9185";
+  public static final String s9186 = "9186";
+  public static final String s9187 = "9187";
+  public static final String s9188 = "9188";
+  public static final String s9189 = "9189";
+  public static final String s9190 = "9190";
+  public static final String s9191 = "9191";
+  public static final String s9192 = "9192";
+  public static final String s9193 = "9193";
+  public static final String s9194 = "9194";
+  public static final String s9195 = "9195";
+  public static final String s9196 = "9196";
+  public static final String s9197 = "9197";
+  public static final String s9198 = "9198";
+  public static final String s9199 = "9199";
+  public static final String s9200 = "9200";
+  public static final String s9201 = "9201";
+  public static final String s9202 = "9202";
+  public static final String s9203 = "9203";
+  public static final String s9204 = "9204";
+  public static final String s9205 = "9205";
+  public static final String s9206 = "9206";
+  public static final String s9207 = "9207";
+  public static final String s9208 = "9208";
+  public static final String s9209 = "9209";
+  public static final String s9210 = "9210";
+  public static final String s9211 = "9211";
+  public static final String s9212 = "9212";
+  public static final String s9213 = "9213";
+  public static final String s9214 = "9214";
+  public static final String s9215 = "9215";
+  public static final String s9216 = "9216";
+  public static final String s9217 = "9217";
+  public static final String s9218 = "9218";
+  public static final String s9219 = "9219";
+  public static final String s9220 = "9220";
+  public static final String s9221 = "9221";
+  public static final String s9222 = "9222";
+  public static final String s9223 = "9223";
+  public static final String s9224 = "9224";
+  public static final String s9225 = "9225";
+  public static final String s9226 = "9226";
+  public static final String s9227 = "9227";
+  public static final String s9228 = "9228";
+  public static final String s9229 = "9229";
+  public static final String s9230 = "9230";
+  public static final String s9231 = "9231";
+  public static final String s9232 = "9232";
+  public static final String s9233 = "9233";
+  public static final String s9234 = "9234";
+  public static final String s9235 = "9235";
+  public static final String s9236 = "9236";
+  public static final String s9237 = "9237";
+  public static final String s9238 = "9238";
+  public static final String s9239 = "9239";
+  public static final String s9240 = "9240";
+  public static final String s9241 = "9241";
+  public static final String s9242 = "9242";
+  public static final String s9243 = "9243";
+  public static final String s9244 = "9244";
+  public static final String s9245 = "9245";
+  public static final String s9246 = "9246";
+  public static final String s9247 = "9247";
+  public static final String s9248 = "9248";
+  public static final String s9249 = "9249";
+  public static final String s9250 = "9250";
+  public static final String s9251 = "9251";
+  public static final String s9252 = "9252";
+  public static final String s9253 = "9253";
+  public static final String s9254 = "9254";
+  public static final String s9255 = "9255";
+  public static final String s9256 = "9256";
+  public static final String s9257 = "9257";
+  public static final String s9258 = "9258";
+  public static final String s9259 = "9259";
+  public static final String s9260 = "9260";
+  public static final String s9261 = "9261";
+  public static final String s9262 = "9262";
+  public static final String s9263 = "9263";
+  public static final String s9264 = "9264";
+  public static final String s9265 = "9265";
+  public static final String s9266 = "9266";
+  public static final String s9267 = "9267";
+  public static final String s9268 = "9268";
+  public static final String s9269 = "9269";
+  public static final String s9270 = "9270";
+  public static final String s9271 = "9271";
+  public static final String s9272 = "9272";
+  public static final String s9273 = "9273";
+  public static final String s9274 = "9274";
+  public static final String s9275 = "9275";
+  public static final String s9276 = "9276";
+  public static final String s9277 = "9277";
+  public static final String s9278 = "9278";
+  public static final String s9279 = "9279";
+  public static final String s9280 = "9280";
+  public static final String s9281 = "9281";
+  public static final String s9282 = "9282";
+  public static final String s9283 = "9283";
+  public static final String s9284 = "9284";
+  public static final String s9285 = "9285";
+  public static final String s9286 = "9286";
+  public static final String s9287 = "9287";
+  public static final String s9288 = "9288";
+  public static final String s9289 = "9289";
+  public static final String s9290 = "9290";
+  public static final String s9291 = "9291";
+  public static final String s9292 = "9292";
+  public static final String s9293 = "9293";
+  public static final String s9294 = "9294";
+  public static final String s9295 = "9295";
+  public static final String s9296 = "9296";
+  public static final String s9297 = "9297";
+  public static final String s9298 = "9298";
+  public static final String s9299 = "9299";
+  public static final String s9300 = "9300";
+  public static final String s9301 = "9301";
+  public static final String s9302 = "9302";
+  public static final String s9303 = "9303";
+  public static final String s9304 = "9304";
+  public static final String s9305 = "9305";
+  public static final String s9306 = "9306";
+  public static final String s9307 = "9307";
+  public static final String s9308 = "9308";
+  public static final String s9309 = "9309";
+  public static final String s9310 = "9310";
+  public static final String s9311 = "9311";
+  public static final String s9312 = "9312";
+  public static final String s9313 = "9313";
+  public static final String s9314 = "9314";
+  public static final String s9315 = "9315";
+  public static final String s9316 = "9316";
+  public static final String s9317 = "9317";
+  public static final String s9318 = "9318";
+  public static final String s9319 = "9319";
+  public static final String s9320 = "9320";
+  public static final String s9321 = "9321";
+  public static final String s9322 = "9322";
+  public static final String s9323 = "9323";
+  public static final String s9324 = "9324";
+  public static final String s9325 = "9325";
+  public static final String s9326 = "9326";
+  public static final String s9327 = "9327";
+  public static final String s9328 = "9328";
+  public static final String s9329 = "9329";
+  public static final String s9330 = "9330";
+  public static final String s9331 = "9331";
+  public static final String s9332 = "9332";
+  public static final String s9333 = "9333";
+  public static final String s9334 = "9334";
+  public static final String s9335 = "9335";
+  public static final String s9336 = "9336";
+  public static final String s9337 = "9337";
+  public static final String s9338 = "9338";
+  public static final String s9339 = "9339";
+  public static final String s9340 = "9340";
+  public static final String s9341 = "9341";
+  public static final String s9342 = "9342";
+  public static final String s9343 = "9343";
+  public static final String s9344 = "9344";
+  public static final String s9345 = "9345";
+  public static final String s9346 = "9346";
+  public static final String s9347 = "9347";
+  public static final String s9348 = "9348";
+  public static final String s9349 = "9349";
+  public static final String s9350 = "9350";
+  public static final String s9351 = "9351";
+  public static final String s9352 = "9352";
+  public static final String s9353 = "9353";
+  public static final String s9354 = "9354";
+  public static final String s9355 = "9355";
+  public static final String s9356 = "9356";
+  public static final String s9357 = "9357";
+  public static final String s9358 = "9358";
+  public static final String s9359 = "9359";
+  public static final String s9360 = "9360";
+  public static final String s9361 = "9361";
+  public static final String s9362 = "9362";
+  public static final String s9363 = "9363";
+  public static final String s9364 = "9364";
+  public static final String s9365 = "9365";
+  public static final String s9366 = "9366";
+  public static final String s9367 = "9367";
+  public static final String s9368 = "9368";
+  public static final String s9369 = "9369";
+  public static final String s9370 = "9370";
+  public static final String s9371 = "9371";
+  public static final String s9372 = "9372";
+  public static final String s9373 = "9373";
+  public static final String s9374 = "9374";
+  public static final String s9375 = "9375";
+  public static final String s9376 = "9376";
+  public static final String s9377 = "9377";
+  public static final String s9378 = "9378";
+  public static final String s9379 = "9379";
+  public static final String s9380 = "9380";
+  public static final String s9381 = "9381";
+  public static final String s9382 = "9382";
+  public static final String s9383 = "9383";
+  public static final String s9384 = "9384";
+  public static final String s9385 = "9385";
+  public static final String s9386 = "9386";
+  public static final String s9387 = "9387";
+  public static final String s9388 = "9388";
+  public static final String s9389 = "9389";
+  public static final String s9390 = "9390";
+  public static final String s9391 = "9391";
+  public static final String s9392 = "9392";
+  public static final String s9393 = "9393";
+  public static final String s9394 = "9394";
+  public static final String s9395 = "9395";
+  public static final String s9396 = "9396";
+  public static final String s9397 = "9397";
+  public static final String s9398 = "9398";
+  public static final String s9399 = "9399";
+  public static final String s9400 = "9400";
+  public static final String s9401 = "9401";
+  public static final String s9402 = "9402";
+  public static final String s9403 = "9403";
+  public static final String s9404 = "9404";
+  public static final String s9405 = "9405";
+  public static final String s9406 = "9406";
+  public static final String s9407 = "9407";
+  public static final String s9408 = "9408";
+  public static final String s9409 = "9409";
+  public static final String s9410 = "9410";
+  public static final String s9411 = "9411";
+  public static final String s9412 = "9412";
+  public static final String s9413 = "9413";
+  public static final String s9414 = "9414";
+  public static final String s9415 = "9415";
+  public static final String s9416 = "9416";
+  public static final String s9417 = "9417";
+  public static final String s9418 = "9418";
+  public static final String s9419 = "9419";
+  public static final String s9420 = "9420";
+  public static final String s9421 = "9421";
+  public static final String s9422 = "9422";
+  public static final String s9423 = "9423";
+  public static final String s9424 = "9424";
+  public static final String s9425 = "9425";
+  public static final String s9426 = "9426";
+  public static final String s9427 = "9427";
+  public static final String s9428 = "9428";
+  public static final String s9429 = "9429";
+  public static final String s9430 = "9430";
+  public static final String s9431 = "9431";
+  public static final String s9432 = "9432";
+  public static final String s9433 = "9433";
+  public static final String s9434 = "9434";
+  public static final String s9435 = "9435";
+  public static final String s9436 = "9436";
+  public static final String s9437 = "9437";
+  public static final String s9438 = "9438";
+  public static final String s9439 = "9439";
+  public static final String s9440 = "9440";
+  public static final String s9441 = "9441";
+  public static final String s9442 = "9442";
+  public static final String s9443 = "9443";
+  public static final String s9444 = "9444";
+  public static final String s9445 = "9445";
+  public static final String s9446 = "9446";
+  public static final String s9447 = "9447";
+  public static final String s9448 = "9448";
+  public static final String s9449 = "9449";
+  public static final String s9450 = "9450";
+  public static final String s9451 = "9451";
+  public static final String s9452 = "9452";
+  public static final String s9453 = "9453";
+  public static final String s9454 = "9454";
+  public static final String s9455 = "9455";
+  public static final String s9456 = "9456";
+  public static final String s9457 = "9457";
+  public static final String s9458 = "9458";
+  public static final String s9459 = "9459";
+  public static final String s9460 = "9460";
+  public static final String s9461 = "9461";
+  public static final String s9462 = "9462";
+  public static final String s9463 = "9463";
+  public static final String s9464 = "9464";
+  public static final String s9465 = "9465";
+  public static final String s9466 = "9466";
+  public static final String s9467 = "9467";
+  public static final String s9468 = "9468";
+  public static final String s9469 = "9469";
+  public static final String s9470 = "9470";
+  public static final String s9471 = "9471";
+  public static final String s9472 = "9472";
+  public static final String s9473 = "9473";
+  public static final String s9474 = "9474";
+  public static final String s9475 = "9475";
+  public static final String s9476 = "9476";
+  public static final String s9477 = "9477";
+  public static final String s9478 = "9478";
+  public static final String s9479 = "9479";
+  public static final String s9480 = "9480";
+  public static final String s9481 = "9481";
+  public static final String s9482 = "9482";
+  public static final String s9483 = "9483";
+  public static final String s9484 = "9484";
+  public static final String s9485 = "9485";
+  public static final String s9486 = "9486";
+  public static final String s9487 = "9487";
+  public static final String s9488 = "9488";
+  public static final String s9489 = "9489";
+  public static final String s9490 = "9490";
+  public static final String s9491 = "9491";
+  public static final String s9492 = "9492";
+  public static final String s9493 = "9493";
+  public static final String s9494 = "9494";
+  public static final String s9495 = "9495";
+  public static final String s9496 = "9496";
+  public static final String s9497 = "9497";
+  public static final String s9498 = "9498";
+  public static final String s9499 = "9499";
+  public static final String s9500 = "9500";
+  public static final String s9501 = "9501";
+  public static final String s9502 = "9502";
+  public static final String s9503 = "9503";
+  public static final String s9504 = "9504";
+  public static final String s9505 = "9505";
+  public static final String s9506 = "9506";
+  public static final String s9507 = "9507";
+  public static final String s9508 = "9508";
+  public static final String s9509 = "9509";
+  public static final String s9510 = "9510";
+  public static final String s9511 = "9511";
+  public static final String s9512 = "9512";
+  public static final String s9513 = "9513";
+  public static final String s9514 = "9514";
+  public static final String s9515 = "9515";
+  public static final String s9516 = "9516";
+  public static final String s9517 = "9517";
+  public static final String s9518 = "9518";
+  public static final String s9519 = "9519";
+  public static final String s9520 = "9520";
+  public static final String s9521 = "9521";
+  public static final String s9522 = "9522";
+  public static final String s9523 = "9523";
+  public static final String s9524 = "9524";
+  public static final String s9525 = "9525";
+  public static final String s9526 = "9526";
+  public static final String s9527 = "9527";
+  public static final String s9528 = "9528";
+  public static final String s9529 = "9529";
+  public static final String s9530 = "9530";
+  public static final String s9531 = "9531";
+  public static final String s9532 = "9532";
+  public static final String s9533 = "9533";
+  public static final String s9534 = "9534";
+  public static final String s9535 = "9535";
+  public static final String s9536 = "9536";
+  public static final String s9537 = "9537";
+  public static final String s9538 = "9538";
+  public static final String s9539 = "9539";
+  public static final String s9540 = "9540";
+  public static final String s9541 = "9541";
+  public static final String s9542 = "9542";
+  public static final String s9543 = "9543";
+  public static final String s9544 = "9544";
+  public static final String s9545 = "9545";
+  public static final String s9546 = "9546";
+  public static final String s9547 = "9547";
+  public static final String s9548 = "9548";
+  public static final String s9549 = "9549";
+  public static final String s9550 = "9550";
+  public static final String s9551 = "9551";
+  public static final String s9552 = "9552";
+  public static final String s9553 = "9553";
+  public static final String s9554 = "9554";
+  public static final String s9555 = "9555";
+  public static final String s9556 = "9556";
+  public static final String s9557 = "9557";
+  public static final String s9558 = "9558";
+  public static final String s9559 = "9559";
+  public static final String s9560 = "9560";
+  public static final String s9561 = "9561";
+  public static final String s9562 = "9562";
+  public static final String s9563 = "9563";
+  public static final String s9564 = "9564";
+  public static final String s9565 = "9565";
+  public static final String s9566 = "9566";
+  public static final String s9567 = "9567";
+  public static final String s9568 = "9568";
+  public static final String s9569 = "9569";
+  public static final String s9570 = "9570";
+  public static final String s9571 = "9571";
+  public static final String s9572 = "9572";
+  public static final String s9573 = "9573";
+  public static final String s9574 = "9574";
+  public static final String s9575 = "9575";
+  public static final String s9576 = "9576";
+  public static final String s9577 = "9577";
+  public static final String s9578 = "9578";
+  public static final String s9579 = "9579";
+  public static final String s9580 = "9580";
+  public static final String s9581 = "9581";
+  public static final String s9582 = "9582";
+  public static final String s9583 = "9583";
+  public static final String s9584 = "9584";
+  public static final String s9585 = "9585";
+  public static final String s9586 = "9586";
+  public static final String s9587 = "9587";
+  public static final String s9588 = "9588";
+  public static final String s9589 = "9589";
+  public static final String s9590 = "9590";
+  public static final String s9591 = "9591";
+  public static final String s9592 = "9592";
+  public static final String s9593 = "9593";
+  public static final String s9594 = "9594";
+  public static final String s9595 = "9595";
+  public static final String s9596 = "9596";
+  public static final String s9597 = "9597";
+  public static final String s9598 = "9598";
+  public static final String s9599 = "9599";
+  public static final String s9600 = "9600";
+  public static final String s9601 = "9601";
+  public static final String s9602 = "9602";
+  public static final String s9603 = "9603";
+  public static final String s9604 = "9604";
+  public static final String s9605 = "9605";
+  public static final String s9606 = "9606";
+  public static final String s9607 = "9607";
+  public static final String s9608 = "9608";
+  public static final String s9609 = "9609";
+  public static final String s9610 = "9610";
+  public static final String s9611 = "9611";
+  public static final String s9612 = "9612";
+  public static final String s9613 = "9613";
+  public static final String s9614 = "9614";
+  public static final String s9615 = "9615";
+  public static final String s9616 = "9616";
+  public static final String s9617 = "9617";
+  public static final String s9618 = "9618";
+  public static final String s9619 = "9619";
+  public static final String s9620 = "9620";
+  public static final String s9621 = "9621";
+  public static final String s9622 = "9622";
+  public static final String s9623 = "9623";
+  public static final String s9624 = "9624";
+  public static final String s9625 = "9625";
+  public static final String s9626 = "9626";
+  public static final String s9627 = "9627";
+  public static final String s9628 = "9628";
+  public static final String s9629 = "9629";
+  public static final String s9630 = "9630";
+  public static final String s9631 = "9631";
+  public static final String s9632 = "9632";
+  public static final String s9633 = "9633";
+  public static final String s9634 = "9634";
+  public static final String s9635 = "9635";
+  public static final String s9636 = "9636";
+  public static final String s9637 = "9637";
+  public static final String s9638 = "9638";
+  public static final String s9639 = "9639";
+  public static final String s9640 = "9640";
+  public static final String s9641 = "9641";
+  public static final String s9642 = "9642";
+  public static final String s9643 = "9643";
+  public static final String s9644 = "9644";
+  public static final String s9645 = "9645";
+  public static final String s9646 = "9646";
+  public static final String s9647 = "9647";
+  public static final String s9648 = "9648";
+  public static final String s9649 = "9649";
+  public static final String s9650 = "9650";
+  public static final String s9651 = "9651";
+  public static final String s9652 = "9652";
+  public static final String s9653 = "9653";
+  public static final String s9654 = "9654";
+  public static final String s9655 = "9655";
+  public static final String s9656 = "9656";
+  public static final String s9657 = "9657";
+  public static final String s9658 = "9658";
+  public static final String s9659 = "9659";
+  public static final String s9660 = "9660";
+  public static final String s9661 = "9661";
+  public static final String s9662 = "9662";
+  public static final String s9663 = "9663";
+  public static final String s9664 = "9664";
+  public static final String s9665 = "9665";
+  public static final String s9666 = "9666";
+  public static final String s9667 = "9667";
+  public static final String s9668 = "9668";
+  public static final String s9669 = "9669";
+  public static final String s9670 = "9670";
+  public static final String s9671 = "9671";
+  public static final String s9672 = "9672";
+  public static final String s9673 = "9673";
+  public static final String s9674 = "9674";
+  public static final String s9675 = "9675";
+  public static final String s9676 = "9676";
+  public static final String s9677 = "9677";
+  public static final String s9678 = "9678";
+  public static final String s9679 = "9679";
+  public static final String s9680 = "9680";
+  public static final String s9681 = "9681";
+  public static final String s9682 = "9682";
+  public static final String s9683 = "9683";
+  public static final String s9684 = "9684";
+  public static final String s9685 = "9685";
+  public static final String s9686 = "9686";
+  public static final String s9687 = "9687";
+  public static final String s9688 = "9688";
+  public static final String s9689 = "9689";
+  public static final String s9690 = "9690";
+  public static final String s9691 = "9691";
+  public static final String s9692 = "9692";
+  public static final String s9693 = "9693";
+  public static final String s9694 = "9694";
+  public static final String s9695 = "9695";
+  public static final String s9696 = "9696";
+  public static final String s9697 = "9697";
+  public static final String s9698 = "9698";
+  public static final String s9699 = "9699";
+  public static final String s9700 = "9700";
+  public static final String s9701 = "9701";
+  public static final String s9702 = "9702";
+  public static final String s9703 = "9703";
+  public static final String s9704 = "9704";
+  public static final String s9705 = "9705";
+  public static final String s9706 = "9706";
+  public static final String s9707 = "9707";
+  public static final String s9708 = "9708";
+  public static final String s9709 = "9709";
+  public static final String s9710 = "9710";
+  public static final String s9711 = "9711";
+  public static final String s9712 = "9712";
+  public static final String s9713 = "9713";
+  public static final String s9714 = "9714";
+  public static final String s9715 = "9715";
+  public static final String s9716 = "9716";
+  public static final String s9717 = "9717";
+  public static final String s9718 = "9718";
+  public static final String s9719 = "9719";
+  public static final String s9720 = "9720";
+  public static final String s9721 = "9721";
+  public static final String s9722 = "9722";
+  public static final String s9723 = "9723";
+  public static final String s9724 = "9724";
+  public static final String s9725 = "9725";
+  public static final String s9726 = "9726";
+  public static final String s9727 = "9727";
+  public static final String s9728 = "9728";
+  public static final String s9729 = "9729";
+  public static final String s9730 = "9730";
+  public static final String s9731 = "9731";
+  public static final String s9732 = "9732";
+  public static final String s9733 = "9733";
+  public static final String s9734 = "9734";
+  public static final String s9735 = "9735";
+  public static final String s9736 = "9736";
+  public static final String s9737 = "9737";
+  public static final String s9738 = "9738";
+  public static final String s9739 = "9739";
+  public static final String s9740 = "9740";
+  public static final String s9741 = "9741";
+  public static final String s9742 = "9742";
+  public static final String s9743 = "9743";
+  public static final String s9744 = "9744";
+  public static final String s9745 = "9745";
+  public static final String s9746 = "9746";
+  public static final String s9747 = "9747";
+  public static final String s9748 = "9748";
+  public static final String s9749 = "9749";
+  public static final String s9750 = "9750";
+  public static final String s9751 = "9751";
+  public static final String s9752 = "9752";
+  public static final String s9753 = "9753";
+  public static final String s9754 = "9754";
+  public static final String s9755 = "9755";
+  public static final String s9756 = "9756";
+  public static final String s9757 = "9757";
+  public static final String s9758 = "9758";
+  public static final String s9759 = "9759";
+  public static final String s9760 = "9760";
+  public static final String s9761 = "9761";
+  public static final String s9762 = "9762";
+  public static final String s9763 = "9763";
+  public static final String s9764 = "9764";
+  public static final String s9765 = "9765";
+  public static final String s9766 = "9766";
+  public static final String s9767 = "9767";
+  public static final String s9768 = "9768";
+  public static final String s9769 = "9769";
+  public static final String s9770 = "9770";
+  public static final String s9771 = "9771";
+  public static final String s9772 = "9772";
+  public static final String s9773 = "9773";
+  public static final String s9774 = "9774";
+  public static final String s9775 = "9775";
+  public static final String s9776 = "9776";
+  public static final String s9777 = "9777";
+  public static final String s9778 = "9778";
+  public static final String s9779 = "9779";
+  public static final String s9780 = "9780";
+  public static final String s9781 = "9781";
+  public static final String s9782 = "9782";
+  public static final String s9783 = "9783";
+  public static final String s9784 = "9784";
+  public static final String s9785 = "9785";
+  public static final String s9786 = "9786";
+  public static final String s9787 = "9787";
+  public static final String s9788 = "9788";
+  public static final String s9789 = "9789";
+  public static final String s9790 = "9790";
+  public static final String s9791 = "9791";
+  public static final String s9792 = "9792";
+  public static final String s9793 = "9793";
+  public static final String s9794 = "9794";
+  public static final String s9795 = "9795";
+  public static final String s9796 = "9796";
+  public static final String s9797 = "9797";
+  public static final String s9798 = "9798";
+  public static final String s9799 = "9799";
+  public static final String s9800 = "9800";
+  public static final String s9801 = "9801";
+  public static final String s9802 = "9802";
+  public static final String s9803 = "9803";
+  public static final String s9804 = "9804";
+  public static final String s9805 = "9805";
+  public static final String s9806 = "9806";
+  public static final String s9807 = "9807";
+  public static final String s9808 = "9808";
+  public static final String s9809 = "9809";
+  public static final String s9810 = "9810";
+  public static final String s9811 = "9811";
+  public static final String s9812 = "9812";
+  public static final String s9813 = "9813";
+  public static final String s9814 = "9814";
+  public static final String s9815 = "9815";
+  public static final String s9816 = "9816";
+  public static final String s9817 = "9817";
+  public static final String s9818 = "9818";
+  public static final String s9819 = "9819";
+  public static final String s9820 = "9820";
+  public static final String s9821 = "9821";
+  public static final String s9822 = "9822";
+  public static final String s9823 = "9823";
+  public static final String s9824 = "9824";
+  public static final String s9825 = "9825";
+  public static final String s9826 = "9826";
+  public static final String s9827 = "9827";
+  public static final String s9828 = "9828";
+  public static final String s9829 = "9829";
+  public static final String s9830 = "9830";
+  public static final String s9831 = "9831";
+  public static final String s9832 = "9832";
+  public static final String s9833 = "9833";
+  public static final String s9834 = "9834";
+  public static final String s9835 = "9835";
+  public static final String s9836 = "9836";
+  public static final String s9837 = "9837";
+  public static final String s9838 = "9838";
+  public static final String s9839 = "9839";
+  public static final String s9840 = "9840";
+  public static final String s9841 = "9841";
+  public static final String s9842 = "9842";
+  public static final String s9843 = "9843";
+  public static final String s9844 = "9844";
+  public static final String s9845 = "9845";
+  public static final String s9846 = "9846";
+  public static final String s9847 = "9847";
+  public static final String s9848 = "9848";
+  public static final String s9849 = "9849";
+  public static final String s9850 = "9850";
+  public static final String s9851 = "9851";
+  public static final String s9852 = "9852";
+  public static final String s9853 = "9853";
+  public static final String s9854 = "9854";
+  public static final String s9855 = "9855";
+  public static final String s9856 = "9856";
+  public static final String s9857 = "9857";
+  public static final String s9858 = "9858";
+  public static final String s9859 = "9859";
+  public static final String s9860 = "9860";
+  public static final String s9861 = "9861";
+  public static final String s9862 = "9862";
+  public static final String s9863 = "9863";
+  public static final String s9864 = "9864";
+  public static final String s9865 = "9865";
+  public static final String s9866 = "9866";
+  public static final String s9867 = "9867";
+  public static final String s9868 = "9868";
+  public static final String s9869 = "9869";
+  public static final String s9870 = "9870";
+  public static final String s9871 = "9871";
+  public static final String s9872 = "9872";
+  public static final String s9873 = "9873";
+  public static final String s9874 = "9874";
+  public static final String s9875 = "9875";
+  public static final String s9876 = "9876";
+  public static final String s9877 = "9877";
+  public static final String s9878 = "9878";
+  public static final String s9879 = "9879";
+  public static final String s9880 = "9880";
+  public static final String s9881 = "9881";
+  public static final String s9882 = "9882";
+  public static final String s9883 = "9883";
+  public static final String s9884 = "9884";
+  public static final String s9885 = "9885";
+  public static final String s9886 = "9886";
+  public static final String s9887 = "9887";
+  public static final String s9888 = "9888";
+  public static final String s9889 = "9889";
+  public static final String s9890 = "9890";
+  public static final String s9891 = "9891";
+  public static final String s9892 = "9892";
+  public static final String s9893 = "9893";
+  public static final String s9894 = "9894";
+  public static final String s9895 = "9895";
+  public static final String s9896 = "9896";
+  public static final String s9897 = "9897";
+  public static final String s9898 = "9898";
+  public static final String s9899 = "9899";
+  public static final String s9900 = "9900";
+  public static final String s9901 = "9901";
+  public static final String s9902 = "9902";
+  public static final String s9903 = "9903";
+  public static final String s9904 = "9904";
+  public static final String s9905 = "9905";
+  public static final String s9906 = "9906";
+  public static final String s9907 = "9907";
+  public static final String s9908 = "9908";
+  public static final String s9909 = "9909";
+  public static final String s9910 = "9910";
+  public static final String s9911 = "9911";
+  public static final String s9912 = "9912";
+  public static final String s9913 = "9913";
+  public static final String s9914 = "9914";
+  public static final String s9915 = "9915";
+  public static final String s9916 = "9916";
+  public static final String s9917 = "9917";
+  public static final String s9918 = "9918";
+  public static final String s9919 = "9919";
+  public static final String s9920 = "9920";
+  public static final String s9921 = "9921";
+  public static final String s9922 = "9922";
+  public static final String s9923 = "9923";
+  public static final String s9924 = "9924";
+  public static final String s9925 = "9925";
+  public static final String s9926 = "9926";
+  public static final String s9927 = "9927";
+  public static final String s9928 = "9928";
+  public static final String s9929 = "9929";
+  public static final String s9930 = "9930";
+  public static final String s9931 = "9931";
+  public static final String s9932 = "9932";
+  public static final String s9933 = "9933";
+  public static final String s9934 = "9934";
+  public static final String s9935 = "9935";
+  public static final String s9936 = "9936";
+  public static final String s9937 = "9937";
+  public static final String s9938 = "9938";
+  public static final String s9939 = "9939";
+  public static final String s9940 = "9940";
+  public static final String s9941 = "9941";
+  public static final String s9942 = "9942";
+  public static final String s9943 = "9943";
+  public static final String s9944 = "9944";
+  public static final String s9945 = "9945";
+  public static final String s9946 = "9946";
+  public static final String s9947 = "9947";
+  public static final String s9948 = "9948";
+  public static final String s9949 = "9949";
+  public static final String s9950 = "9950";
+  public static final String s9951 = "9951";
+  public static final String s9952 = "9952";
+  public static final String s9953 = "9953";
+  public static final String s9954 = "9954";
+  public static final String s9955 = "9955";
+  public static final String s9956 = "9956";
+  public static final String s9957 = "9957";
+  public static final String s9958 = "9958";
+  public static final String s9959 = "9959";
+  public static final String s9960 = "9960";
+  public static final String s9961 = "9961";
+  public static final String s9962 = "9962";
+  public static final String s9963 = "9963";
+  public static final String s9964 = "9964";
+  public static final String s9965 = "9965";
+  public static final String s9966 = "9966";
+  public static final String s9967 = "9967";
+  public static final String s9968 = "9968";
+  public static final String s9969 = "9969";
+  public static final String s9970 = "9970";
+  public static final String s9971 = "9971";
+  public static final String s9972 = "9972";
+  public static final String s9973 = "9973";
+  public static final String s9974 = "9974";
+  public static final String s9975 = "9975";
+  public static final String s9976 = "9976";
+  public static final String s9977 = "9977";
+  public static final String s9978 = "9978";
+  public static final String s9979 = "9979";
+  public static final String s9980 = "9980";
+  public static final String s9981 = "9981";
+  public static final String s9982 = "9982";
+  public static final String s9983 = "9983";
+  public static final String s9984 = "9984";
+  public static final String s9985 = "9985";
+  public static final String s9986 = "9986";
+  public static final String s9987 = "9987";
+  public static final String s9988 = "9988";
+  public static final String s9989 = "9989";
+  public static final String s9990 = "9990";
+  public static final String s9991 = "9991";
+  public static final String s9992 = "9992";
+  public static final String s9993 = "9993";
+  public static final String s9994 = "9994";
+  public static final String s9995 = "9995";
+  public static final String s9996 = "9996";
+  public static final String s9997 = "9997";
+  public static final String s9998 = "9998";
+  public static final String s9999 = "9999";
+  public static final String s10000 = "10000";
+  public static final String s10001 = "10001";
+  public static final String s10002 = "10002";
+  public static final String s10003 = "10003";
+  public static final String s10004 = "10004";
+  public static final String s10005 = "10005";
+  public static final String s10006 = "10006";
+  public static final String s10007 = "10007";
+  public static final String s10008 = "10008";
+  public static final String s10009 = "10009";
+  public static final String s10010 = "10010";
+  public static final String s10011 = "10011";
+  public static final String s10012 = "10012";
+  public static final String s10013 = "10013";
+  public static final String s10014 = "10014";
+  public static final String s10015 = "10015";
+  public static final String s10016 = "10016";
+  public static final String s10017 = "10017";
+  public static final String s10018 = "10018";
+  public static final String s10019 = "10019";
+  public static final String s10020 = "10020";
+  public static final String s10021 = "10021";
+  public static final String s10022 = "10022";
+  public static final String s10023 = "10023";
+  public static final String s10024 = "10024";
+  public static final String s10025 = "10025";
+  public static final String s10026 = "10026";
+  public static final String s10027 = "10027";
+  public static final String s10028 = "10028";
+  public static final String s10029 = "10029";
+  public static final String s10030 = "10030";
+  public static final String s10031 = "10031";
+  public static final String s10032 = "10032";
+  public static final String s10033 = "10033";
+  public static final String s10034 = "10034";
+  public static final String s10035 = "10035";
+  public static final String s10036 = "10036";
+  public static final String s10037 = "10037";
+  public static final String s10038 = "10038";
+  public static final String s10039 = "10039";
+  public static final String s10040 = "10040";
+  public static final String s10041 = "10041";
+  public static final String s10042 = "10042";
+  public static final String s10043 = "10043";
+  public static final String s10044 = "10044";
+  public static final String s10045 = "10045";
+  public static final String s10046 = "10046";
+  public static final String s10047 = "10047";
+  public static final String s10048 = "10048";
+  public static final String s10049 = "10049";
+  public static final String s10050 = "10050";
+  public static final String s10051 = "10051";
+  public static final String s10052 = "10052";
+  public static final String s10053 = "10053";
+  public static final String s10054 = "10054";
+  public static final String s10055 = "10055";
+  public static final String s10056 = "10056";
+  public static final String s10057 = "10057";
+  public static final String s10058 = "10058";
+  public static final String s10059 = "10059";
+  public static final String s10060 = "10060";
+  public static final String s10061 = "10061";
+  public static final String s10062 = "10062";
+  public static final String s10063 = "10063";
+  public static final String s10064 = "10064";
+  public static final String s10065 = "10065";
+  public static final String s10066 = "10066";
+  public static final String s10067 = "10067";
+  public static final String s10068 = "10068";
+  public static final String s10069 = "10069";
+  public static final String s10070 = "10070";
+  public static final String s10071 = "10071";
+  public static final String s10072 = "10072";
+  public static final String s10073 = "10073";
+  public static final String s10074 = "10074";
+  public static final String s10075 = "10075";
+  public static final String s10076 = "10076";
+  public static final String s10077 = "10077";
+  public static final String s10078 = "10078";
+  public static final String s10079 = "10079";
+  public static final String s10080 = "10080";
+  public static final String s10081 = "10081";
+  public static final String s10082 = "10082";
+  public static final String s10083 = "10083";
+  public static final String s10084 = "10084";
+  public static final String s10085 = "10085";
+  public static final String s10086 = "10086";
+  public static final String s10087 = "10087";
+  public static final String s10088 = "10088";
+  public static final String s10089 = "10089";
+  public static final String s10090 = "10090";
+  public static final String s10091 = "10091";
+  public static final String s10092 = "10092";
+  public static final String s10093 = "10093";
+  public static final String s10094 = "10094";
+  public static final String s10095 = "10095";
+  public static final String s10096 = "10096";
+  public static final String s10097 = "10097";
+  public static final String s10098 = "10098";
+  public static final String s10099 = "10099";
+  public static final String s10100 = "10100";
+  public static final String s10101 = "10101";
+  public static final String s10102 = "10102";
+  public static final String s10103 = "10103";
+  public static final String s10104 = "10104";
+  public static final String s10105 = "10105";
+  public static final String s10106 = "10106";
+  public static final String s10107 = "10107";
+  public static final String s10108 = "10108";
+  public static final String s10109 = "10109";
+  public static final String s10110 = "10110";
+  public static final String s10111 = "10111";
+  public static final String s10112 = "10112";
+  public static final String s10113 = "10113";
+  public static final String s10114 = "10114";
+  public static final String s10115 = "10115";
+  public static final String s10116 = "10116";
+  public static final String s10117 = "10117";
+  public static final String s10118 = "10118";
+  public static final String s10119 = "10119";
+  public static final String s10120 = "10120";
+  public static final String s10121 = "10121";
+  public static final String s10122 = "10122";
+  public static final String s10123 = "10123";
+  public static final String s10124 = "10124";
+  public static final String s10125 = "10125";
+  public static final String s10126 = "10126";
+  public static final String s10127 = "10127";
+  public static final String s10128 = "10128";
+  public static final String s10129 = "10129";
+  public static final String s10130 = "10130";
+  public static final String s10131 = "10131";
+  public static final String s10132 = "10132";
+  public static final String s10133 = "10133";
+  public static final String s10134 = "10134";
+  public static final String s10135 = "10135";
+  public static final String s10136 = "10136";
+  public static final String s10137 = "10137";
+  public static final String s10138 = "10138";
+  public static final String s10139 = "10139";
+  public static final String s10140 = "10140";
+  public static final String s10141 = "10141";
+  public static final String s10142 = "10142";
+  public static final String s10143 = "10143";
+  public static final String s10144 = "10144";
+  public static final String s10145 = "10145";
+  public static final String s10146 = "10146";
+  public static final String s10147 = "10147";
+  public static final String s10148 = "10148";
+  public static final String s10149 = "10149";
+  public static final String s10150 = "10150";
+  public static final String s10151 = "10151";
+  public static final String s10152 = "10152";
+  public static final String s10153 = "10153";
+  public static final String s10154 = "10154";
+  public static final String s10155 = "10155";
+  public static final String s10156 = "10156";
+  public static final String s10157 = "10157";
+  public static final String s10158 = "10158";
+  public static final String s10159 = "10159";
+  public static final String s10160 = "10160";
+  public static final String s10161 = "10161";
+  public static final String s10162 = "10162";
+  public static final String s10163 = "10163";
+  public static final String s10164 = "10164";
+  public static final String s10165 = "10165";
+  public static final String s10166 = "10166";
+  public static final String s10167 = "10167";
+  public static final String s10168 = "10168";
+  public static final String s10169 = "10169";
+  public static final String s10170 = "10170";
+  public static final String s10171 = "10171";
+  public static final String s10172 = "10172";
+  public static final String s10173 = "10173";
+  public static final String s10174 = "10174";
+  public static final String s10175 = "10175";
+  public static final String s10176 = "10176";
+  public static final String s10177 = "10177";
+  public static final String s10178 = "10178";
+  public static final String s10179 = "10179";
+  public static final String s10180 = "10180";
+  public static final String s10181 = "10181";
+  public static final String s10182 = "10182";
+  public static final String s10183 = "10183";
+  public static final String s10184 = "10184";
+  public static final String s10185 = "10185";
+  public static final String s10186 = "10186";
+  public static final String s10187 = "10187";
+  public static final String s10188 = "10188";
+  public static final String s10189 = "10189";
+  public static final String s10190 = "10190";
+  public static final String s10191 = "10191";
+  public static final String s10192 = "10192";
+  public static final String s10193 = "10193";
+  public static final String s10194 = "10194";
+  public static final String s10195 = "10195";
+  public static final String s10196 = "10196";
+  public static final String s10197 = "10197";
+  public static final String s10198 = "10198";
+  public static final String s10199 = "10199";
+  public static final String s10200 = "10200";
+  public static final String s10201 = "10201";
+  public static final String s10202 = "10202";
+  public static final String s10203 = "10203";
+  public static final String s10204 = "10204";
+  public static final String s10205 = "10205";
+  public static final String s10206 = "10206";
+  public static final String s10207 = "10207";
+  public static final String s10208 = "10208";
+  public static final String s10209 = "10209";
+  public static final String s10210 = "10210";
+  public static final String s10211 = "10211";
+  public static final String s10212 = "10212";
+  public static final String s10213 = "10213";
+  public static final String s10214 = "10214";
+  public static final String s10215 = "10215";
+  public static final String s10216 = "10216";
+  public static final String s10217 = "10217";
+  public static final String s10218 = "10218";
+  public static final String s10219 = "10219";
+  public static final String s10220 = "10220";
+  public static final String s10221 = "10221";
+  public static final String s10222 = "10222";
+  public static final String s10223 = "10223";
+  public static final String s10224 = "10224";
+  public static final String s10225 = "10225";
+  public static final String s10226 = "10226";
+  public static final String s10227 = "10227";
+  public static final String s10228 = "10228";
+  public static final String s10229 = "10229";
+  public static final String s10230 = "10230";
+  public static final String s10231 = "10231";
+  public static final String s10232 = "10232";
+  public static final String s10233 = "10233";
+  public static final String s10234 = "10234";
+  public static final String s10235 = "10235";
+  public static final String s10236 = "10236";
+  public static final String s10237 = "10237";
+  public static final String s10238 = "10238";
+  public static final String s10239 = "10239";
+  public static final String s10240 = "10240";
+  public static final String s10241 = "10241";
+  public static final String s10242 = "10242";
+  public static final String s10243 = "10243";
+  public static final String s10244 = "10244";
+  public static final String s10245 = "10245";
+  public static final String s10246 = "10246";
+  public static final String s10247 = "10247";
+  public static final String s10248 = "10248";
+  public static final String s10249 = "10249";
+  public static final String s10250 = "10250";
+  public static final String s10251 = "10251";
+  public static final String s10252 = "10252";
+  public static final String s10253 = "10253";
+  public static final String s10254 = "10254";
+  public static final String s10255 = "10255";
+  public static final String s10256 = "10256";
+  public static final String s10257 = "10257";
+  public static final String s10258 = "10258";
+  public static final String s10259 = "10259";
+  public static final String s10260 = "10260";
+  public static final String s10261 = "10261";
+  public static final String s10262 = "10262";
+  public static final String s10263 = "10263";
+  public static final String s10264 = "10264";
+  public static final String s10265 = "10265";
+  public static final String s10266 = "10266";
+  public static final String s10267 = "10267";
+  public static final String s10268 = "10268";
+  public static final String s10269 = "10269";
+  public static final String s10270 = "10270";
+  public static final String s10271 = "10271";
+  public static final String s10272 = "10272";
+  public static final String s10273 = "10273";
+  public static final String s10274 = "10274";
+  public static final String s10275 = "10275";
+  public static final String s10276 = "10276";
+  public static final String s10277 = "10277";
+  public static final String s10278 = "10278";
+  public static final String s10279 = "10279";
+  public static final String s10280 = "10280";
+  public static final String s10281 = "10281";
+  public static final String s10282 = "10282";
+  public static final String s10283 = "10283";
+  public static final String s10284 = "10284";
+  public static final String s10285 = "10285";
+  public static final String s10286 = "10286";
+  public static final String s10287 = "10287";
+  public static final String s10288 = "10288";
+  public static final String s10289 = "10289";
+  public static final String s10290 = "10290";
+  public static final String s10291 = "10291";
+  public static final String s10292 = "10292";
+  public static final String s10293 = "10293";
+  public static final String s10294 = "10294";
+  public static final String s10295 = "10295";
+  public static final String s10296 = "10296";
+  public static final String s10297 = "10297";
+  public static final String s10298 = "10298";
+  public static final String s10299 = "10299";
+  public static final String s10300 = "10300";
+  public static final String s10301 = "10301";
+  public static final String s10302 = "10302";
+  public static final String s10303 = "10303";
+  public static final String s10304 = "10304";
+  public static final String s10305 = "10305";
+  public static final String s10306 = "10306";
+  public static final String s10307 = "10307";
+  public static final String s10308 = "10308";
+  public static final String s10309 = "10309";
+  public static final String s10310 = "10310";
+  public static final String s10311 = "10311";
+  public static final String s10312 = "10312";
+  public static final String s10313 = "10313";
+  public static final String s10314 = "10314";
+  public static final String s10315 = "10315";
+  public static final String s10316 = "10316";
+  public static final String s10317 = "10317";
+  public static final String s10318 = "10318";
+  public static final String s10319 = "10319";
+  public static final String s10320 = "10320";
+  public static final String s10321 = "10321";
+  public static final String s10322 = "10322";
+  public static final String s10323 = "10323";
+  public static final String s10324 = "10324";
+  public static final String s10325 = "10325";
+  public static final String s10326 = "10326";
+  public static final String s10327 = "10327";
+  public static final String s10328 = "10328";
+  public static final String s10329 = "10329";
+  public static final String s10330 = "10330";
+  public static final String s10331 = "10331";
+  public static final String s10332 = "10332";
+  public static final String s10333 = "10333";
+  public static final String s10334 = "10334";
+  public static final String s10335 = "10335";
+  public static final String s10336 = "10336";
+  public static final String s10337 = "10337";
+  public static final String s10338 = "10338";
+  public static final String s10339 = "10339";
+  public static final String s10340 = "10340";
+  public static final String s10341 = "10341";
+  public static final String s10342 = "10342";
+  public static final String s10343 = "10343";
+  public static final String s10344 = "10344";
+  public static final String s10345 = "10345";
+  public static final String s10346 = "10346";
+  public static final String s10347 = "10347";
+  public static final String s10348 = "10348";
+  public static final String s10349 = "10349";
+  public static final String s10350 = "10350";
+  public static final String s10351 = "10351";
+  public static final String s10352 = "10352";
+  public static final String s10353 = "10353";
+  public static final String s10354 = "10354";
+  public static final String s10355 = "10355";
+  public static final String s10356 = "10356";
+  public static final String s10357 = "10357";
+  public static final String s10358 = "10358";
+  public static final String s10359 = "10359";
+  public static final String s10360 = "10360";
+  public static final String s10361 = "10361";
+  public static final String s10362 = "10362";
+  public static final String s10363 = "10363";
+  public static final String s10364 = "10364";
+  public static final String s10365 = "10365";
+  public static final String s10366 = "10366";
+  public static final String s10367 = "10367";
+  public static final String s10368 = "10368";
+  public static final String s10369 = "10369";
+  public static final String s10370 = "10370";
+  public static final String s10371 = "10371";
+  public static final String s10372 = "10372";
+  public static final String s10373 = "10373";
+  public static final String s10374 = "10374";
+  public static final String s10375 = "10375";
+  public static final String s10376 = "10376";
+  public static final String s10377 = "10377";
+  public static final String s10378 = "10378";
+  public static final String s10379 = "10379";
+  public static final String s10380 = "10380";
+  public static final String s10381 = "10381";
+  public static final String s10382 = "10382";
+  public static final String s10383 = "10383";
+  public static final String s10384 = "10384";
+  public static final String s10385 = "10385";
+  public static final String s10386 = "10386";
+  public static final String s10387 = "10387";
+  public static final String s10388 = "10388";
+  public static final String s10389 = "10389";
+  public static final String s10390 = "10390";
+  public static final String s10391 = "10391";
+  public static final String s10392 = "10392";
+  public static final String s10393 = "10393";
+  public static final String s10394 = "10394";
+  public static final String s10395 = "10395";
+  public static final String s10396 = "10396";
+  public static final String s10397 = "10397";
+  public static final String s10398 = "10398";
+  public static final String s10399 = "10399";
+  public static final String s10400 = "10400";
+  public static final String s10401 = "10401";
+  public static final String s10402 = "10402";
+  public static final String s10403 = "10403";
+  public static final String s10404 = "10404";
+  public static final String s10405 = "10405";
+  public static final String s10406 = "10406";
+  public static final String s10407 = "10407";
+  public static final String s10408 = "10408";
+  public static final String s10409 = "10409";
+  public static final String s10410 = "10410";
+  public static final String s10411 = "10411";
+  public static final String s10412 = "10412";
+  public static final String s10413 = "10413";
+  public static final String s10414 = "10414";
+  public static final String s10415 = "10415";
+  public static final String s10416 = "10416";
+  public static final String s10417 = "10417";
+  public static final String s10418 = "10418";
+  public static final String s10419 = "10419";
+  public static final String s10420 = "10420";
+  public static final String s10421 = "10421";
+  public static final String s10422 = "10422";
+  public static final String s10423 = "10423";
+  public static final String s10424 = "10424";
+  public static final String s10425 = "10425";
+  public static final String s10426 = "10426";
+  public static final String s10427 = "10427";
+  public static final String s10428 = "10428";
+  public static final String s10429 = "10429";
+  public static final String s10430 = "10430";
+  public static final String s10431 = "10431";
+  public static final String s10432 = "10432";
+  public static final String s10433 = "10433";
+  public static final String s10434 = "10434";
+  public static final String s10435 = "10435";
+  public static final String s10436 = "10436";
+  public static final String s10437 = "10437";
+  public static final String s10438 = "10438";
+  public static final String s10439 = "10439";
+  public static final String s10440 = "10440";
+  public static final String s10441 = "10441";
+  public static final String s10442 = "10442";
+  public static final String s10443 = "10443";
+  public static final String s10444 = "10444";
+  public static final String s10445 = "10445";
+  public static final String s10446 = "10446";
+  public static final String s10447 = "10447";
+  public static final String s10448 = "10448";
+  public static final String s10449 = "10449";
+  public static final String s10450 = "10450";
+  public static final String s10451 = "10451";
+  public static final String s10452 = "10452";
+  public static final String s10453 = "10453";
+  public static final String s10454 = "10454";
+  public static final String s10455 = "10455";
+  public static final String s10456 = "10456";
+  public static final String s10457 = "10457";
+  public static final String s10458 = "10458";
+  public static final String s10459 = "10459";
+  public static final String s10460 = "10460";
+  public static final String s10461 = "10461";
+  public static final String s10462 = "10462";
+  public static final String s10463 = "10463";
+  public static final String s10464 = "10464";
+  public static final String s10465 = "10465";
+  public static final String s10466 = "10466";
+  public static final String s10467 = "10467";
+  public static final String s10468 = "10468";
+  public static final String s10469 = "10469";
+  public static final String s10470 = "10470";
+  public static final String s10471 = "10471";
+  public static final String s10472 = "10472";
+  public static final String s10473 = "10473";
+  public static final String s10474 = "10474";
+  public static final String s10475 = "10475";
+  public static final String s10476 = "10476";
+  public static final String s10477 = "10477";
+  public static final String s10478 = "10478";
+  public static final String s10479 = "10479";
+  public static final String s10480 = "10480";
+  public static final String s10481 = "10481";
+  public static final String s10482 = "10482";
+  public static final String s10483 = "10483";
+  public static final String s10484 = "10484";
+  public static final String s10485 = "10485";
+  public static final String s10486 = "10486";
+  public static final String s10487 = "10487";
+  public static final String s10488 = "10488";
+  public static final String s10489 = "10489";
+  public static final String s10490 = "10490";
+  public static final String s10491 = "10491";
+  public static final String s10492 = "10492";
+  public static final String s10493 = "10493";
+  public static final String s10494 = "10494";
+  public static final String s10495 = "10495";
+  public static final String s10496 = "10496";
+  public static final String s10497 = "10497";
+  public static final String s10498 = "10498";
+  public static final String s10499 = "10499";
+  public static final String s10500 = "10500";
+  public static final String s10501 = "10501";
+  public static final String s10502 = "10502";
+  public static final String s10503 = "10503";
+  public static final String s10504 = "10504";
+  public static final String s10505 = "10505";
+  public static final String s10506 = "10506";
+  public static final String s10507 = "10507";
+  public static final String s10508 = "10508";
+  public static final String s10509 = "10509";
+  public static final String s10510 = "10510";
+  public static final String s10511 = "10511";
+  public static final String s10512 = "10512";
+  public static final String s10513 = "10513";
+  public static final String s10514 = "10514";
+  public static final String s10515 = "10515";
+  public static final String s10516 = "10516";
+  public static final String s10517 = "10517";
+  public static final String s10518 = "10518";
+  public static final String s10519 = "10519";
+  public static final String s10520 = "10520";
+  public static final String s10521 = "10521";
+  public static final String s10522 = "10522";
+  public static final String s10523 = "10523";
+  public static final String s10524 = "10524";
+  public static final String s10525 = "10525";
+  public static final String s10526 = "10526";
+  public static final String s10527 = "10527";
+  public static final String s10528 = "10528";
+  public static final String s10529 = "10529";
+  public static final String s10530 = "10530";
+  public static final String s10531 = "10531";
+  public static final String s10532 = "10532";
+  public static final String s10533 = "10533";
+  public static final String s10534 = "10534";
+  public static final String s10535 = "10535";
+  public static final String s10536 = "10536";
+  public static final String s10537 = "10537";
+  public static final String s10538 = "10538";
+  public static final String s10539 = "10539";
+  public static final String s10540 = "10540";
+  public static final String s10541 = "10541";
+  public static final String s10542 = "10542";
+  public static final String s10543 = "10543";
+  public static final String s10544 = "10544";
+  public static final String s10545 = "10545";
+  public static final String s10546 = "10546";
+  public static final String s10547 = "10547";
+  public static final String s10548 = "10548";
+  public static final String s10549 = "10549";
+  public static final String s10550 = "10550";
+  public static final String s10551 = "10551";
+  public static final String s10552 = "10552";
+  public static final String s10553 = "10553";
+  public static final String s10554 = "10554";
+  public static final String s10555 = "10555";
+  public static final String s10556 = "10556";
+  public static final String s10557 = "10557";
+  public static final String s10558 = "10558";
+  public static final String s10559 = "10559";
+  public static final String s10560 = "10560";
+  public static final String s10561 = "10561";
+  public static final String s10562 = "10562";
+  public static final String s10563 = "10563";
+  public static final String s10564 = "10564";
+  public static final String s10565 = "10565";
+  public static final String s10566 = "10566";
+  public static final String s10567 = "10567";
+  public static final String s10568 = "10568";
+  public static final String s10569 = "10569";
+  public static final String s10570 = "10570";
+  public static final String s10571 = "10571";
+  public static final String s10572 = "10572";
+  public static final String s10573 = "10573";
+  public static final String s10574 = "10574";
+  public static final String s10575 = "10575";
+  public static final String s10576 = "10576";
+  public static final String s10577 = "10577";
+  public static final String s10578 = "10578";
+  public static final String s10579 = "10579";
+  public static final String s10580 = "10580";
+  public static final String s10581 = "10581";
+  public static final String s10582 = "10582";
+  public static final String s10583 = "10583";
+  public static final String s10584 = "10584";
+  public static final String s10585 = "10585";
+  public static final String s10586 = "10586";
+  public static final String s10587 = "10587";
+  public static final String s10588 = "10588";
+  public static final String s10589 = "10589";
+  public static final String s10590 = "10590";
+  public static final String s10591 = "10591";
+  public static final String s10592 = "10592";
+  public static final String s10593 = "10593";
+  public static final String s10594 = "10594";
+  public static final String s10595 = "10595";
+  public static final String s10596 = "10596";
+  public static final String s10597 = "10597";
+  public static final String s10598 = "10598";
+  public static final String s10599 = "10599";
+  public static final String s10600 = "10600";
+  public static final String s10601 = "10601";
+  public static final String s10602 = "10602";
+  public static final String s10603 = "10603";
+  public static final String s10604 = "10604";
+  public static final String s10605 = "10605";
+  public static final String s10606 = "10606";
+  public static final String s10607 = "10607";
+  public static final String s10608 = "10608";
+  public static final String s10609 = "10609";
+  public static final String s10610 = "10610";
+  public static final String s10611 = "10611";
+  public static final String s10612 = "10612";
+  public static final String s10613 = "10613";
+  public static final String s10614 = "10614";
+  public static final String s10615 = "10615";
+  public static final String s10616 = "10616";
+  public static final String s10617 = "10617";
+  public static final String s10618 = "10618";
+  public static final String s10619 = "10619";
+  public static final String s10620 = "10620";
+  public static final String s10621 = "10621";
+  public static final String s10622 = "10622";
+  public static final String s10623 = "10623";
+  public static final String s10624 = "10624";
+  public static final String s10625 = "10625";
+  public static final String s10626 = "10626";
+  public static final String s10627 = "10627";
+  public static final String s10628 = "10628";
+  public static final String s10629 = "10629";
+  public static final String s10630 = "10630";
+  public static final String s10631 = "10631";
+  public static final String s10632 = "10632";
+  public static final String s10633 = "10633";
+  public static final String s10634 = "10634";
+  public static final String s10635 = "10635";
+  public static final String s10636 = "10636";
+  public static final String s10637 = "10637";
+  public static final String s10638 = "10638";
+  public static final String s10639 = "10639";
+  public static final String s10640 = "10640";
+  public static final String s10641 = "10641";
+  public static final String s10642 = "10642";
+  public static final String s10643 = "10643";
+  public static final String s10644 = "10644";
+  public static final String s10645 = "10645";
+  public static final String s10646 = "10646";
+  public static final String s10647 = "10647";
+  public static final String s10648 = "10648";
+  public static final String s10649 = "10649";
+  public static final String s10650 = "10650";
+  public static final String s10651 = "10651";
+  public static final String s10652 = "10652";
+  public static final String s10653 = "10653";
+  public static final String s10654 = "10654";
+  public static final String s10655 = "10655";
+  public static final String s10656 = "10656";
+  public static final String s10657 = "10657";
+  public static final String s10658 = "10658";
+  public static final String s10659 = "10659";
+  public static final String s10660 = "10660";
+  public static final String s10661 = "10661";
+  public static final String s10662 = "10662";
+  public static final String s10663 = "10663";
+  public static final String s10664 = "10664";
+  public static final String s10665 = "10665";
+  public static final String s10666 = "10666";
+  public static final String s10667 = "10667";
+  public static final String s10668 = "10668";
+  public static final String s10669 = "10669";
+  public static final String s10670 = "10670";
+  public static final String s10671 = "10671";
+  public static final String s10672 = "10672";
+  public static final String s10673 = "10673";
+  public static final String s10674 = "10674";
+  public static final String s10675 = "10675";
+  public static final String s10676 = "10676";
+  public static final String s10677 = "10677";
+  public static final String s10678 = "10678";
+  public static final String s10679 = "10679";
+  public static final String s10680 = "10680";
+  public static final String s10681 = "10681";
+  public static final String s10682 = "10682";
+  public static final String s10683 = "10683";
+  public static final String s10684 = "10684";
+  public static final String s10685 = "10685";
+  public static final String s10686 = "10686";
+  public static final String s10687 = "10687";
+  public static final String s10688 = "10688";
+  public static final String s10689 = "10689";
+  public static final String s10690 = "10690";
+  public static final String s10691 = "10691";
+  public static final String s10692 = "10692";
+  public static final String s10693 = "10693";
+  public static final String s10694 = "10694";
+  public static final String s10695 = "10695";
+  public static final String s10696 = "10696";
+  public static final String s10697 = "10697";
+  public static final String s10698 = "10698";
+  public static final String s10699 = "10699";
+  public static final String s10700 = "10700";
+  public static final String s10701 = "10701";
+  public static final String s10702 = "10702";
+  public static final String s10703 = "10703";
+  public static final String s10704 = "10704";
+  public static final String s10705 = "10705";
+  public static final String s10706 = "10706";
+  public static final String s10707 = "10707";
+  public static final String s10708 = "10708";
+  public static final String s10709 = "10709";
+  public static final String s10710 = "10710";
+  public static final String s10711 = "10711";
+  public static final String s10712 = "10712";
+  public static final String s10713 = "10713";
+  public static final String s10714 = "10714";
+  public static final String s10715 = "10715";
+  public static final String s10716 = "10716";
+  public static final String s10717 = "10717";
+  public static final String s10718 = "10718";
+  public static final String s10719 = "10719";
+  public static final String s10720 = "10720";
+  public static final String s10721 = "10721";
+  public static final String s10722 = "10722";
+  public static final String s10723 = "10723";
+  public static final String s10724 = "10724";
+  public static final String s10725 = "10725";
+  public static final String s10726 = "10726";
+  public static final String s10727 = "10727";
+  public static final String s10728 = "10728";
+  public static final String s10729 = "10729";
+  public static final String s10730 = "10730";
+  public static final String s10731 = "10731";
+  public static final String s10732 = "10732";
+  public static final String s10733 = "10733";
+  public static final String s10734 = "10734";
+  public static final String s10735 = "10735";
+  public static final String s10736 = "10736";
+  public static final String s10737 = "10737";
+  public static final String s10738 = "10738";
+  public static final String s10739 = "10739";
+  public static final String s10740 = "10740";
+  public static final String s10741 = "10741";
+  public static final String s10742 = "10742";
+  public static final String s10743 = "10743";
+  public static final String s10744 = "10744";
+  public static final String s10745 = "10745";
+  public static final String s10746 = "10746";
+  public static final String s10747 = "10747";
+  public static final String s10748 = "10748";
+  public static final String s10749 = "10749";
+  public static final String s10750 = "10750";
+  public static final String s10751 = "10751";
+  public static final String s10752 = "10752";
+  public static final String s10753 = "10753";
+  public static final String s10754 = "10754";
+  public static final String s10755 = "10755";
+  public static final String s10756 = "10756";
+  public static final String s10757 = "10757";
+  public static final String s10758 = "10758";
+  public static final String s10759 = "10759";
+  public static final String s10760 = "10760";
+  public static final String s10761 = "10761";
+  public static final String s10762 = "10762";
+  public static final String s10763 = "10763";
+  public static final String s10764 = "10764";
+  public static final String s10765 = "10765";
+  public static final String s10766 = "10766";
+  public static final String s10767 = "10767";
+  public static final String s10768 = "10768";
+  public static final String s10769 = "10769";
+  public static final String s10770 = "10770";
+  public static final String s10771 = "10771";
+  public static final String s10772 = "10772";
+  public static final String s10773 = "10773";
+  public static final String s10774 = "10774";
+  public static final String s10775 = "10775";
+  public static final String s10776 = "10776";
+  public static final String s10777 = "10777";
+  public static final String s10778 = "10778";
+  public static final String s10779 = "10779";
+  public static final String s10780 = "10780";
+  public static final String s10781 = "10781";
+  public static final String s10782 = "10782";
+  public static final String s10783 = "10783";
+  public static final String s10784 = "10784";
+  public static final String s10785 = "10785";
+  public static final String s10786 = "10786";
+  public static final String s10787 = "10787";
+  public static final String s10788 = "10788";
+  public static final String s10789 = "10789";
+  public static final String s10790 = "10790";
+  public static final String s10791 = "10791";
+  public static final String s10792 = "10792";
+  public static final String s10793 = "10793";
+  public static final String s10794 = "10794";
+  public static final String s10795 = "10795";
+  public static final String s10796 = "10796";
+  public static final String s10797 = "10797";
+  public static final String s10798 = "10798";
+  public static final String s10799 = "10799";
+  public static final String s10800 = "10800";
+  public static final String s10801 = "10801";
+  public static final String s10802 = "10802";
+  public static final String s10803 = "10803";
+  public static final String s10804 = "10804";
+  public static final String s10805 = "10805";
+  public static final String s10806 = "10806";
+  public static final String s10807 = "10807";
+  public static final String s10808 = "10808";
+  public static final String s10809 = "10809";
+  public static final String s10810 = "10810";
+  public static final String s10811 = "10811";
+  public static final String s10812 = "10812";
+  public static final String s10813 = "10813";
+  public static final String s10814 = "10814";
+  public static final String s10815 = "10815";
+  public static final String s10816 = "10816";
+  public static final String s10817 = "10817";
+  public static final String s10818 = "10818";
+  public static final String s10819 = "10819";
+  public static final String s10820 = "10820";
+  public static final String s10821 = "10821";
+  public static final String s10822 = "10822";
+  public static final String s10823 = "10823";
+  public static final String s10824 = "10824";
+  public static final String s10825 = "10825";
+  public static final String s10826 = "10826";
+  public static final String s10827 = "10827";
+  public static final String s10828 = "10828";
+  public static final String s10829 = "10829";
+  public static final String s10830 = "10830";
+  public static final String s10831 = "10831";
+  public static final String s10832 = "10832";
+  public static final String s10833 = "10833";
+  public static final String s10834 = "10834";
+  public static final String s10835 = "10835";
+  public static final String s10836 = "10836";
+  public static final String s10837 = "10837";
+  public static final String s10838 = "10838";
+  public static final String s10839 = "10839";
+  public static final String s10840 = "10840";
+  public static final String s10841 = "10841";
+  public static final String s10842 = "10842";
+  public static final String s10843 = "10843";
+  public static final String s10844 = "10844";
+  public static final String s10845 = "10845";
+  public static final String s10846 = "10846";
+  public static final String s10847 = "10847";
+  public static final String s10848 = "10848";
+  public static final String s10849 = "10849";
+  public static final String s10850 = "10850";
+  public static final String s10851 = "10851";
+  public static final String s10852 = "10852";
+  public static final String s10853 = "10853";
+  public static final String s10854 = "10854";
+  public static final String s10855 = "10855";
+  public static final String s10856 = "10856";
+  public static final String s10857 = "10857";
+  public static final String s10858 = "10858";
+  public static final String s10859 = "10859";
+  public static final String s10860 = "10860";
+  public static final String s10861 = "10861";
+  public static final String s10862 = "10862";
+  public static final String s10863 = "10863";
+  public static final String s10864 = "10864";
+  public static final String s10865 = "10865";
+  public static final String s10866 = "10866";
+  public static final String s10867 = "10867";
+  public static final String s10868 = "10868";
+  public static final String s10869 = "10869";
+  public static final String s10870 = "10870";
+  public static final String s10871 = "10871";
+  public static final String s10872 = "10872";
+  public static final String s10873 = "10873";
+  public static final String s10874 = "10874";
+  public static final String s10875 = "10875";
+  public static final String s10876 = "10876";
+  public static final String s10877 = "10877";
+  public static final String s10878 = "10878";
+  public static final String s10879 = "10879";
+  public static final String s10880 = "10880";
+  public static final String s10881 = "10881";
+  public static final String s10882 = "10882";
+  public static final String s10883 = "10883";
+  public static final String s10884 = "10884";
+  public static final String s10885 = "10885";
+  public static final String s10886 = "10886";
+  public static final String s10887 = "10887";
+  public static final String s10888 = "10888";
+  public static final String s10889 = "10889";
+  public static final String s10890 = "10890";
+  public static final String s10891 = "10891";
+  public static final String s10892 = "10892";
+  public static final String s10893 = "10893";
+  public static final String s10894 = "10894";
+  public static final String s10895 = "10895";
+  public static final String s10896 = "10896";
+  public static final String s10897 = "10897";
+  public static final String s10898 = "10898";
+  public static final String s10899 = "10899";
+  public static final String s10900 = "10900";
+  public static final String s10901 = "10901";
+  public static final String s10902 = "10902";
+  public static final String s10903 = "10903";
+  public static final String s10904 = "10904";
+  public static final String s10905 = "10905";
+  public static final String s10906 = "10906";
+  public static final String s10907 = "10907";
+  public static final String s10908 = "10908";
+  public static final String s10909 = "10909";
+  public static final String s10910 = "10910";
+  public static final String s10911 = "10911";
+  public static final String s10912 = "10912";
+  public static final String s10913 = "10913";
+  public static final String s10914 = "10914";
+  public static final String s10915 = "10915";
+  public static final String s10916 = "10916";
+  public static final String s10917 = "10917";
+  public static final String s10918 = "10918";
+  public static final String s10919 = "10919";
+  public static final String s10920 = "10920";
+  public static final String s10921 = "10921";
+  public static final String s10922 = "10922";
+  public static final String s10923 = "10923";
+  public static final String s10924 = "10924";
+  public static final String s10925 = "10925";
+  public static final String s10926 = "10926";
+  public static final String s10927 = "10927";
+  public static final String s10928 = "10928";
+  public static final String s10929 = "10929";
+  public static final String s10930 = "10930";
+  public static final String s10931 = "10931";
+  public static final String s10932 = "10932";
+  public static final String s10933 = "10933";
+  public static final String s10934 = "10934";
+  public static final String s10935 = "10935";
+  public static final String s10936 = "10936";
+  public static final String s10937 = "10937";
+  public static final String s10938 = "10938";
+  public static final String s10939 = "10939";
+  public static final String s10940 = "10940";
+  public static final String s10941 = "10941";
+  public static final String s10942 = "10942";
+  public static final String s10943 = "10943";
+  public static final String s10944 = "10944";
+  public static final String s10945 = "10945";
+  public static final String s10946 = "10946";
+  public static final String s10947 = "10947";
+  public static final String s10948 = "10948";
+  public static final String s10949 = "10949";
+  public static final String s10950 = "10950";
+  public static final String s10951 = "10951";
+  public static final String s10952 = "10952";
+  public static final String s10953 = "10953";
+  public static final String s10954 = "10954";
+  public static final String s10955 = "10955";
+  public static final String s10956 = "10956";
+  public static final String s10957 = "10957";
+  public static final String s10958 = "10958";
+  public static final String s10959 = "10959";
+  public static final String s10960 = "10960";
+  public static final String s10961 = "10961";
+  public static final String s10962 = "10962";
+  public static final String s10963 = "10963";
+  public static final String s10964 = "10964";
+  public static final String s10965 = "10965";
+  public static final String s10966 = "10966";
+  public static final String s10967 = "10967";
+  public static final String s10968 = "10968";
+  public static final String s10969 = "10969";
+  public static final String s10970 = "10970";
+  public static final String s10971 = "10971";
+  public static final String s10972 = "10972";
+  public static final String s10973 = "10973";
+  public static final String s10974 = "10974";
+  public static final String s10975 = "10975";
+  public static final String s10976 = "10976";
+  public static final String s10977 = "10977";
+  public static final String s10978 = "10978";
+  public static final String s10979 = "10979";
+  public static final String s10980 = "10980";
+  public static final String s10981 = "10981";
+  public static final String s10982 = "10982";
+  public static final String s10983 = "10983";
+  public static final String s10984 = "10984";
+  public static final String s10985 = "10985";
+  public static final String s10986 = "10986";
+  public static final String s10987 = "10987";
+  public static final String s10988 = "10988";
+  public static final String s10989 = "10989";
+  public static final String s10990 = "10990";
+  public static final String s10991 = "10991";
+  public static final String s10992 = "10992";
+  public static final String s10993 = "10993";
+  public static final String s10994 = "10994";
+  public static final String s10995 = "10995";
+  public static final String s10996 = "10996";
+  public static final String s10997 = "10997";
+  public static final String s10998 = "10998";
+  public static final String s10999 = "10999";
+  public static final String s11000 = "11000";
+  public static final String s11001 = "11001";
+  public static final String s11002 = "11002";
+  public static final String s11003 = "11003";
+  public static final String s11004 = "11004";
+  public static final String s11005 = "11005";
+  public static final String s11006 = "11006";
+  public static final String s11007 = "11007";
+  public static final String s11008 = "11008";
+  public static final String s11009 = "11009";
+  public static final String s11010 = "11010";
+  public static final String s11011 = "11011";
+  public static final String s11012 = "11012";
+  public static final String s11013 = "11013";
+  public static final String s11014 = "11014";
+  public static final String s11015 = "11015";
+  public static final String s11016 = "11016";
+  public static final String s11017 = "11017";
+  public static final String s11018 = "11018";
+  public static final String s11019 = "11019";
+  public static final String s11020 = "11020";
+  public static final String s11021 = "11021";
+  public static final String s11022 = "11022";
+  public static final String s11023 = "11023";
+  public static final String s11024 = "11024";
+  public static final String s11025 = "11025";
+  public static final String s11026 = "11026";
+  public static final String s11027 = "11027";
+  public static final String s11028 = "11028";
+  public static final String s11029 = "11029";
+  public static final String s11030 = "11030";
+  public static final String s11031 = "11031";
+  public static final String s11032 = "11032";
+  public static final String s11033 = "11033";
+  public static final String s11034 = "11034";
+  public static final String s11035 = "11035";
+  public static final String s11036 = "11036";
+  public static final String s11037 = "11037";
+  public static final String s11038 = "11038";
+  public static final String s11039 = "11039";
+  public static final String s11040 = "11040";
+  public static final String s11041 = "11041";
+  public static final String s11042 = "11042";
+  public static final String s11043 = "11043";
+  public static final String s11044 = "11044";
+  public static final String s11045 = "11045";
+  public static final String s11046 = "11046";
+  public static final String s11047 = "11047";
+  public static final String s11048 = "11048";
+  public static final String s11049 = "11049";
+  public static final String s11050 = "11050";
+  public static final String s11051 = "11051";
+  public static final String s11052 = "11052";
+  public static final String s11053 = "11053";
+  public static final String s11054 = "11054";
+  public static final String s11055 = "11055";
+  public static final String s11056 = "11056";
+  public static final String s11057 = "11057";
+  public static final String s11058 = "11058";
+  public static final String s11059 = "11059";
+  public static final String s11060 = "11060";
+  public static final String s11061 = "11061";
+  public static final String s11062 = "11062";
+  public static final String s11063 = "11063";
+  public static final String s11064 = "11064";
+  public static final String s11065 = "11065";
+  public static final String s11066 = "11066";
+  public static final String s11067 = "11067";
+  public static final String s11068 = "11068";
+  public static final String s11069 = "11069";
+  public static final String s11070 = "11070";
+  public static final String s11071 = "11071";
+  public static final String s11072 = "11072";
+  public static final String s11073 = "11073";
+  public static final String s11074 = "11074";
+  public static final String s11075 = "11075";
+  public static final String s11076 = "11076";
+  public static final String s11077 = "11077";
+  public static final String s11078 = "11078";
+  public static final String s11079 = "11079";
+  public static final String s11080 = "11080";
+  public static final String s11081 = "11081";
+  public static final String s11082 = "11082";
+  public static final String s11083 = "11083";
+  public static final String s11084 = "11084";
+  public static final String s11085 = "11085";
+  public static final String s11086 = "11086";
+  public static final String s11087 = "11087";
+  public static final String s11088 = "11088";
+  public static final String s11089 = "11089";
+  public static final String s11090 = "11090";
+  public static final String s11091 = "11091";
+  public static final String s11092 = "11092";
+  public static final String s11093 = "11093";
+  public static final String s11094 = "11094";
+  public static final String s11095 = "11095";
+  public static final String s11096 = "11096";
+  public static final String s11097 = "11097";
+  public static final String s11098 = "11098";
+  public static final String s11099 = "11099";
+  public static final String s11100 = "11100";
+  public static final String s11101 = "11101";
+  public static final String s11102 = "11102";
+  public static final String s11103 = "11103";
+  public static final String s11104 = "11104";
+  public static final String s11105 = "11105";
+  public static final String s11106 = "11106";
+  public static final String s11107 = "11107";
+  public static final String s11108 = "11108";
+  public static final String s11109 = "11109";
+  public static final String s11110 = "11110";
+  public static final String s11111 = "11111";
+  public static final String s11112 = "11112";
+  public static final String s11113 = "11113";
+  public static final String s11114 = "11114";
+  public static final String s11115 = "11115";
+  public static final String s11116 = "11116";
+  public static final String s11117 = "11117";
+  public static final String s11118 = "11118";
+  public static final String s11119 = "11119";
+  public static final String s11120 = "11120";
+  public static final String s11121 = "11121";
+  public static final String s11122 = "11122";
+  public static final String s11123 = "11123";
+  public static final String s11124 = "11124";
+  public static final String s11125 = "11125";
+  public static final String s11126 = "11126";
+  public static final String s11127 = "11127";
+  public static final String s11128 = "11128";
+  public static final String s11129 = "11129";
+  public static final String s11130 = "11130";
+  public static final String s11131 = "11131";
+  public static final String s11132 = "11132";
+  public static final String s11133 = "11133";
+  public static final String s11134 = "11134";
+  public static final String s11135 = "11135";
+  public static final String s11136 = "11136";
+  public static final String s11137 = "11137";
+  public static final String s11138 = "11138";
+  public static final String s11139 = "11139";
+  public static final String s11140 = "11140";
+  public static final String s11141 = "11141";
+  public static final String s11142 = "11142";
+  public static final String s11143 = "11143";
+  public static final String s11144 = "11144";
+  public static final String s11145 = "11145";
+  public static final String s11146 = "11146";
+  public static final String s11147 = "11147";
+  public static final String s11148 = "11148";
+  public static final String s11149 = "11149";
+  public static final String s11150 = "11150";
+  public static final String s11151 = "11151";
+  public static final String s11152 = "11152";
+  public static final String s11153 = "11153";
+  public static final String s11154 = "11154";
+  public static final String s11155 = "11155";
+  public static final String s11156 = "11156";
+  public static final String s11157 = "11157";
+  public static final String s11158 = "11158";
+  public static final String s11159 = "11159";
+  public static final String s11160 = "11160";
+  public static final String s11161 = "11161";
+  public static final String s11162 = "11162";
+  public static final String s11163 = "11163";
+  public static final String s11164 = "11164";
+  public static final String s11165 = "11165";
+  public static final String s11166 = "11166";
+  public static final String s11167 = "11167";
+  public static final String s11168 = "11168";
+  public static final String s11169 = "11169";
+  public static final String s11170 = "11170";
+  public static final String s11171 = "11171";
+  public static final String s11172 = "11172";
+  public static final String s11173 = "11173";
+  public static final String s11174 = "11174";
+  public static final String s11175 = "11175";
+  public static final String s11176 = "11176";
+  public static final String s11177 = "11177";
+  public static final String s11178 = "11178";
+  public static final String s11179 = "11179";
+  public static final String s11180 = "11180";
+  public static final String s11181 = "11181";
+  public static final String s11182 = "11182";
+  public static final String s11183 = "11183";
+  public static final String s11184 = "11184";
+  public static final String s11185 = "11185";
+  public static final String s11186 = "11186";
+  public static final String s11187 = "11187";
+  public static final String s11188 = "11188";
+  public static final String s11189 = "11189";
+  public static final String s11190 = "11190";
+  public static final String s11191 = "11191";
+  public static final String s11192 = "11192";
+  public static final String s11193 = "11193";
+  public static final String s11194 = "11194";
+  public static final String s11195 = "11195";
+  public static final String s11196 = "11196";
+  public static final String s11197 = "11197";
+  public static final String s11198 = "11198";
+  public static final String s11199 = "11199";
+  public static final String s11200 = "11200";
+  public static final String s11201 = "11201";
+  public static final String s11202 = "11202";
+  public static final String s11203 = "11203";
+  public static final String s11204 = "11204";
+  public static final String s11205 = "11205";
+  public static final String s11206 = "11206";
+  public static final String s11207 = "11207";
+  public static final String s11208 = "11208";
+  public static final String s11209 = "11209";
+  public static final String s11210 = "11210";
+  public static final String s11211 = "11211";
+  public static final String s11212 = "11212";
+  public static final String s11213 = "11213";
+  public static final String s11214 = "11214";
+  public static final String s11215 = "11215";
+  public static final String s11216 = "11216";
+  public static final String s11217 = "11217";
+  public static final String s11218 = "11218";
+  public static final String s11219 = "11219";
+  public static final String s11220 = "11220";
+  public static final String s11221 = "11221";
+  public static final String s11222 = "11222";
+  public static final String s11223 = "11223";
+  public static final String s11224 = "11224";
+  public static final String s11225 = "11225";
+  public static final String s11226 = "11226";
+  public static final String s11227 = "11227";
+  public static final String s11228 = "11228";
+  public static final String s11229 = "11229";
+  public static final String s11230 = "11230";
+  public static final String s11231 = "11231";
+  public static final String s11232 = "11232";
+  public static final String s11233 = "11233";
+  public static final String s11234 = "11234";
+  public static final String s11235 = "11235";
+  public static final String s11236 = "11236";
+  public static final String s11237 = "11237";
+  public static final String s11238 = "11238";
+  public static final String s11239 = "11239";
+  public static final String s11240 = "11240";
+  public static final String s11241 = "11241";
+  public static final String s11242 = "11242";
+  public static final String s11243 = "11243";
+  public static final String s11244 = "11244";
+  public static final String s11245 = "11245";
+  public static final String s11246 = "11246";
+  public static final String s11247 = "11247";
+  public static final String s11248 = "11248";
+  public static final String s11249 = "11249";
+  public static final String s11250 = "11250";
+  public static final String s11251 = "11251";
+  public static final String s11252 = "11252";
+  public static final String s11253 = "11253";
+  public static final String s11254 = "11254";
+  public static final String s11255 = "11255";
+  public static final String s11256 = "11256";
+  public static final String s11257 = "11257";
+  public static final String s11258 = "11258";
+  public static final String s11259 = "11259";
+  public static final String s11260 = "11260";
+  public static final String s11261 = "11261";
+  public static final String s11262 = "11262";
+  public static final String s11263 = "11263";
+  public static final String s11264 = "11264";
+  public static final String s11265 = "11265";
+  public static final String s11266 = "11266";
+  public static final String s11267 = "11267";
+  public static final String s11268 = "11268";
+  public static final String s11269 = "11269";
+  public static final String s11270 = "11270";
+  public static final String s11271 = "11271";
+  public static final String s11272 = "11272";
+  public static final String s11273 = "11273";
+  public static final String s11274 = "11274";
+  public static final String s11275 = "11275";
+  public static final String s11276 = "11276";
+  public static final String s11277 = "11277";
+  public static final String s11278 = "11278";
+  public static final String s11279 = "11279";
+  public static final String s11280 = "11280";
+  public static final String s11281 = "11281";
+  public static final String s11282 = "11282";
+  public static final String s11283 = "11283";
+  public static final String s11284 = "11284";
+  public static final String s11285 = "11285";
+  public static final String s11286 = "11286";
+  public static final String s11287 = "11287";
+  public static final String s11288 = "11288";
+  public static final String s11289 = "11289";
+  public static final String s11290 = "11290";
+  public static final String s11291 = "11291";
+  public static final String s11292 = "11292";
+  public static final String s11293 = "11293";
+  public static final String s11294 = "11294";
+  public static final String s11295 = "11295";
+  public static final String s11296 = "11296";
+  public static final String s11297 = "11297";
+  public static final String s11298 = "11298";
+  public static final String s11299 = "11299";
+  public static final String s11300 = "11300";
+  public static final String s11301 = "11301";
+  public static final String s11302 = "11302";
+  public static final String s11303 = "11303";
+  public static final String s11304 = "11304";
+  public static final String s11305 = "11305";
+  public static final String s11306 = "11306";
+  public static final String s11307 = "11307";
+  public static final String s11308 = "11308";
+  public static final String s11309 = "11309";
+  public static final String s11310 = "11310";
+  public static final String s11311 = "11311";
+  public static final String s11312 = "11312";
+  public static final String s11313 = "11313";
+  public static final String s11314 = "11314";
+  public static final String s11315 = "11315";
+  public static final String s11316 = "11316";
+  public static final String s11317 = "11317";
+  public static final String s11318 = "11318";
+  public static final String s11319 = "11319";
+  public static final String s11320 = "11320";
+  public static final String s11321 = "11321";
+  public static final String s11322 = "11322";
+  public static final String s11323 = "11323";
+  public static final String s11324 = "11324";
+  public static final String s11325 = "11325";
+  public static final String s11326 = "11326";
+  public static final String s11327 = "11327";
+  public static final String s11328 = "11328";
+  public static final String s11329 = "11329";
+  public static final String s11330 = "11330";
+  public static final String s11331 = "11331";
+  public static final String s11332 = "11332";
+  public static final String s11333 = "11333";
+  public static final String s11334 = "11334";
+  public static final String s11335 = "11335";
+  public static final String s11336 = "11336";
+  public static final String s11337 = "11337";
+  public static final String s11338 = "11338";
+  public static final String s11339 = "11339";
+  public static final String s11340 = "11340";
+  public static final String s11341 = "11341";
+  public static final String s11342 = "11342";
+  public static final String s11343 = "11343";
+  public static final String s11344 = "11344";
+  public static final String s11345 = "11345";
+  public static final String s11346 = "11346";
+  public static final String s11347 = "11347";
+  public static final String s11348 = "11348";
+  public static final String s11349 = "11349";
+  public static final String s11350 = "11350";
+  public static final String s11351 = "11351";
+  public static final String s11352 = "11352";
+  public static final String s11353 = "11353";
+  public static final String s11354 = "11354";
+  public static final String s11355 = "11355";
+  public static final String s11356 = "11356";
+  public static final String s11357 = "11357";
+  public static final String s11358 = "11358";
+  public static final String s11359 = "11359";
+  public static final String s11360 = "11360";
+  public static final String s11361 = "11361";
+  public static final String s11362 = "11362";
+  public static final String s11363 = "11363";
+  public static final String s11364 = "11364";
+  public static final String s11365 = "11365";
+  public static final String s11366 = "11366";
+  public static final String s11367 = "11367";
+  public static final String s11368 = "11368";
+  public static final String s11369 = "11369";
+  public static final String s11370 = "11370";
+  public static final String s11371 = "11371";
+  public static final String s11372 = "11372";
+  public static final String s11373 = "11373";
+  public static final String s11374 = "11374";
+  public static final String s11375 = "11375";
+  public static final String s11376 = "11376";
+  public static final String s11377 = "11377";
+  public static final String s11378 = "11378";
+  public static final String s11379 = "11379";
+  public static final String s11380 = "11380";
+  public static final String s11381 = "11381";
+  public static final String s11382 = "11382";
+  public static final String s11383 = "11383";
+  public static final String s11384 = "11384";
+  public static final String s11385 = "11385";
+  public static final String s11386 = "11386";
+  public static final String s11387 = "11387";
+  public static final String s11388 = "11388";
+  public static final String s11389 = "11389";
+  public static final String s11390 = "11390";
+  public static final String s11391 = "11391";
+  public static final String s11392 = "11392";
+  public static final String s11393 = "11393";
+  public static final String s11394 = "11394";
+  public static final String s11395 = "11395";
+  public static final String s11396 = "11396";
+  public static final String s11397 = "11397";
+  public static final String s11398 = "11398";
+  public static final String s11399 = "11399";
+  public static final String s11400 = "11400";
+  public static final String s11401 = "11401";
+  public static final String s11402 = "11402";
+  public static final String s11403 = "11403";
+  public static final String s11404 = "11404";
+  public static final String s11405 = "11405";
+  public static final String s11406 = "11406";
+  public static final String s11407 = "11407";
+  public static final String s11408 = "11408";
+  public static final String s11409 = "11409";
+  public static final String s11410 = "11410";
+  public static final String s11411 = "11411";
+  public static final String s11412 = "11412";
+  public static final String s11413 = "11413";
+  public static final String s11414 = "11414";
+  public static final String s11415 = "11415";
+  public static final String s11416 = "11416";
+  public static final String s11417 = "11417";
+  public static final String s11418 = "11418";
+  public static final String s11419 = "11419";
+  public static final String s11420 = "11420";
+  public static final String s11421 = "11421";
+  public static final String s11422 = "11422";
+  public static final String s11423 = "11423";
+  public static final String s11424 = "11424";
+  public static final String s11425 = "11425";
+  public static final String s11426 = "11426";
+  public static final String s11427 = "11427";
+  public static final String s11428 = "11428";
+  public static final String s11429 = "11429";
+  public static final String s11430 = "11430";
+  public static final String s11431 = "11431";
+  public static final String s11432 = "11432";
+  public static final String s11433 = "11433";
+  public static final String s11434 = "11434";
+  public static final String s11435 = "11435";
+  public static final String s11436 = "11436";
+  public static final String s11437 = "11437";
+  public static final String s11438 = "11438";
+  public static final String s11439 = "11439";
+  public static final String s11440 = "11440";
+  public static final String s11441 = "11441";
+  public static final String s11442 = "11442";
+  public static final String s11443 = "11443";
+  public static final String s11444 = "11444";
+  public static final String s11445 = "11445";
+  public static final String s11446 = "11446";
+  public static final String s11447 = "11447";
+  public static final String s11448 = "11448";
+  public static final String s11449 = "11449";
+  public static final String s11450 = "11450";
+  public static final String s11451 = "11451";
+  public static final String s11452 = "11452";
+  public static final String s11453 = "11453";
+  public static final String s11454 = "11454";
+  public static final String s11455 = "11455";
+  public static final String s11456 = "11456";
+  public static final String s11457 = "11457";
+  public static final String s11458 = "11458";
+  public static final String s11459 = "11459";
+  public static final String s11460 = "11460";
+  public static final String s11461 = "11461";
+  public static final String s11462 = "11462";
+  public static final String s11463 = "11463";
+  public static final String s11464 = "11464";
+  public static final String s11465 = "11465";
+  public static final String s11466 = "11466";
+  public static final String s11467 = "11467";
+  public static final String s11468 = "11468";
+  public static final String s11469 = "11469";
+  public static final String s11470 = "11470";
+  public static final String s11471 = "11471";
+  public static final String s11472 = "11472";
+  public static final String s11473 = "11473";
+  public static final String s11474 = "11474";
+  public static final String s11475 = "11475";
+  public static final String s11476 = "11476";
+  public static final String s11477 = "11477";
+  public static final String s11478 = "11478";
+  public static final String s11479 = "11479";
+  public static final String s11480 = "11480";
+  public static final String s11481 = "11481";
+  public static final String s11482 = "11482";
+  public static final String s11483 = "11483";
+  public static final String s11484 = "11484";
+  public static final String s11485 = "11485";
+  public static final String s11486 = "11486";
+  public static final String s11487 = "11487";
+  public static final String s11488 = "11488";
+  public static final String s11489 = "11489";
+  public static final String s11490 = "11490";
+  public static final String s11491 = "11491";
+  public static final String s11492 = "11492";
+  public static final String s11493 = "11493";
+  public static final String s11494 = "11494";
+  public static final String s11495 = "11495";
+  public static final String s11496 = "11496";
+  public static final String s11497 = "11497";
+  public static final String s11498 = "11498";
+  public static final String s11499 = "11499";
+  public static final String s11500 = "11500";
+  public static final String s11501 = "11501";
+  public static final String s11502 = "11502";
+  public static final String s11503 = "11503";
+  public static final String s11504 = "11504";
+  public static final String s11505 = "11505";
+  public static final String s11506 = "11506";
+  public static final String s11507 = "11507";
+  public static final String s11508 = "11508";
+  public static final String s11509 = "11509";
+  public static final String s11510 = "11510";
+  public static final String s11511 = "11511";
+  public static final String s11512 = "11512";
+  public static final String s11513 = "11513";
+  public static final String s11514 = "11514";
+  public static final String s11515 = "11515";
+  public static final String s11516 = "11516";
+  public static final String s11517 = "11517";
+  public static final String s11518 = "11518";
+  public static final String s11519 = "11519";
+  public static final String s11520 = "11520";
+  public static final String s11521 = "11521";
+  public static final String s11522 = "11522";
+  public static final String s11523 = "11523";
+  public static final String s11524 = "11524";
+  public static final String s11525 = "11525";
+  public static final String s11526 = "11526";
+  public static final String s11527 = "11527";
+  public static final String s11528 = "11528";
+  public static final String s11529 = "11529";
+  public static final String s11530 = "11530";
+  public static final String s11531 = "11531";
+  public static final String s11532 = "11532";
+  public static final String s11533 = "11533";
+  public static final String s11534 = "11534";
+  public static final String s11535 = "11535";
+  public static final String s11536 = "11536";
+  public static final String s11537 = "11537";
+  public static final String s11538 = "11538";
+  public static final String s11539 = "11539";
+  public static final String s11540 = "11540";
+  public static final String s11541 = "11541";
+  public static final String s11542 = "11542";
+  public static final String s11543 = "11543";
+  public static final String s11544 = "11544";
+  public static final String s11545 = "11545";
+  public static final String s11546 = "11546";
+  public static final String s11547 = "11547";
+  public static final String s11548 = "11548";
+  public static final String s11549 = "11549";
+  public static final String s11550 = "11550";
+  public static final String s11551 = "11551";
+  public static final String s11552 = "11552";
+  public static final String s11553 = "11553";
+  public static final String s11554 = "11554";
+  public static final String s11555 = "11555";
+  public static final String s11556 = "11556";
+  public static final String s11557 = "11557";
+  public static final String s11558 = "11558";
+  public static final String s11559 = "11559";
+  public static final String s11560 = "11560";
+  public static final String s11561 = "11561";
+  public static final String s11562 = "11562";
+  public static final String s11563 = "11563";
+  public static final String s11564 = "11564";
+  public static final String s11565 = "11565";
+  public static final String s11566 = "11566";
+  public static final String s11567 = "11567";
+  public static final String s11568 = "11568";
+  public static final String s11569 = "11569";
+  public static final String s11570 = "11570";
+  public static final String s11571 = "11571";
+  public static final String s11572 = "11572";
+  public static final String s11573 = "11573";
+  public static final String s11574 = "11574";
+  public static final String s11575 = "11575";
+  public static final String s11576 = "11576";
+  public static final String s11577 = "11577";
+  public static final String s11578 = "11578";
+  public static final String s11579 = "11579";
+  public static final String s11580 = "11580";
+  public static final String s11581 = "11581";
+  public static final String s11582 = "11582";
+  public static final String s11583 = "11583";
+  public static final String s11584 = "11584";
+  public static final String s11585 = "11585";
+  public static final String s11586 = "11586";
+  public static final String s11587 = "11587";
+  public static final String s11588 = "11588";
+  public static final String s11589 = "11589";
+  public static final String s11590 = "11590";
+  public static final String s11591 = "11591";
+  public static final String s11592 = "11592";
+  public static final String s11593 = "11593";
+  public static final String s11594 = "11594";
+  public static final String s11595 = "11595";
+  public static final String s11596 = "11596";
+  public static final String s11597 = "11597";
+  public static final String s11598 = "11598";
+  public static final String s11599 = "11599";
+  public static final String s11600 = "11600";
+  public static final String s11601 = "11601";
+  public static final String s11602 = "11602";
+  public static final String s11603 = "11603";
+  public static final String s11604 = "11604";
+  public static final String s11605 = "11605";
+  public static final String s11606 = "11606";
+  public static final String s11607 = "11607";
+  public static final String s11608 = "11608";
+  public static final String s11609 = "11609";
+  public static final String s11610 = "11610";
+  public static final String s11611 = "11611";
+  public static final String s11612 = "11612";
+  public static final String s11613 = "11613";
+  public static final String s11614 = "11614";
+  public static final String s11615 = "11615";
+  public static final String s11616 = "11616";
+  public static final String s11617 = "11617";
+  public static final String s11618 = "11618";
+  public static final String s11619 = "11619";
+  public static final String s11620 = "11620";
+  public static final String s11621 = "11621";
+  public static final String s11622 = "11622";
+  public static final String s11623 = "11623";
+  public static final String s11624 = "11624";
+  public static final String s11625 = "11625";
+  public static final String s11626 = "11626";
+  public static final String s11627 = "11627";
+  public static final String s11628 = "11628";
+  public static final String s11629 = "11629";
+  public static final String s11630 = "11630";
+  public static final String s11631 = "11631";
+  public static final String s11632 = "11632";
+  public static final String s11633 = "11633";
+  public static final String s11634 = "11634";
+  public static final String s11635 = "11635";
+  public static final String s11636 = "11636";
+  public static final String s11637 = "11637";
+  public static final String s11638 = "11638";
+  public static final String s11639 = "11639";
+  public static final String s11640 = "11640";
+  public static final String s11641 = "11641";
+  public static final String s11642 = "11642";
+  public static final String s11643 = "11643";
+  public static final String s11644 = "11644";
+  public static final String s11645 = "11645";
+  public static final String s11646 = "11646";
+  public static final String s11647 = "11647";
+  public static final String s11648 = "11648";
+  public static final String s11649 = "11649";
+  public static final String s11650 = "11650";
+  public static final String s11651 = "11651";
+  public static final String s11652 = "11652";
+  public static final String s11653 = "11653";
+  public static final String s11654 = "11654";
+  public static final String s11655 = "11655";
+  public static final String s11656 = "11656";
+  public static final String s11657 = "11657";
+  public static final String s11658 = "11658";
+  public static final String s11659 = "11659";
+  public static final String s11660 = "11660";
+  public static final String s11661 = "11661";
+  public static final String s11662 = "11662";
+  public static final String s11663 = "11663";
+  public static final String s11664 = "11664";
+  public static final String s11665 = "11665";
+  public static final String s11666 = "11666";
+  public static final String s11667 = "11667";
+  public static final String s11668 = "11668";
+  public static final String s11669 = "11669";
+  public static final String s11670 = "11670";
+  public static final String s11671 = "11671";
+  public static final String s11672 = "11672";
+  public static final String s11673 = "11673";
+  public static final String s11674 = "11674";
+  public static final String s11675 = "11675";
+  public static final String s11676 = "11676";
+  public static final String s11677 = "11677";
+  public static final String s11678 = "11678";
+  public static final String s11679 = "11679";
+  public static final String s11680 = "11680";
+  public static final String s11681 = "11681";
+  public static final String s11682 = "11682";
+  public static final String s11683 = "11683";
+  public static final String s11684 = "11684";
+  public static final String s11685 = "11685";
+  public static final String s11686 = "11686";
+  public static final String s11687 = "11687";
+  public static final String s11688 = "11688";
+  public static final String s11689 = "11689";
+  public static final String s11690 = "11690";
+  public static final String s11691 = "11691";
+  public static final String s11692 = "11692";
+  public static final String s11693 = "11693";
+  public static final String s11694 = "11694";
+  public static final String s11695 = "11695";
+  public static final String s11696 = "11696";
+  public static final String s11697 = "11697";
+  public static final String s11698 = "11698";
+  public static final String s11699 = "11699";
+  public static final String s11700 = "11700";
+  public static final String s11701 = "11701";
+  public static final String s11702 = "11702";
+  public static final String s11703 = "11703";
+  public static final String s11704 = "11704";
+  public static final String s11705 = "11705";
+  public static final String s11706 = "11706";
+  public static final String s11707 = "11707";
+  public static final String s11708 = "11708";
+  public static final String s11709 = "11709";
+  public static final String s11710 = "11710";
+  public static final String s11711 = "11711";
+  public static final String s11712 = "11712";
+  public static final String s11713 = "11713";
+  public static final String s11714 = "11714";
+  public static final String s11715 = "11715";
+  public static final String s11716 = "11716";
+  public static final String s11717 = "11717";
+  public static final String s11718 = "11718";
+  public static final String s11719 = "11719";
+  public static final String s11720 = "11720";
+  public static final String s11721 = "11721";
+  public static final String s11722 = "11722";
+  public static final String s11723 = "11723";
+  public static final String s11724 = "11724";
+  public static final String s11725 = "11725";
+  public static final String s11726 = "11726";
+  public static final String s11727 = "11727";
+  public static final String s11728 = "11728";
+  public static final String s11729 = "11729";
+  public static final String s11730 = "11730";
+  public static final String s11731 = "11731";
+  public static final String s11732 = "11732";
+  public static final String s11733 = "11733";
+  public static final String s11734 = "11734";
+  public static final String s11735 = "11735";
+  public static final String s11736 = "11736";
+  public static final String s11737 = "11737";
+  public static final String s11738 = "11738";
+  public static final String s11739 = "11739";
+  public static final String s11740 = "11740";
+  public static final String s11741 = "11741";
+  public static final String s11742 = "11742";
+  public static final String s11743 = "11743";
+  public static final String s11744 = "11744";
+  public static final String s11745 = "11745";
+  public static final String s11746 = "11746";
+  public static final String s11747 = "11747";
+  public static final String s11748 = "11748";
+  public static final String s11749 = "11749";
+  public static final String s11750 = "11750";
+  public static final String s11751 = "11751";
+  public static final String s11752 = "11752";
+  public static final String s11753 = "11753";
+  public static final String s11754 = "11754";
+  public static final String s11755 = "11755";
+  public static final String s11756 = "11756";
+  public static final String s11757 = "11757";
+  public static final String s11758 = "11758";
+  public static final String s11759 = "11759";
+  public static final String s11760 = "11760";
+  public static final String s11761 = "11761";
+  public static final String s11762 = "11762";
+  public static final String s11763 = "11763";
+  public static final String s11764 = "11764";
+  public static final String s11765 = "11765";
+  public static final String s11766 = "11766";
+  public static final String s11767 = "11767";
+  public static final String s11768 = "11768";
+  public static final String s11769 = "11769";
+  public static final String s11770 = "11770";
+  public static final String s11771 = "11771";
+  public static final String s11772 = "11772";
+  public static final String s11773 = "11773";
+  public static final String s11774 = "11774";
+  public static final String s11775 = "11775";
+  public static final String s11776 = "11776";
+  public static final String s11777 = "11777";
+  public static final String s11778 = "11778";
+  public static final String s11779 = "11779";
+  public static final String s11780 = "11780";
+  public static final String s11781 = "11781";
+  public static final String s11782 = "11782";
+  public static final String s11783 = "11783";
+  public static final String s11784 = "11784";
+  public static final String s11785 = "11785";
+  public static final String s11786 = "11786";
+  public static final String s11787 = "11787";
+  public static final String s11788 = "11788";
+  public static final String s11789 = "11789";
+  public static final String s11790 = "11790";
+  public static final String s11791 = "11791";
+  public static final String s11792 = "11792";
+  public static final String s11793 = "11793";
+  public static final String s11794 = "11794";
+  public static final String s11795 = "11795";
+  public static final String s11796 = "11796";
+  public static final String s11797 = "11797";
+  public static final String s11798 = "11798";
+  public static final String s11799 = "11799";
+  public static final String s11800 = "11800";
+  public static final String s11801 = "11801";
+  public static final String s11802 = "11802";
+  public static final String s11803 = "11803";
+  public static final String s11804 = "11804";
+  public static final String s11805 = "11805";
+  public static final String s11806 = "11806";
+  public static final String s11807 = "11807";
+  public static final String s11808 = "11808";
+  public static final String s11809 = "11809";
+  public static final String s11810 = "11810";
+  public static final String s11811 = "11811";
+  public static final String s11812 = "11812";
+  public static final String s11813 = "11813";
+  public static final String s11814 = "11814";
+  public static final String s11815 = "11815";
+  public static final String s11816 = "11816";
+  public static final String s11817 = "11817";
+  public static final String s11818 = "11818";
+  public static final String s11819 = "11819";
+  public static final String s11820 = "11820";
+  public static final String s11821 = "11821";
+  public static final String s11822 = "11822";
+  public static final String s11823 = "11823";
+  public static final String s11824 = "11824";
+  public static final String s11825 = "11825";
+  public static final String s11826 = "11826";
+  public static final String s11827 = "11827";
+  public static final String s11828 = "11828";
+  public static final String s11829 = "11829";
+  public static final String s11830 = "11830";
+  public static final String s11831 = "11831";
+  public static final String s11832 = "11832";
+  public static final String s11833 = "11833";
+  public static final String s11834 = "11834";
+  public static final String s11835 = "11835";
+  public static final String s11836 = "11836";
+  public static final String s11837 = "11837";
+  public static final String s11838 = "11838";
+  public static final String s11839 = "11839";
+  public static final String s11840 = "11840";
+  public static final String s11841 = "11841";
+  public static final String s11842 = "11842";
+  public static final String s11843 = "11843";
+  public static final String s11844 = "11844";
+  public static final String s11845 = "11845";
+  public static final String s11846 = "11846";
+  public static final String s11847 = "11847";
+  public static final String s11848 = "11848";
+  public static final String s11849 = "11849";
+  public static final String s11850 = "11850";
+  public static final String s11851 = "11851";
+  public static final String s11852 = "11852";
+  public static final String s11853 = "11853";
+  public static final String s11854 = "11854";
+  public static final String s11855 = "11855";
+  public static final String s11856 = "11856";
+  public static final String s11857 = "11857";
+  public static final String s11858 = "11858";
+  public static final String s11859 = "11859";
+  public static final String s11860 = "11860";
+  public static final String s11861 = "11861";
+  public static final String s11862 = "11862";
+  public static final String s11863 = "11863";
+  public static final String s11864 = "11864";
+  public static final String s11865 = "11865";
+  public static final String s11866 = "11866";
+  public static final String s11867 = "11867";
+  public static final String s11868 = "11868";
+  public static final String s11869 = "11869";
+  public static final String s11870 = "11870";
+  public static final String s11871 = "11871";
+  public static final String s11872 = "11872";
+  public static final String s11873 = "11873";
+  public static final String s11874 = "11874";
+  public static final String s11875 = "11875";
+  public static final String s11876 = "11876";
+  public static final String s11877 = "11877";
+  public static final String s11878 = "11878";
+  public static final String s11879 = "11879";
+  public static final String s11880 = "11880";
+  public static final String s11881 = "11881";
+  public static final String s11882 = "11882";
+  public static final String s11883 = "11883";
+  public static final String s11884 = "11884";
+  public static final String s11885 = "11885";
+  public static final String s11886 = "11886";
+  public static final String s11887 = "11887";
+  public static final String s11888 = "11888";
+  public static final String s11889 = "11889";
+  public static final String s11890 = "11890";
+  public static final String s11891 = "11891";
+  public static final String s11892 = "11892";
+  public static final String s11893 = "11893";
+  public static final String s11894 = "11894";
+  public static final String s11895 = "11895";
+  public static final String s11896 = "11896";
+  public static final String s11897 = "11897";
+  public static final String s11898 = "11898";
+  public static final String s11899 = "11899";
+  public static final String s11900 = "11900";
+  public static final String s11901 = "11901";
+  public static final String s11902 = "11902";
+  public static final String s11903 = "11903";
+  public static final String s11904 = "11904";
+  public static final String s11905 = "11905";
+  public static final String s11906 = "11906";
+  public static final String s11907 = "11907";
+  public static final String s11908 = "11908";
+  public static final String s11909 = "11909";
+  public static final String s11910 = "11910";
+  public static final String s11911 = "11911";
+  public static final String s11912 = "11912";
+  public static final String s11913 = "11913";
+  public static final String s11914 = "11914";
+  public static final String s11915 = "11915";
+  public static final String s11916 = "11916";
+  public static final String s11917 = "11917";
+  public static final String s11918 = "11918";
+  public static final String s11919 = "11919";
+  public static final String s11920 = "11920";
+  public static final String s11921 = "11921";
+  public static final String s11922 = "11922";
+  public static final String s11923 = "11923";
+  public static final String s11924 = "11924";
+  public static final String s11925 = "11925";
+  public static final String s11926 = "11926";
+  public static final String s11927 = "11927";
+  public static final String s11928 = "11928";
+  public static final String s11929 = "11929";
+  public static final String s11930 = "11930";
+  public static final String s11931 = "11931";
+  public static final String s11932 = "11932";
+  public static final String s11933 = "11933";
+  public static final String s11934 = "11934";
+  public static final String s11935 = "11935";
+  public static final String s11936 = "11936";
+  public static final String s11937 = "11937";
+  public static final String s11938 = "11938";
+  public static final String s11939 = "11939";
+  public static final String s11940 = "11940";
+  public static final String s11941 = "11941";
+  public static final String s11942 = "11942";
+  public static final String s11943 = "11943";
+  public static final String s11944 = "11944";
+  public static final String s11945 = "11945";
+  public static final String s11946 = "11946";
+  public static final String s11947 = "11947";
+  public static final String s11948 = "11948";
+  public static final String s11949 = "11949";
+  public static final String s11950 = "11950";
+  public static final String s11951 = "11951";
+  public static final String s11952 = "11952";
+  public static final String s11953 = "11953";
+  public static final String s11954 = "11954";
+  public static final String s11955 = "11955";
+  public static final String s11956 = "11956";
+  public static final String s11957 = "11957";
+  public static final String s11958 = "11958";
+  public static final String s11959 = "11959";
+  public static final String s11960 = "11960";
+  public static final String s11961 = "11961";
+  public static final String s11962 = "11962";
+  public static final String s11963 = "11963";
+  public static final String s11964 = "11964";
+  public static final String s11965 = "11965";
+  public static final String s11966 = "11966";
+  public static final String s11967 = "11967";
+  public static final String s11968 = "11968";
+  public static final String s11969 = "11969";
+  public static final String s11970 = "11970";
+  public static final String s11971 = "11971";
+  public static final String s11972 = "11972";
+  public static final String s11973 = "11973";
+  public static final String s11974 = "11974";
+  public static final String s11975 = "11975";
+  public static final String s11976 = "11976";
+  public static final String s11977 = "11977";
+  public static final String s11978 = "11978";
+  public static final String s11979 = "11979";
+  public static final String s11980 = "11980";
+  public static final String s11981 = "11981";
+  public static final String s11982 = "11982";
+  public static final String s11983 = "11983";
+  public static final String s11984 = "11984";
+  public static final String s11985 = "11985";
+  public static final String s11986 = "11986";
+  public static final String s11987 = "11987";
+  public static final String s11988 = "11988";
+  public static final String s11989 = "11989";
+  public static final String s11990 = "11990";
+  public static final String s11991 = "11991";
+  public static final String s11992 = "11992";
+  public static final String s11993 = "11993";
+  public static final String s11994 = "11994";
+  public static final String s11995 = "11995";
+  public static final String s11996 = "11996";
+  public static final String s11997 = "11997";
+  public static final String s11998 = "11998";
+  public static final String s11999 = "11999";
+  public static final String s12000 = "12000";
+  public static final String s12001 = "12001";
+  public static final String s12002 = "12002";
+  public static final String s12003 = "12003";
+  public static final String s12004 = "12004";
+  public static final String s12005 = "12005";
+  public static final String s12006 = "12006";
+  public static final String s12007 = "12007";
+  public static final String s12008 = "12008";
+  public static final String s12009 = "12009";
+  public static final String s12010 = "12010";
+  public static final String s12011 = "12011";
+  public static final String s12012 = "12012";
+  public static final String s12013 = "12013";
+  public static final String s12014 = "12014";
+  public static final String s12015 = "12015";
+  public static final String s12016 = "12016";
+  public static final String s12017 = "12017";
+  public static final String s12018 = "12018";
+  public static final String s12019 = "12019";
+  public static final String s12020 = "12020";
+  public static final String s12021 = "12021";
+  public static final String s12022 = "12022";
+  public static final String s12023 = "12023";
+  public static final String s12024 = "12024";
+  public static final String s12025 = "12025";
+  public static final String s12026 = "12026";
+  public static final String s12027 = "12027";
+  public static final String s12028 = "12028";
+  public static final String s12029 = "12029";
+  public static final String s12030 = "12030";
+  public static final String s12031 = "12031";
+  public static final String s12032 = "12032";
+  public static final String s12033 = "12033";
+  public static final String s12034 = "12034";
+  public static final String s12035 = "12035";
+  public static final String s12036 = "12036";
+  public static final String s12037 = "12037";
+  public static final String s12038 = "12038";
+  public static final String s12039 = "12039";
+  public static final String s12040 = "12040";
+  public static final String s12041 = "12041";
+  public static final String s12042 = "12042";
+  public static final String s12043 = "12043";
+  public static final String s12044 = "12044";
+  public static final String s12045 = "12045";
+  public static final String s12046 = "12046";
+  public static final String s12047 = "12047";
+  public static final String s12048 = "12048";
+  public static final String s12049 = "12049";
+  public static final String s12050 = "12050";
+  public static final String s12051 = "12051";
+  public static final String s12052 = "12052";
+  public static final String s12053 = "12053";
+  public static final String s12054 = "12054";
+  public static final String s12055 = "12055";
+  public static final String s12056 = "12056";
+  public static final String s12057 = "12057";
+  public static final String s12058 = "12058";
+  public static final String s12059 = "12059";
+  public static final String s12060 = "12060";
+  public static final String s12061 = "12061";
+  public static final String s12062 = "12062";
+  public static final String s12063 = "12063";
+  public static final String s12064 = "12064";
+  public static final String s12065 = "12065";
+  public static final String s12066 = "12066";
+  public static final String s12067 = "12067";
+  public static final String s12068 = "12068";
+  public static final String s12069 = "12069";
+  public static final String s12070 = "12070";
+  public static final String s12071 = "12071";
+  public static final String s12072 = "12072";
+  public static final String s12073 = "12073";
+  public static final String s12074 = "12074";
+  public static final String s12075 = "12075";
+  public static final String s12076 = "12076";
+  public static final String s12077 = "12077";
+  public static final String s12078 = "12078";
+  public static final String s12079 = "12079";
+  public static final String s12080 = "12080";
+  public static final String s12081 = "12081";
+  public static final String s12082 = "12082";
+  public static final String s12083 = "12083";
+  public static final String s12084 = "12084";
+  public static final String s12085 = "12085";
+  public static final String s12086 = "12086";
+  public static final String s12087 = "12087";
+  public static final String s12088 = "12088";
+  public static final String s12089 = "12089";
+  public static final String s12090 = "12090";
+  public static final String s12091 = "12091";
+  public static final String s12092 = "12092";
+  public static final String s12093 = "12093";
+  public static final String s12094 = "12094";
+  public static final String s12095 = "12095";
+  public static final String s12096 = "12096";
+  public static final String s12097 = "12097";
+  public static final String s12098 = "12098";
+  public static final String s12099 = "12099";
+  public static final String s12100 = "12100";
+  public static final String s12101 = "12101";
+  public static final String s12102 = "12102";
+  public static final String s12103 = "12103";
+  public static final String s12104 = "12104";
+  public static final String s12105 = "12105";
+  public static final String s12106 = "12106";
+  public static final String s12107 = "12107";
+  public static final String s12108 = "12108";
+  public static final String s12109 = "12109";
+  public static final String s12110 = "12110";
+  public static final String s12111 = "12111";
+  public static final String s12112 = "12112";
+  public static final String s12113 = "12113";
+  public static final String s12114 = "12114";
+  public static final String s12115 = "12115";
+  public static final String s12116 = "12116";
+  public static final String s12117 = "12117";
+  public static final String s12118 = "12118";
+  public static final String s12119 = "12119";
+  public static final String s12120 = "12120";
+  public static final String s12121 = "12121";
+  public static final String s12122 = "12122";
+  public static final String s12123 = "12123";
+  public static final String s12124 = "12124";
+  public static final String s12125 = "12125";
+  public static final String s12126 = "12126";
+  public static final String s12127 = "12127";
+  public static final String s12128 = "12128";
+  public static final String s12129 = "12129";
+  public static final String s12130 = "12130";
+  public static final String s12131 = "12131";
+  public static final String s12132 = "12132";
+  public static final String s12133 = "12133";
+  public static final String s12134 = "12134";
+  public static final String s12135 = "12135";
+  public static final String s12136 = "12136";
+  public static final String s12137 = "12137";
+  public static final String s12138 = "12138";
+  public static final String s12139 = "12139";
+  public static final String s12140 = "12140";
+  public static final String s12141 = "12141";
+  public static final String s12142 = "12142";
+  public static final String s12143 = "12143";
+  public static final String s12144 = "12144";
+  public static final String s12145 = "12145";
+  public static final String s12146 = "12146";
+  public static final String s12147 = "12147";
+  public static final String s12148 = "12148";
+  public static final String s12149 = "12149";
+  public static final String s12150 = "12150";
+  public static final String s12151 = "12151";
+  public static final String s12152 = "12152";
+  public static final String s12153 = "12153";
+  public static final String s12154 = "12154";
+  public static final String s12155 = "12155";
+  public static final String s12156 = "12156";
+  public static final String s12157 = "12157";
+  public static final String s12158 = "12158";
+  public static final String s12159 = "12159";
+  public static final String s12160 = "12160";
+  public static final String s12161 = "12161";
+  public static final String s12162 = "12162";
+  public static final String s12163 = "12163";
+  public static final String s12164 = "12164";
+  public static final String s12165 = "12165";
+  public static final String s12166 = "12166";
+  public static final String s12167 = "12167";
+  public static final String s12168 = "12168";
+  public static final String s12169 = "12169";
+  public static final String s12170 = "12170";
+  public static final String s12171 = "12171";
+  public static final String s12172 = "12172";
+  public static final String s12173 = "12173";
+  public static final String s12174 = "12174";
+  public static final String s12175 = "12175";
+  public static final String s12176 = "12176";
+  public static final String s12177 = "12177";
+  public static final String s12178 = "12178";
+  public static final String s12179 = "12179";
+  public static final String s12180 = "12180";
+  public static final String s12181 = "12181";
+  public static final String s12182 = "12182";
+  public static final String s12183 = "12183";
+  public static final String s12184 = "12184";
+  public static final String s12185 = "12185";
+  public static final String s12186 = "12186";
+  public static final String s12187 = "12187";
+  public static final String s12188 = "12188";
+  public static final String s12189 = "12189";
+  public static final String s12190 = "12190";
+  public static final String s12191 = "12191";
+  public static final String s12192 = "12192";
+  public static final String s12193 = "12193";
+  public static final String s12194 = "12194";
+  public static final String s12195 = "12195";
+  public static final String s12196 = "12196";
+  public static final String s12197 = "12197";
+  public static final String s12198 = "12198";
+  public static final String s12199 = "12199";
+  public static final String s12200 = "12200";
+  public static final String s12201 = "12201";
+  public static final String s12202 = "12202";
+  public static final String s12203 = "12203";
+  public static final String s12204 = "12204";
+  public static final String s12205 = "12205";
+  public static final String s12206 = "12206";
+  public static final String s12207 = "12207";
+  public static final String s12208 = "12208";
+  public static final String s12209 = "12209";
+  public static final String s12210 = "12210";
+  public static final String s12211 = "12211";
+  public static final String s12212 = "12212";
+  public static final String s12213 = "12213";
+  public static final String s12214 = "12214";
+  public static final String s12215 = "12215";
+  public static final String s12216 = "12216";
+  public static final String s12217 = "12217";
+  public static final String s12218 = "12218";
+  public static final String s12219 = "12219";
+  public static final String s12220 = "12220";
+  public static final String s12221 = "12221";
+  public static final String s12222 = "12222";
+  public static final String s12223 = "12223";
+  public static final String s12224 = "12224";
+  public static final String s12225 = "12225";
+  public static final String s12226 = "12226";
+  public static final String s12227 = "12227";
+  public static final String s12228 = "12228";
+  public static final String s12229 = "12229";
+  public static final String s12230 = "12230";
+  public static final String s12231 = "12231";
+  public static final String s12232 = "12232";
+  public static final String s12233 = "12233";
+  public static final String s12234 = "12234";
+  public static final String s12235 = "12235";
+  public static final String s12236 = "12236";
+  public static final String s12237 = "12237";
+  public static final String s12238 = "12238";
+  public static final String s12239 = "12239";
+  public static final String s12240 = "12240";
+  public static final String s12241 = "12241";
+  public static final String s12242 = "12242";
+  public static final String s12243 = "12243";
+  public static final String s12244 = "12244";
+  public static final String s12245 = "12245";
+  public static final String s12246 = "12246";
+  public static final String s12247 = "12247";
+  public static final String s12248 = "12248";
+  public static final String s12249 = "12249";
+  public static final String s12250 = "12250";
+  public static final String s12251 = "12251";
+  public static final String s12252 = "12252";
+  public static final String s12253 = "12253";
+  public static final String s12254 = "12254";
+  public static final String s12255 = "12255";
+  public static final String s12256 = "12256";
+  public static final String s12257 = "12257";
+  public static final String s12258 = "12258";
+  public static final String s12259 = "12259";
+  public static final String s12260 = "12260";
+  public static final String s12261 = "12261";
+  public static final String s12262 = "12262";
+  public static final String s12263 = "12263";
+  public static final String s12264 = "12264";
+  public static final String s12265 = "12265";
+  public static final String s12266 = "12266";
+  public static final String s12267 = "12267";
+  public static final String s12268 = "12268";
+  public static final String s12269 = "12269";
+  public static final String s12270 = "12270";
+  public static final String s12271 = "12271";
+  public static final String s12272 = "12272";
+  public static final String s12273 = "12273";
+  public static final String s12274 = "12274";
+  public static final String s12275 = "12275";
+  public static final String s12276 = "12276";
+  public static final String s12277 = "12277";
+  public static final String s12278 = "12278";
+  public static final String s12279 = "12279";
+  public static final String s12280 = "12280";
+  public static final String s12281 = "12281";
+  public static final String s12282 = "12282";
+  public static final String s12283 = "12283";
+  public static final String s12284 = "12284";
+  public static final String s12285 = "12285";
+  public static final String s12286 = "12286";
+  public static final String s12287 = "12287";
+  public static final String s12288 = "12288";
+  public static final String s12289 = "12289";
+  public static final String s12290 = "12290";
+  public static final String s12291 = "12291";
+  public static final String s12292 = "12292";
+  public static final String s12293 = "12293";
+  public static final String s12294 = "12294";
+  public static final String s12295 = "12295";
+  public static final String s12296 = "12296";
+  public static final String s12297 = "12297";
+  public static final String s12298 = "12298";
+  public static final String s12299 = "12299";
+  public static final String s12300 = "12300";
+  public static final String s12301 = "12301";
+  public static final String s12302 = "12302";
+  public static final String s12303 = "12303";
+  public static final String s12304 = "12304";
+  public static final String s12305 = "12305";
+  public static final String s12306 = "12306";
+  public static final String s12307 = "12307";
+  public static final String s12308 = "12308";
+  public static final String s12309 = "12309";
+  public static final String s12310 = "12310";
+  public static final String s12311 = "12311";
+  public static final String s12312 = "12312";
+  public static final String s12313 = "12313";
+  public static final String s12314 = "12314";
+  public static final String s12315 = "12315";
+  public static final String s12316 = "12316";
+  public static final String s12317 = "12317";
+  public static final String s12318 = "12318";
+  public static final String s12319 = "12319";
+  public static final String s12320 = "12320";
+  public static final String s12321 = "12321";
+  public static final String s12322 = "12322";
+  public static final String s12323 = "12323";
+  public static final String s12324 = "12324";
+  public static final String s12325 = "12325";
+  public static final String s12326 = "12326";
+  public static final String s12327 = "12327";
+  public static final String s12328 = "12328";
+  public static final String s12329 = "12329";
+  public static final String s12330 = "12330";
+  public static final String s12331 = "12331";
+  public static final String s12332 = "12332";
+  public static final String s12333 = "12333";
+  public static final String s12334 = "12334";
+  public static final String s12335 = "12335";
+  public static final String s12336 = "12336";
+  public static final String s12337 = "12337";
+  public static final String s12338 = "12338";
+  public static final String s12339 = "12339";
+  public static final String s12340 = "12340";
+  public static final String s12341 = "12341";
+  public static final String s12342 = "12342";
+  public static final String s12343 = "12343";
+  public static final String s12344 = "12344";
+  public static final String s12345 = "12345";
+  public static final String s12346 = "12346";
+  public static final String s12347 = "12347";
+  public static final String s12348 = "12348";
+  public static final String s12349 = "12349";
+  public static final String s12350 = "12350";
+  public static final String s12351 = "12351";
+  public static final String s12352 = "12352";
+  public static final String s12353 = "12353";
+  public static final String s12354 = "12354";
+  public static final String s12355 = "12355";
+  public static final String s12356 = "12356";
+  public static final String s12357 = "12357";
+  public static final String s12358 = "12358";
+  public static final String s12359 = "12359";
+  public static final String s12360 = "12360";
+  public static final String s12361 = "12361";
+  public static final String s12362 = "12362";
+  public static final String s12363 = "12363";
+  public static final String s12364 = "12364";
+  public static final String s12365 = "12365";
+  public static final String s12366 = "12366";
+  public static final String s12367 = "12367";
+  public static final String s12368 = "12368";
+  public static final String s12369 = "12369";
+  public static final String s12370 = "12370";
+  public static final String s12371 = "12371";
+  public static final String s12372 = "12372";
+  public static final String s12373 = "12373";
+  public static final String s12374 = "12374";
+  public static final String s12375 = "12375";
+  public static final String s12376 = "12376";
+  public static final String s12377 = "12377";
+  public static final String s12378 = "12378";
+  public static final String s12379 = "12379";
+  public static final String s12380 = "12380";
+  public static final String s12381 = "12381";
+  public static final String s12382 = "12382";
+  public static final String s12383 = "12383";
+  public static final String s12384 = "12384";
+  public static final String s12385 = "12385";
+  public static final String s12386 = "12386";
+  public static final String s12387 = "12387";
+  public static final String s12388 = "12388";
+  public static final String s12389 = "12389";
+  public static final String s12390 = "12390";
+  public static final String s12391 = "12391";
+  public static final String s12392 = "12392";
+  public static final String s12393 = "12393";
+  public static final String s12394 = "12394";
+  public static final String s12395 = "12395";
+  public static final String s12396 = "12396";
+  public static final String s12397 = "12397";
+  public static final String s12398 = "12398";
+  public static final String s12399 = "12399";
+  public static final String s12400 = "12400";
+  public static final String s12401 = "12401";
+  public static final String s12402 = "12402";
+  public static final String s12403 = "12403";
+  public static final String s12404 = "12404";
+  public static final String s12405 = "12405";
+  public static final String s12406 = "12406";
+  public static final String s12407 = "12407";
+  public static final String s12408 = "12408";
+  public static final String s12409 = "12409";
+  public static final String s12410 = "12410";
+  public static final String s12411 = "12411";
+  public static final String s12412 = "12412";
+  public static final String s12413 = "12413";
+  public static final String s12414 = "12414";
+  public static final String s12415 = "12415";
+  public static final String s12416 = "12416";
+  public static final String s12417 = "12417";
+  public static final String s12418 = "12418";
+  public static final String s12419 = "12419";
+  public static final String s12420 = "12420";
+  public static final String s12421 = "12421";
+  public static final String s12422 = "12422";
+  public static final String s12423 = "12423";
+  public static final String s12424 = "12424";
+  public static final String s12425 = "12425";
+  public static final String s12426 = "12426";
+  public static final String s12427 = "12427";
+  public static final String s12428 = "12428";
+  public static final String s12429 = "12429";
+  public static final String s12430 = "12430";
+  public static final String s12431 = "12431";
+  public static final String s12432 = "12432";
+  public static final String s12433 = "12433";
+  public static final String s12434 = "12434";
+  public static final String s12435 = "12435";
+  public static final String s12436 = "12436";
+  public static final String s12437 = "12437";
+  public static final String s12438 = "12438";
+  public static final String s12439 = "12439";
+  public static final String s12440 = "12440";
+  public static final String s12441 = "12441";
+  public static final String s12442 = "12442";
+  public static final String s12443 = "12443";
+  public static final String s12444 = "12444";
+  public static final String s12445 = "12445";
+  public static final String s12446 = "12446";
+  public static final String s12447 = "12447";
+  public static final String s12448 = "12448";
+  public static final String s12449 = "12449";
+  public static final String s12450 = "12450";
+  public static final String s12451 = "12451";
+  public static final String s12452 = "12452";
+  public static final String s12453 = "12453";
+  public static final String s12454 = "12454";
+  public static final String s12455 = "12455";
+  public static final String s12456 = "12456";
+  public static final String s12457 = "12457";
+  public static final String s12458 = "12458";
+  public static final String s12459 = "12459";
+  public static final String s12460 = "12460";
+  public static final String s12461 = "12461";
+  public static final String s12462 = "12462";
+  public static final String s12463 = "12463";
+  public static final String s12464 = "12464";
+  public static final String s12465 = "12465";
+  public static final String s12466 = "12466";
+  public static final String s12467 = "12467";
+  public static final String s12468 = "12468";
+  public static final String s12469 = "12469";
+  public static final String s12470 = "12470";
+  public static final String s12471 = "12471";
+  public static final String s12472 = "12472";
+  public static final String s12473 = "12473";
+  public static final String s12474 = "12474";
+  public static final String s12475 = "12475";
+  public static final String s12476 = "12476";
+  public static final String s12477 = "12477";
+  public static final String s12478 = "12478";
+  public static final String s12479 = "12479";
+  public static final String s12480 = "12480";
+  public static final String s12481 = "12481";
+  public static final String s12482 = "12482";
+  public static final String s12483 = "12483";
+  public static final String s12484 = "12484";
+  public static final String s12485 = "12485";
+  public static final String s12486 = "12486";
+  public static final String s12487 = "12487";
+  public static final String s12488 = "12488";
+  public static final String s12489 = "12489";
+  public static final String s12490 = "12490";
+  public static final String s12491 = "12491";
+  public static final String s12492 = "12492";
+  public static final String s12493 = "12493";
+  public static final String s12494 = "12494";
+  public static final String s12495 = "12495";
+  public static final String s12496 = "12496";
+  public static final String s12497 = "12497";
+  public static final String s12498 = "12498";
+  public static final String s12499 = "12499";
+  public static final String s12500 = "12500";
+  public static final String s12501 = "12501";
+  public static final String s12502 = "12502";
+  public static final String s12503 = "12503";
+  public static final String s12504 = "12504";
+  public static final String s12505 = "12505";
+  public static final String s12506 = "12506";
+  public static final String s12507 = "12507";
+  public static final String s12508 = "12508";
+  public static final String s12509 = "12509";
+  public static final String s12510 = "12510";
+  public static final String s12511 = "12511";
+  public static final String s12512 = "12512";
+  public static final String s12513 = "12513";
+  public static final String s12514 = "12514";
+  public static final String s12515 = "12515";
+  public static final String s12516 = "12516";
+  public static final String s12517 = "12517";
+  public static final String s12518 = "12518";
+  public static final String s12519 = "12519";
+  public static final String s12520 = "12520";
+  public static final String s12521 = "12521";
+  public static final String s12522 = "12522";
+  public static final String s12523 = "12523";
+  public static final String s12524 = "12524";
+  public static final String s12525 = "12525";
+  public static final String s12526 = "12526";
+  public static final String s12527 = "12527";
+  public static final String s12528 = "12528";
+  public static final String s12529 = "12529";
+  public static final String s12530 = "12530";
+  public static final String s12531 = "12531";
+  public static final String s12532 = "12532";
+  public static final String s12533 = "12533";
+  public static final String s12534 = "12534";
+  public static final String s12535 = "12535";
+  public static final String s12536 = "12536";
+  public static final String s12537 = "12537";
+  public static final String s12538 = "12538";
+  public static final String s12539 = "12539";
+  public static final String s12540 = "12540";
+  public static final String s12541 = "12541";
+  public static final String s12542 = "12542";
+  public static final String s12543 = "12543";
+  public static final String s12544 = "12544";
+  public static final String s12545 = "12545";
+  public static final String s12546 = "12546";
+  public static final String s12547 = "12547";
+  public static final String s12548 = "12548";
+  public static final String s12549 = "12549";
+  public static final String s12550 = "12550";
+  public static final String s12551 = "12551";
+  public static final String s12552 = "12552";
+  public static final String s12553 = "12553";
+  public static final String s12554 = "12554";
+  public static final String s12555 = "12555";
+  public static final String s12556 = "12556";
+  public static final String s12557 = "12557";
+  public static final String s12558 = "12558";
+  public static final String s12559 = "12559";
+  public static final String s12560 = "12560";
+  public static final String s12561 = "12561";
+  public static final String s12562 = "12562";
+  public static final String s12563 = "12563";
+  public static final String s12564 = "12564";
+  public static final String s12565 = "12565";
+  public static final String s12566 = "12566";
+  public static final String s12567 = "12567";
+  public static final String s12568 = "12568";
+  public static final String s12569 = "12569";
+  public static final String s12570 = "12570";
+  public static final String s12571 = "12571";
+  public static final String s12572 = "12572";
+  public static final String s12573 = "12573";
+  public static final String s12574 = "12574";
+  public static final String s12575 = "12575";
+  public static final String s12576 = "12576";
+  public static final String s12577 = "12577";
+  public static final String s12578 = "12578";
+  public static final String s12579 = "12579";
+  public static final String s12580 = "12580";
+  public static final String s12581 = "12581";
+  public static final String s12582 = "12582";
+  public static final String s12583 = "12583";
+  public static final String s12584 = "12584";
+  public static final String s12585 = "12585";
+  public static final String s12586 = "12586";
+  public static final String s12587 = "12587";
+  public static final String s12588 = "12588";
+  public static final String s12589 = "12589";
+  public static final String s12590 = "12590";
+  public static final String s12591 = "12591";
+  public static final String s12592 = "12592";
+  public static final String s12593 = "12593";
+  public static final String s12594 = "12594";
+  public static final String s12595 = "12595";
+  public static final String s12596 = "12596";
+  public static final String s12597 = "12597";
+  public static final String s12598 = "12598";
+  public static final String s12599 = "12599";
+  public static final String s12600 = "12600";
+  public static final String s12601 = "12601";
+  public static final String s12602 = "12602";
+  public static final String s12603 = "12603";
+  public static final String s12604 = "12604";
+  public static final String s12605 = "12605";
+  public static final String s12606 = "12606";
+  public static final String s12607 = "12607";
+  public static final String s12608 = "12608";
+  public static final String s12609 = "12609";
+  public static final String s12610 = "12610";
+  public static final String s12611 = "12611";
+  public static final String s12612 = "12612";
+  public static final String s12613 = "12613";
+  public static final String s12614 = "12614";
+  public static final String s12615 = "12615";
+  public static final String s12616 = "12616";
+  public static final String s12617 = "12617";
+  public static final String s12618 = "12618";
+  public static final String s12619 = "12619";
+  public static final String s12620 = "12620";
+  public static final String s12621 = "12621";
+  public static final String s12622 = "12622";
+  public static final String s12623 = "12623";
+  public static final String s12624 = "12624";
+  public static final String s12625 = "12625";
+  public static final String s12626 = "12626";
+  public static final String s12627 = "12627";
+  public static final String s12628 = "12628";
+  public static final String s12629 = "12629";
+  public static final String s12630 = "12630";
+  public static final String s12631 = "12631";
+  public static final String s12632 = "12632";
+  public static final String s12633 = "12633";
+  public static final String s12634 = "12634";
+  public static final String s12635 = "12635";
+  public static final String s12636 = "12636";
+  public static final String s12637 = "12637";
+  public static final String s12638 = "12638";
+  public static final String s12639 = "12639";
+  public static final String s12640 = "12640";
+  public static final String s12641 = "12641";
+  public static final String s12642 = "12642";
+  public static final String s12643 = "12643";
+  public static final String s12644 = "12644";
+  public static final String s12645 = "12645";
+  public static final String s12646 = "12646";
+  public static final String s12647 = "12647";
+  public static final String s12648 = "12648";
+  public static final String s12649 = "12649";
+  public static final String s12650 = "12650";
+  public static final String s12651 = "12651";
+  public static final String s12652 = "12652";
+  public static final String s12653 = "12653";
+  public static final String s12654 = "12654";
+  public static final String s12655 = "12655";
+  public static final String s12656 = "12656";
+  public static final String s12657 = "12657";
+  public static final String s12658 = "12658";
+  public static final String s12659 = "12659";
+  public static final String s12660 = "12660";
+  public static final String s12661 = "12661";
+  public static final String s12662 = "12662";
+  public static final String s12663 = "12663";
+  public static final String s12664 = "12664";
+  public static final String s12665 = "12665";
+  public static final String s12666 = "12666";
+  public static final String s12667 = "12667";
+  public static final String s12668 = "12668";
+  public static final String s12669 = "12669";
+  public static final String s12670 = "12670";
+  public static final String s12671 = "12671";
+  public static final String s12672 = "12672";
+  public static final String s12673 = "12673";
+  public static final String s12674 = "12674";
+  public static final String s12675 = "12675";
+  public static final String s12676 = "12676";
+  public static final String s12677 = "12677";
+  public static final String s12678 = "12678";
+  public static final String s12679 = "12679";
+  public static final String s12680 = "12680";
+  public static final String s12681 = "12681";
+  public static final String s12682 = "12682";
+  public static final String s12683 = "12683";
+  public static final String s12684 = "12684";
+  public static final String s12685 = "12685";
+  public static final String s12686 = "12686";
+  public static final String s12687 = "12687";
+  public static final String s12688 = "12688";
+  public static final String s12689 = "12689";
+  public static final String s12690 = "12690";
+  public static final String s12691 = "12691";
+  public static final String s12692 = "12692";
+  public static final String s12693 = "12693";
+  public static final String s12694 = "12694";
+  public static final String s12695 = "12695";
+  public static final String s12696 = "12696";
+  public static final String s12697 = "12697";
+  public static final String s12698 = "12698";
+  public static final String s12699 = "12699";
+  public static final String s12700 = "12700";
+  public static final String s12701 = "12701";
+  public static final String s12702 = "12702";
+  public static final String s12703 = "12703";
+  public static final String s12704 = "12704";
+  public static final String s12705 = "12705";
+  public static final String s12706 = "12706";
+  public static final String s12707 = "12707";
+  public static final String s12708 = "12708";
+  public static final String s12709 = "12709";
+  public static final String s12710 = "12710";
+  public static final String s12711 = "12711";
+  public static final String s12712 = "12712";
+  public static final String s12713 = "12713";
+  public static final String s12714 = "12714";
+  public static final String s12715 = "12715";
+  public static final String s12716 = "12716";
+  public static final String s12717 = "12717";
+  public static final String s12718 = "12718";
+  public static final String s12719 = "12719";
+  public static final String s12720 = "12720";
+  public static final String s12721 = "12721";
+  public static final String s12722 = "12722";
+  public static final String s12723 = "12723";
+  public static final String s12724 = "12724";
+  public static final String s12725 = "12725";
+  public static final String s12726 = "12726";
+  public static final String s12727 = "12727";
+  public static final String s12728 = "12728";
+  public static final String s12729 = "12729";
+  public static final String s12730 = "12730";
+  public static final String s12731 = "12731";
+  public static final String s12732 = "12732";
+  public static final String s12733 = "12733";
+  public static final String s12734 = "12734";
+  public static final String s12735 = "12735";
+  public static final String s12736 = "12736";
+  public static final String s12737 = "12737";
+  public static final String s12738 = "12738";
+  public static final String s12739 = "12739";
+  public static final String s12740 = "12740";
+  public static final String s12741 = "12741";
+  public static final String s12742 = "12742";
+  public static final String s12743 = "12743";
+  public static final String s12744 = "12744";
+  public static final String s12745 = "12745";
+  public static final String s12746 = "12746";
+  public static final String s12747 = "12747";
+  public static final String s12748 = "12748";
+  public static final String s12749 = "12749";
+  public static final String s12750 = "12750";
+  public static final String s12751 = "12751";
+  public static final String s12752 = "12752";
+  public static final String s12753 = "12753";
+  public static final String s12754 = "12754";
+  public static final String s12755 = "12755";
+  public static final String s12756 = "12756";
+  public static final String s12757 = "12757";
+  public static final String s12758 = "12758";
+  public static final String s12759 = "12759";
+  public static final String s12760 = "12760";
+  public static final String s12761 = "12761";
+  public static final String s12762 = "12762";
+  public static final String s12763 = "12763";
+  public static final String s12764 = "12764";
+  public static final String s12765 = "12765";
+  public static final String s12766 = "12766";
+  public static final String s12767 = "12767";
+  public static final String s12768 = "12768";
+  public static final String s12769 = "12769";
+  public static final String s12770 = "12770";
+  public static final String s12771 = "12771";
+  public static final String s12772 = "12772";
+  public static final String s12773 = "12773";
+  public static final String s12774 = "12774";
+  public static final String s12775 = "12775";
+  public static final String s12776 = "12776";
+  public static final String s12777 = "12777";
+  public static final String s12778 = "12778";
+  public static final String s12779 = "12779";
+  public static final String s12780 = "12780";
+  public static final String s12781 = "12781";
+  public static final String s12782 = "12782";
+  public static final String s12783 = "12783";
+  public static final String s12784 = "12784";
+  public static final String s12785 = "12785";
+  public static final String s12786 = "12786";
+  public static final String s12787 = "12787";
+  public static final String s12788 = "12788";
+  public static final String s12789 = "12789";
+  public static final String s12790 = "12790";
+  public static final String s12791 = "12791";
+  public static final String s12792 = "12792";
+  public static final String s12793 = "12793";
+  public static final String s12794 = "12794";
+  public static final String s12795 = "12795";
+  public static final String s12796 = "12796";
+  public static final String s12797 = "12797";
+  public static final String s12798 = "12798";
+  public static final String s12799 = "12799";
+  public static final String s12800 = "12800";
+  public static final String s12801 = "12801";
+  public static final String s12802 = "12802";
+  public static final String s12803 = "12803";
+  public static final String s12804 = "12804";
+  public static final String s12805 = "12805";
+  public static final String s12806 = "12806";
+  public static final String s12807 = "12807";
+  public static final String s12808 = "12808";
+  public static final String s12809 = "12809";
+  public static final String s12810 = "12810";
+  public static final String s12811 = "12811";
+  public static final String s12812 = "12812";
+  public static final String s12813 = "12813";
+  public static final String s12814 = "12814";
+  public static final String s12815 = "12815";
+  public static final String s12816 = "12816";
+  public static final String s12817 = "12817";
+  public static final String s12818 = "12818";
+  public static final String s12819 = "12819";
+  public static final String s12820 = "12820";
+  public static final String s12821 = "12821";
+  public static final String s12822 = "12822";
+  public static final String s12823 = "12823";
+  public static final String s12824 = "12824";
+  public static final String s12825 = "12825";
+  public static final String s12826 = "12826";
+  public static final String s12827 = "12827";
+  public static final String s12828 = "12828";
+  public static final String s12829 = "12829";
+  public static final String s12830 = "12830";
+  public static final String s12831 = "12831";
+  public static final String s12832 = "12832";
+  public static final String s12833 = "12833";
+  public static final String s12834 = "12834";
+  public static final String s12835 = "12835";
+  public static final String s12836 = "12836";
+  public static final String s12837 = "12837";
+  public static final String s12838 = "12838";
+  public static final String s12839 = "12839";
+  public static final String s12840 = "12840";
+  public static final String s12841 = "12841";
+  public static final String s12842 = "12842";
+  public static final String s12843 = "12843";
+  public static final String s12844 = "12844";
+  public static final String s12845 = "12845";
+  public static final String s12846 = "12846";
+  public static final String s12847 = "12847";
+  public static final String s12848 = "12848";
+  public static final String s12849 = "12849";
+  public static final String s12850 = "12850";
+  public static final String s12851 = "12851";
+  public static final String s12852 = "12852";
+  public static final String s12853 = "12853";
+  public static final String s12854 = "12854";
+  public static final String s12855 = "12855";
+  public static final String s12856 = "12856";
+  public static final String s12857 = "12857";
+  public static final String s12858 = "12858";
+  public static final String s12859 = "12859";
+  public static final String s12860 = "12860";
+  public static final String s12861 = "12861";
+  public static final String s12862 = "12862";
+  public static final String s12863 = "12863";
+  public static final String s12864 = "12864";
+  public static final String s12865 = "12865";
+  public static final String s12866 = "12866";
+  public static final String s12867 = "12867";
+  public static final String s12868 = "12868";
+  public static final String s12869 = "12869";
+  public static final String s12870 = "12870";
+  public static final String s12871 = "12871";
+  public static final String s12872 = "12872";
+  public static final String s12873 = "12873";
+  public static final String s12874 = "12874";
+  public static final String s12875 = "12875";
+  public static final String s12876 = "12876";
+  public static final String s12877 = "12877";
+  public static final String s12878 = "12878";
+  public static final String s12879 = "12879";
+  public static final String s12880 = "12880";
+  public static final String s12881 = "12881";
+  public static final String s12882 = "12882";
+  public static final String s12883 = "12883";
+  public static final String s12884 = "12884";
+  public static final String s12885 = "12885";
+  public static final String s12886 = "12886";
+  public static final String s12887 = "12887";
+  public static final String s12888 = "12888";
+  public static final String s12889 = "12889";
+  public static final String s12890 = "12890";
+  public static final String s12891 = "12891";
+  public static final String s12892 = "12892";
+  public static final String s12893 = "12893";
+  public static final String s12894 = "12894";
+  public static final String s12895 = "12895";
+  public static final String s12896 = "12896";
+  public static final String s12897 = "12897";
+  public static final String s12898 = "12898";
+  public static final String s12899 = "12899";
+  public static final String s12900 = "12900";
+  public static final String s12901 = "12901";
+  public static final String s12902 = "12902";
+  public static final String s12903 = "12903";
+  public static final String s12904 = "12904";
+  public static final String s12905 = "12905";
+  public static final String s12906 = "12906";
+  public static final String s12907 = "12907";
+  public static final String s12908 = "12908";
+  public static final String s12909 = "12909";
+  public static final String s12910 = "12910";
+  public static final String s12911 = "12911";
+  public static final String s12912 = "12912";
+  public static final String s12913 = "12913";
+  public static final String s12914 = "12914";
+  public static final String s12915 = "12915";
+  public static final String s12916 = "12916";
+  public static final String s12917 = "12917";
+  public static final String s12918 = "12918";
+  public static final String s12919 = "12919";
+  public static final String s12920 = "12920";
+  public static final String s12921 = "12921";
+  public static final String s12922 = "12922";
+  public static final String s12923 = "12923";
+  public static final String s12924 = "12924";
+  public static final String s12925 = "12925";
+  public static final String s12926 = "12926";
+  public static final String s12927 = "12927";
+  public static final String s12928 = "12928";
+  public static final String s12929 = "12929";
+  public static final String s12930 = "12930";
+  public static final String s12931 = "12931";
+  public static final String s12932 = "12932";
+  public static final String s12933 = "12933";
+  public static final String s12934 = "12934";
+  public static final String s12935 = "12935";
+  public static final String s12936 = "12936";
+  public static final String s12937 = "12937";
+  public static final String s12938 = "12938";
+  public static final String s12939 = "12939";
+  public static final String s12940 = "12940";
+  public static final String s12941 = "12941";
+  public static final String s12942 = "12942";
+  public static final String s12943 = "12943";
+  public static final String s12944 = "12944";
+  public static final String s12945 = "12945";
+  public static final String s12946 = "12946";
+  public static final String s12947 = "12947";
+  public static final String s12948 = "12948";
+  public static final String s12949 = "12949";
+  public static final String s12950 = "12950";
+  public static final String s12951 = "12951";
+  public static final String s12952 = "12952";
+  public static final String s12953 = "12953";
+  public static final String s12954 = "12954";
+  public static final String s12955 = "12955";
+  public static final String s12956 = "12956";
+  public static final String s12957 = "12957";
+  public static final String s12958 = "12958";
+  public static final String s12959 = "12959";
+  public static final String s12960 = "12960";
+  public static final String s12961 = "12961";
+  public static final String s12962 = "12962";
+  public static final String s12963 = "12963";
+  public static final String s12964 = "12964";
+  public static final String s12965 = "12965";
+  public static final String s12966 = "12966";
+  public static final String s12967 = "12967";
+  public static final String s12968 = "12968";
+  public static final String s12969 = "12969";
+  public static final String s12970 = "12970";
+  public static final String s12971 = "12971";
+  public static final String s12972 = "12972";
+  public static final String s12973 = "12973";
+  public static final String s12974 = "12974";
+  public static final String s12975 = "12975";
+  public static final String s12976 = "12976";
+  public static final String s12977 = "12977";
+  public static final String s12978 = "12978";
+  public static final String s12979 = "12979";
+  public static final String s12980 = "12980";
+  public static final String s12981 = "12981";
+  public static final String s12982 = "12982";
+  public static final String s12983 = "12983";
+  public static final String s12984 = "12984";
+  public static final String s12985 = "12985";
+  public static final String s12986 = "12986";
+  public static final String s12987 = "12987";
+  public static final String s12988 = "12988";
+  public static final String s12989 = "12989";
+  public static final String s12990 = "12990";
+  public static final String s12991 = "12991";
+  public static final String s12992 = "12992";
+  public static final String s12993 = "12993";
+  public static final String s12994 = "12994";
+  public static final String s12995 = "12995";
+  public static final String s12996 = "12996";
+  public static final String s12997 = "12997";
+  public static final String s12998 = "12998";
+  public static final String s12999 = "12999";
+  public static final String s13000 = "13000";
+  public static final String s13001 = "13001";
+  public static final String s13002 = "13002";
+  public static final String s13003 = "13003";
+  public static final String s13004 = "13004";
+  public static final String s13005 = "13005";
+  public static final String s13006 = "13006";
+  public static final String s13007 = "13007";
+  public static final String s13008 = "13008";
+  public static final String s13009 = "13009";
+  public static final String s13010 = "13010";
+  public static final String s13011 = "13011";
+  public static final String s13012 = "13012";
+  public static final String s13013 = "13013";
+  public static final String s13014 = "13014";
+  public static final String s13015 = "13015";
+  public static final String s13016 = "13016";
+  public static final String s13017 = "13017";
+  public static final String s13018 = "13018";
+  public static final String s13019 = "13019";
+  public static final String s13020 = "13020";
+  public static final String s13021 = "13021";
+  public static final String s13022 = "13022";
+  public static final String s13023 = "13023";
+  public static final String s13024 = "13024";
+  public static final String s13025 = "13025";
+  public static final String s13026 = "13026";
+  public static final String s13027 = "13027";
+  public static final String s13028 = "13028";
+  public static final String s13029 = "13029";
+  public static final String s13030 = "13030";
+  public static final String s13031 = "13031";
+  public static final String s13032 = "13032";
+  public static final String s13033 = "13033";
+  public static final String s13034 = "13034";
+  public static final String s13035 = "13035";
+  public static final String s13036 = "13036";
+  public static final String s13037 = "13037";
+  public static final String s13038 = "13038";
+  public static final String s13039 = "13039";
+  public static final String s13040 = "13040";
+  public static final String s13041 = "13041";
+  public static final String s13042 = "13042";
+  public static final String s13043 = "13043";
+  public static final String s13044 = "13044";
+  public static final String s13045 = "13045";
+  public static final String s13046 = "13046";
+  public static final String s13047 = "13047";
+  public static final String s13048 = "13048";
+  public static final String s13049 = "13049";
+  public static final String s13050 = "13050";
+  public static final String s13051 = "13051";
+  public static final String s13052 = "13052";
+  public static final String s13053 = "13053";
+  public static final String s13054 = "13054";
+  public static final String s13055 = "13055";
+  public static final String s13056 = "13056";
+  public static final String s13057 = "13057";
+  public static final String s13058 = "13058";
+  public static final String s13059 = "13059";
+  public static final String s13060 = "13060";
+  public static final String s13061 = "13061";
+  public static final String s13062 = "13062";
+  public static final String s13063 = "13063";
+  public static final String s13064 = "13064";
+  public static final String s13065 = "13065";
+  public static final String s13066 = "13066";
+  public static final String s13067 = "13067";
+  public static final String s13068 = "13068";
+  public static final String s13069 = "13069";
+  public static final String s13070 = "13070";
+  public static final String s13071 = "13071";
+  public static final String s13072 = "13072";
+  public static final String s13073 = "13073";
+  public static final String s13074 = "13074";
+  public static final String s13075 = "13075";
+  public static final String s13076 = "13076";
+  public static final String s13077 = "13077";
+  public static final String s13078 = "13078";
+  public static final String s13079 = "13079";
+  public static final String s13080 = "13080";
+  public static final String s13081 = "13081";
+  public static final String s13082 = "13082";
+  public static final String s13083 = "13083";
+  public static final String s13084 = "13084";
+  public static final String s13085 = "13085";
+  public static final String s13086 = "13086";
+  public static final String s13087 = "13087";
+  public static final String s13088 = "13088";
+  public static final String s13089 = "13089";
+  public static final String s13090 = "13090";
+  public static final String s13091 = "13091";
+  public static final String s13092 = "13092";
+  public static final String s13093 = "13093";
+  public static final String s13094 = "13094";
+  public static final String s13095 = "13095";
+  public static final String s13096 = "13096";
+  public static final String s13097 = "13097";
+  public static final String s13098 = "13098";
+  public static final String s13099 = "13099";
+  public static final String s13100 = "13100";
+  public static final String s13101 = "13101";
+  public static final String s13102 = "13102";
+  public static final String s13103 = "13103";
+  public static final String s13104 = "13104";
+  public static final String s13105 = "13105";
+  public static final String s13106 = "13106";
+  public static final String s13107 = "13107";
+  public static final String s13108 = "13108";
+  public static final String s13109 = "13109";
+  public static final String s13110 = "13110";
+  public static final String s13111 = "13111";
+  public static final String s13112 = "13112";
+  public static final String s13113 = "13113";
+  public static final String s13114 = "13114";
+  public static final String s13115 = "13115";
+  public static final String s13116 = "13116";
+  public static final String s13117 = "13117";
+  public static final String s13118 = "13118";
+  public static final String s13119 = "13119";
+  public static final String s13120 = "13120";
+  public static final String s13121 = "13121";
+  public static final String s13122 = "13122";
+  public static final String s13123 = "13123";
+  public static final String s13124 = "13124";
+  public static final String s13125 = "13125";
+  public static final String s13126 = "13126";
+  public static final String s13127 = "13127";
+  public static final String s13128 = "13128";
+  public static final String s13129 = "13129";
+  public static final String s13130 = "13130";
+  public static final String s13131 = "13131";
+  public static final String s13132 = "13132";
+  public static final String s13133 = "13133";
+  public static final String s13134 = "13134";
+  public static final String s13135 = "13135";
+  public static final String s13136 = "13136";
+  public static final String s13137 = "13137";
+  public static final String s13138 = "13138";
+  public static final String s13139 = "13139";
+  public static final String s13140 = "13140";
+  public static final String s13141 = "13141";
+  public static final String s13142 = "13142";
+  public static final String s13143 = "13143";
+  public static final String s13144 = "13144";
+  public static final String s13145 = "13145";
+  public static final String s13146 = "13146";
+  public static final String s13147 = "13147";
+  public static final String s13148 = "13148";
+  public static final String s13149 = "13149";
+  public static final String s13150 = "13150";
+  public static final String s13151 = "13151";
+  public static final String s13152 = "13152";
+  public static final String s13153 = "13153";
+  public static final String s13154 = "13154";
+  public static final String s13155 = "13155";
+  public static final String s13156 = "13156";
+  public static final String s13157 = "13157";
+  public static final String s13158 = "13158";
+  public static final String s13159 = "13159";
+  public static final String s13160 = "13160";
+  public static final String s13161 = "13161";
+  public static final String s13162 = "13162";
+  public static final String s13163 = "13163";
+  public static final String s13164 = "13164";
+  public static final String s13165 = "13165";
+  public static final String s13166 = "13166";
+  public static final String s13167 = "13167";
+  public static final String s13168 = "13168";
+  public static final String s13169 = "13169";
+  public static final String s13170 = "13170";
+  public static final String s13171 = "13171";
+  public static final String s13172 = "13172";
+  public static final String s13173 = "13173";
+  public static final String s13174 = "13174";
+  public static final String s13175 = "13175";
+  public static final String s13176 = "13176";
+  public static final String s13177 = "13177";
+  public static final String s13178 = "13178";
+  public static final String s13179 = "13179";
+  public static final String s13180 = "13180";
+  public static final String s13181 = "13181";
+  public static final String s13182 = "13182";
+  public static final String s13183 = "13183";
+  public static final String s13184 = "13184";
+  public static final String s13185 = "13185";
+  public static final String s13186 = "13186";
+  public static final String s13187 = "13187";
+  public static final String s13188 = "13188";
+  public static final String s13189 = "13189";
+  public static final String s13190 = "13190";
+  public static final String s13191 = "13191";
+  public static final String s13192 = "13192";
+  public static final String s13193 = "13193";
+  public static final String s13194 = "13194";
+  public static final String s13195 = "13195";
+  public static final String s13196 = "13196";
+  public static final String s13197 = "13197";
+  public static final String s13198 = "13198";
+  public static final String s13199 = "13199";
+  public static final String s13200 = "13200";
+  public static final String s13201 = "13201";
+  public static final String s13202 = "13202";
+  public static final String s13203 = "13203";
+  public static final String s13204 = "13204";
+  public static final String s13205 = "13205";
+  public static final String s13206 = "13206";
+  public static final String s13207 = "13207";
+  public static final String s13208 = "13208";
+  public static final String s13209 = "13209";
+  public static final String s13210 = "13210";
+  public static final String s13211 = "13211";
+  public static final String s13212 = "13212";
+  public static final String s13213 = "13213";
+  public static final String s13214 = "13214";
+  public static final String s13215 = "13215";
+  public static final String s13216 = "13216";
+  public static final String s13217 = "13217";
+  public static final String s13218 = "13218";
+  public static final String s13219 = "13219";
+  public static final String s13220 = "13220";
+  public static final String s13221 = "13221";
+  public static final String s13222 = "13222";
+  public static final String s13223 = "13223";
+  public static final String s13224 = "13224";
+  public static final String s13225 = "13225";
+  public static final String s13226 = "13226";
+  public static final String s13227 = "13227";
+  public static final String s13228 = "13228";
+  public static final String s13229 = "13229";
+  public static final String s13230 = "13230";
+  public static final String s13231 = "13231";
+  public static final String s13232 = "13232";
+  public static final String s13233 = "13233";
+  public static final String s13234 = "13234";
+  public static final String s13235 = "13235";
+  public static final String s13236 = "13236";
+  public static final String s13237 = "13237";
+  public static final String s13238 = "13238";
+  public static final String s13239 = "13239";
+  public static final String s13240 = "13240";
+  public static final String s13241 = "13241";
+  public static final String s13242 = "13242";
+  public static final String s13243 = "13243";
+  public static final String s13244 = "13244";
+  public static final String s13245 = "13245";
+  public static final String s13246 = "13246";
+  public static final String s13247 = "13247";
+  public static final String s13248 = "13248";
+  public static final String s13249 = "13249";
+  public static final String s13250 = "13250";
+  public static final String s13251 = "13251";
+  public static final String s13252 = "13252";
+  public static final String s13253 = "13253";
+  public static final String s13254 = "13254";
+  public static final String s13255 = "13255";
+  public static final String s13256 = "13256";
+  public static final String s13257 = "13257";
+  public static final String s13258 = "13258";
+  public static final String s13259 = "13259";
+  public static final String s13260 = "13260";
+  public static final String s13261 = "13261";
+  public static final String s13262 = "13262";
+  public static final String s13263 = "13263";
+  public static final String s13264 = "13264";
+  public static final String s13265 = "13265";
+  public static final String s13266 = "13266";
+  public static final String s13267 = "13267";
+  public static final String s13268 = "13268";
+  public static final String s13269 = "13269";
+  public static final String s13270 = "13270";
+  public static final String s13271 = "13271";
+  public static final String s13272 = "13272";
+  public static final String s13273 = "13273";
+  public static final String s13274 = "13274";
+  public static final String s13275 = "13275";
+  public static final String s13276 = "13276";
+  public static final String s13277 = "13277";
+  public static final String s13278 = "13278";
+  public static final String s13279 = "13279";
+  public static final String s13280 = "13280";
+  public static final String s13281 = "13281";
+  public static final String s13282 = "13282";
+  public static final String s13283 = "13283";
+  public static final String s13284 = "13284";
+  public static final String s13285 = "13285";
+  public static final String s13286 = "13286";
+  public static final String s13287 = "13287";
+  public static final String s13288 = "13288";
+  public static final String s13289 = "13289";
+  public static final String s13290 = "13290";
+  public static final String s13291 = "13291";
+  public static final String s13292 = "13292";
+  public static final String s13293 = "13293";
+  public static final String s13294 = "13294";
+  public static final String s13295 = "13295";
+  public static final String s13296 = "13296";
+  public static final String s13297 = "13297";
+  public static final String s13298 = "13298";
+  public static final String s13299 = "13299";
+  public static final String s13300 = "13300";
+  public static final String s13301 = "13301";
+  public static final String s13302 = "13302";
+  public static final String s13303 = "13303";
+  public static final String s13304 = "13304";
+  public static final String s13305 = "13305";
+  public static final String s13306 = "13306";
+  public static final String s13307 = "13307";
+  public static final String s13308 = "13308";
+  public static final String s13309 = "13309";
+  public static final String s13310 = "13310";
+  public static final String s13311 = "13311";
+  public static final String s13312 = "13312";
+  public static final String s13313 = "13313";
+  public static final String s13314 = "13314";
+  public static final String s13315 = "13315";
+  public static final String s13316 = "13316";
+  public static final String s13317 = "13317";
+  public static final String s13318 = "13318";
+  public static final String s13319 = "13319";
+  public static final String s13320 = "13320";
+  public static final String s13321 = "13321";
+  public static final String s13322 = "13322";
+  public static final String s13323 = "13323";
+  public static final String s13324 = "13324";
+  public static final String s13325 = "13325";
+  public static final String s13326 = "13326";
+  public static final String s13327 = "13327";
+  public static final String s13328 = "13328";
+  public static final String s13329 = "13329";
+  public static final String s13330 = "13330";
+  public static final String s13331 = "13331";
+  public static final String s13332 = "13332";
+  public static final String s13333 = "13333";
+  public static final String s13334 = "13334";
+  public static final String s13335 = "13335";
+  public static final String s13336 = "13336";
+  public static final String s13337 = "13337";
+  public static final String s13338 = "13338";
+  public static final String s13339 = "13339";
+  public static final String s13340 = "13340";
+  public static final String s13341 = "13341";
+  public static final String s13342 = "13342";
+  public static final String s13343 = "13343";
+  public static final String s13344 = "13344";
+  public static final String s13345 = "13345";
+  public static final String s13346 = "13346";
+  public static final String s13347 = "13347";
+  public static final String s13348 = "13348";
+  public static final String s13349 = "13349";
+  public static final String s13350 = "13350";
+  public static final String s13351 = "13351";
+  public static final String s13352 = "13352";
+  public static final String s13353 = "13353";
+  public static final String s13354 = "13354";
+  public static final String s13355 = "13355";
+  public static final String s13356 = "13356";
+  public static final String s13357 = "13357";
+  public static final String s13358 = "13358";
+  public static final String s13359 = "13359";
+  public static final String s13360 = "13360";
+  public static final String s13361 = "13361";
+  public static final String s13362 = "13362";
+  public static final String s13363 = "13363";
+  public static final String s13364 = "13364";
+  public static final String s13365 = "13365";
+  public static final String s13366 = "13366";
+  public static final String s13367 = "13367";
+  public static final String s13368 = "13368";
+  public static final String s13369 = "13369";
+  public static final String s13370 = "13370";
+  public static final String s13371 = "13371";
+  public static final String s13372 = "13372";
+  public static final String s13373 = "13373";
+  public static final String s13374 = "13374";
+  public static final String s13375 = "13375";
+  public static final String s13376 = "13376";
+  public static final String s13377 = "13377";
+  public static final String s13378 = "13378";
+  public static final String s13379 = "13379";
+  public static final String s13380 = "13380";
+  public static final String s13381 = "13381";
+  public static final String s13382 = "13382";
+  public static final String s13383 = "13383";
+  public static final String s13384 = "13384";
+  public static final String s13385 = "13385";
+  public static final String s13386 = "13386";
+  public static final String s13387 = "13387";
+  public static final String s13388 = "13388";
+  public static final String s13389 = "13389";
+  public static final String s13390 = "13390";
+  public static final String s13391 = "13391";
+  public static final String s13392 = "13392";
+  public static final String s13393 = "13393";
+  public static final String s13394 = "13394";
+  public static final String s13395 = "13395";
+  public static final String s13396 = "13396";
+  public static final String s13397 = "13397";
+  public static final String s13398 = "13398";
+  public static final String s13399 = "13399";
+  public static final String s13400 = "13400";
+  public static final String s13401 = "13401";
+  public static final String s13402 = "13402";
+  public static final String s13403 = "13403";
+  public static final String s13404 = "13404";
+  public static final String s13405 = "13405";
+  public static final String s13406 = "13406";
+  public static final String s13407 = "13407";
+  public static final String s13408 = "13408";
+  public static final String s13409 = "13409";
+  public static final String s13410 = "13410";
+  public static final String s13411 = "13411";
+  public static final String s13412 = "13412";
+  public static final String s13413 = "13413";
+  public static final String s13414 = "13414";
+  public static final String s13415 = "13415";
+  public static final String s13416 = "13416";
+  public static final String s13417 = "13417";
+  public static final String s13418 = "13418";
+  public static final String s13419 = "13419";
+  public static final String s13420 = "13420";
+  public static final String s13421 = "13421";
+  public static final String s13422 = "13422";
+  public static final String s13423 = "13423";
+  public static final String s13424 = "13424";
+  public static final String s13425 = "13425";
+  public static final String s13426 = "13426";
+  public static final String s13427 = "13427";
+  public static final String s13428 = "13428";
+  public static final String s13429 = "13429";
+  public static final String s13430 = "13430";
+  public static final String s13431 = "13431";
+  public static final String s13432 = "13432";
+  public static final String s13433 = "13433";
+  public static final String s13434 = "13434";
+  public static final String s13435 = "13435";
+  public static final String s13436 = "13436";
+  public static final String s13437 = "13437";
+  public static final String s13438 = "13438";
+  public static final String s13439 = "13439";
+  public static final String s13440 = "13440";
+  public static final String s13441 = "13441";
+  public static final String s13442 = "13442";
+  public static final String s13443 = "13443";
+  public static final String s13444 = "13444";
+  public static final String s13445 = "13445";
+  public static final String s13446 = "13446";
+  public static final String s13447 = "13447";
+  public static final String s13448 = "13448";
+  public static final String s13449 = "13449";
+  public static final String s13450 = "13450";
+  public static final String s13451 = "13451";
+  public static final String s13452 = "13452";
+  public static final String s13453 = "13453";
+  public static final String s13454 = "13454";
+  public static final String s13455 = "13455";
+  public static final String s13456 = "13456";
+  public static final String s13457 = "13457";
+  public static final String s13458 = "13458";
+  public static final String s13459 = "13459";
+  public static final String s13460 = "13460";
+  public static final String s13461 = "13461";
+  public static final String s13462 = "13462";
+  public static final String s13463 = "13463";
+  public static final String s13464 = "13464";
+  public static final String s13465 = "13465";
+  public static final String s13466 = "13466";
+  public static final String s13467 = "13467";
+  public static final String s13468 = "13468";
+  public static final String s13469 = "13469";
+  public static final String s13470 = "13470";
+  public static final String s13471 = "13471";
+  public static final String s13472 = "13472";
+  public static final String s13473 = "13473";
+  public static final String s13474 = "13474";
+  public static final String s13475 = "13475";
+  public static final String s13476 = "13476";
+  public static final String s13477 = "13477";
+  public static final String s13478 = "13478";
+  public static final String s13479 = "13479";
+  public static final String s13480 = "13480";
+  public static final String s13481 = "13481";
+  public static final String s13482 = "13482";
+  public static final String s13483 = "13483";
+  public static final String s13484 = "13484";
+  public static final String s13485 = "13485";
+  public static final String s13486 = "13486";
+  public static final String s13487 = "13487";
+  public static final String s13488 = "13488";
+  public static final String s13489 = "13489";
+  public static final String s13490 = "13490";
+  public static final String s13491 = "13491";
+  public static final String s13492 = "13492";
+  public static final String s13493 = "13493";
+  public static final String s13494 = "13494";
+  public static final String s13495 = "13495";
+  public static final String s13496 = "13496";
+  public static final String s13497 = "13497";
+  public static final String s13498 = "13498";
+  public static final String s13499 = "13499";
+  public static final String s13500 = "13500";
+  public static final String s13501 = "13501";
+  public static final String s13502 = "13502";
+  public static final String s13503 = "13503";
+  public static final String s13504 = "13504";
+  public static final String s13505 = "13505";
+  public static final String s13506 = "13506";
+  public static final String s13507 = "13507";
+  public static final String s13508 = "13508";
+  public static final String s13509 = "13509";
+  public static final String s13510 = "13510";
+  public static final String s13511 = "13511";
+  public static final String s13512 = "13512";
+  public static final String s13513 = "13513";
+  public static final String s13514 = "13514";
+  public static final String s13515 = "13515";
+  public static final String s13516 = "13516";
+  public static final String s13517 = "13517";
+  public static final String s13518 = "13518";
+  public static final String s13519 = "13519";
+  public static final String s13520 = "13520";
+  public static final String s13521 = "13521";
+  public static final String s13522 = "13522";
+  public static final String s13523 = "13523";
+  public static final String s13524 = "13524";
+  public static final String s13525 = "13525";
+  public static final String s13526 = "13526";
+  public static final String s13527 = "13527";
+  public static final String s13528 = "13528";
+  public static final String s13529 = "13529";
+  public static final String s13530 = "13530";
+  public static final String s13531 = "13531";
+  public static final String s13532 = "13532";
+  public static final String s13533 = "13533";
+  public static final String s13534 = "13534";
+  public static final String s13535 = "13535";
+  public static final String s13536 = "13536";
+  public static final String s13537 = "13537";
+  public static final String s13538 = "13538";
+  public static final String s13539 = "13539";
+  public static final String s13540 = "13540";
+  public static final String s13541 = "13541";
+  public static final String s13542 = "13542";
+  public static final String s13543 = "13543";
+  public static final String s13544 = "13544";
+  public static final String s13545 = "13545";
+  public static final String s13546 = "13546";
+  public static final String s13547 = "13547";
+  public static final String s13548 = "13548";
+  public static final String s13549 = "13549";
+  public static final String s13550 = "13550";
+  public static final String s13551 = "13551";
+  public static final String s13552 = "13552";
+  public static final String s13553 = "13553";
+  public static final String s13554 = "13554";
+  public static final String s13555 = "13555";
+  public static final String s13556 = "13556";
+  public static final String s13557 = "13557";
+  public static final String s13558 = "13558";
+  public static final String s13559 = "13559";
+  public static final String s13560 = "13560";
+  public static final String s13561 = "13561";
+  public static final String s13562 = "13562";
+  public static final String s13563 = "13563";
+  public static final String s13564 = "13564";
+  public static final String s13565 = "13565";
+  public static final String s13566 = "13566";
+  public static final String s13567 = "13567";
+  public static final String s13568 = "13568";
+  public static final String s13569 = "13569";
+  public static final String s13570 = "13570";
+  public static final String s13571 = "13571";
+  public static final String s13572 = "13572";
+  public static final String s13573 = "13573";
+  public static final String s13574 = "13574";
+  public static final String s13575 = "13575";
+  public static final String s13576 = "13576";
+  public static final String s13577 = "13577";
+  public static final String s13578 = "13578";
+  public static final String s13579 = "13579";
+  public static final String s13580 = "13580";
+  public static final String s13581 = "13581";
+  public static final String s13582 = "13582";
+  public static final String s13583 = "13583";
+  public static final String s13584 = "13584";
+  public static final String s13585 = "13585";
+  public static final String s13586 = "13586";
+  public static final String s13587 = "13587";
+  public static final String s13588 = "13588";
+  public static final String s13589 = "13589";
+  public static final String s13590 = "13590";
+  public static final String s13591 = "13591";
+  public static final String s13592 = "13592";
+  public static final String s13593 = "13593";
+  public static final String s13594 = "13594";
+  public static final String s13595 = "13595";
+  public static final String s13596 = "13596";
+  public static final String s13597 = "13597";
+  public static final String s13598 = "13598";
+  public static final String s13599 = "13599";
+  public static final String s13600 = "13600";
+  public static final String s13601 = "13601";
+  public static final String s13602 = "13602";
+  public static final String s13603 = "13603";
+  public static final String s13604 = "13604";
+  public static final String s13605 = "13605";
+  public static final String s13606 = "13606";
+  public static final String s13607 = "13607";
+  public static final String s13608 = "13608";
+  public static final String s13609 = "13609";
+  public static final String s13610 = "13610";
+  public static final String s13611 = "13611";
+  public static final String s13612 = "13612";
+  public static final String s13613 = "13613";
+  public static final String s13614 = "13614";
+  public static final String s13615 = "13615";
+  public static final String s13616 = "13616";
+  public static final String s13617 = "13617";
+  public static final String s13618 = "13618";
+  public static final String s13619 = "13619";
+  public static final String s13620 = "13620";
+  public static final String s13621 = "13621";
+  public static final String s13622 = "13622";
+  public static final String s13623 = "13623";
+  public static final String s13624 = "13624";
+  public static final String s13625 = "13625";
+  public static final String s13626 = "13626";
+  public static final String s13627 = "13627";
+  public static final String s13628 = "13628";
+  public static final String s13629 = "13629";
+  public static final String s13630 = "13630";
+  public static final String s13631 = "13631";
+  public static final String s13632 = "13632";
+  public static final String s13633 = "13633";
+  public static final String s13634 = "13634";
+  public static final String s13635 = "13635";
+  public static final String s13636 = "13636";
+  public static final String s13637 = "13637";
+  public static final String s13638 = "13638";
+  public static final String s13639 = "13639";
+  public static final String s13640 = "13640";
+  public static final String s13641 = "13641";
+  public static final String s13642 = "13642";
+  public static final String s13643 = "13643";
+  public static final String s13644 = "13644";
+  public static final String s13645 = "13645";
+  public static final String s13646 = "13646";
+  public static final String s13647 = "13647";
+  public static final String s13648 = "13648";
+  public static final String s13649 = "13649";
+  public static final String s13650 = "13650";
+  public static final String s13651 = "13651";
+  public static final String s13652 = "13652";
+  public static final String s13653 = "13653";
+  public static final String s13654 = "13654";
+  public static final String s13655 = "13655";
+  public static final String s13656 = "13656";
+  public static final String s13657 = "13657";
+  public static final String s13658 = "13658";
+  public static final String s13659 = "13659";
+  public static final String s13660 = "13660";
+  public static final String s13661 = "13661";
+  public static final String s13662 = "13662";
+  public static final String s13663 = "13663";
+  public static final String s13664 = "13664";
+  public static final String s13665 = "13665";
+  public static final String s13666 = "13666";
+  public static final String s13667 = "13667";
+  public static final String s13668 = "13668";
+  public static final String s13669 = "13669";
+  public static final String s13670 = "13670";
+  public static final String s13671 = "13671";
+  public static final String s13672 = "13672";
+  public static final String s13673 = "13673";
+  public static final String s13674 = "13674";
+  public static final String s13675 = "13675";
+  public static final String s13676 = "13676";
+  public static final String s13677 = "13677";
+  public static final String s13678 = "13678";
+  public static final String s13679 = "13679";
+  public static final String s13680 = "13680";
+  public static final String s13681 = "13681";
+  public static final String s13682 = "13682";
+  public static final String s13683 = "13683";
+  public static final String s13684 = "13684";
+  public static final String s13685 = "13685";
+  public static final String s13686 = "13686";
+  public static final String s13687 = "13687";
+  public static final String s13688 = "13688";
+  public static final String s13689 = "13689";
+  public static final String s13690 = "13690";
+  public static final String s13691 = "13691";
+  public static final String s13692 = "13692";
+  public static final String s13693 = "13693";
+  public static final String s13694 = "13694";
+  public static final String s13695 = "13695";
+  public static final String s13696 = "13696";
+  public static final String s13697 = "13697";
+  public static final String s13698 = "13698";
+  public static final String s13699 = "13699";
+  public static final String s13700 = "13700";
+  public static final String s13701 = "13701";
+  public static final String s13702 = "13702";
+  public static final String s13703 = "13703";
+  public static final String s13704 = "13704";
+  public static final String s13705 = "13705";
+  public static final String s13706 = "13706";
+  public static final String s13707 = "13707";
+  public static final String s13708 = "13708";
+  public static final String s13709 = "13709";
+  public static final String s13710 = "13710";
+  public static final String s13711 = "13711";
+  public static final String s13712 = "13712";
+  public static final String s13713 = "13713";
+  public static final String s13714 = "13714";
+  public static final String s13715 = "13715";
+  public static final String s13716 = "13716";
+  public static final String s13717 = "13717";
+  public static final String s13718 = "13718";
+  public static final String s13719 = "13719";
+  public static final String s13720 = "13720";
+  public static final String s13721 = "13721";
+  public static final String s13722 = "13722";
+  public static final String s13723 = "13723";
+  public static final String s13724 = "13724";
+  public static final String s13725 = "13725";
+  public static final String s13726 = "13726";
+  public static final String s13727 = "13727";
+  public static final String s13728 = "13728";
+  public static final String s13729 = "13729";
+  public static final String s13730 = "13730";
+  public static final String s13731 = "13731";
+  public static final String s13732 = "13732";
+  public static final String s13733 = "13733";
+  public static final String s13734 = "13734";
+  public static final String s13735 = "13735";
+  public static final String s13736 = "13736";
+  public static final String s13737 = "13737";
+  public static final String s13738 = "13738";
+  public static final String s13739 = "13739";
+  public static final String s13740 = "13740";
+  public static final String s13741 = "13741";
+  public static final String s13742 = "13742";
+  public static final String s13743 = "13743";
+  public static final String s13744 = "13744";
+  public static final String s13745 = "13745";
+  public static final String s13746 = "13746";
+  public static final String s13747 = "13747";
+  public static final String s13748 = "13748";
+  public static final String s13749 = "13749";
+  public static final String s13750 = "13750";
+  public static final String s13751 = "13751";
+  public static final String s13752 = "13752";
+  public static final String s13753 = "13753";
+  public static final String s13754 = "13754";
+  public static final String s13755 = "13755";
+  public static final String s13756 = "13756";
+  public static final String s13757 = "13757";
+  public static final String s13758 = "13758";
+  public static final String s13759 = "13759";
+  public static final String s13760 = "13760";
+  public static final String s13761 = "13761";
+  public static final String s13762 = "13762";
+  public static final String s13763 = "13763";
+  public static final String s13764 = "13764";
+  public static final String s13765 = "13765";
+  public static final String s13766 = "13766";
+  public static final String s13767 = "13767";
+  public static final String s13768 = "13768";
+  public static final String s13769 = "13769";
+  public static final String s13770 = "13770";
+  public static final String s13771 = "13771";
+  public static final String s13772 = "13772";
+  public static final String s13773 = "13773";
+  public static final String s13774 = "13774";
+  public static final String s13775 = "13775";
+  public static final String s13776 = "13776";
+  public static final String s13777 = "13777";
+  public static final String s13778 = "13778";
+  public static final String s13779 = "13779";
+  public static final String s13780 = "13780";
+  public static final String s13781 = "13781";
+  public static final String s13782 = "13782";
+  public static final String s13783 = "13783";
+  public static final String s13784 = "13784";
+  public static final String s13785 = "13785";
+  public static final String s13786 = "13786";
+  public static final String s13787 = "13787";
+  public static final String s13788 = "13788";
+  public static final String s13789 = "13789";
+  public static final String s13790 = "13790";
+  public static final String s13791 = "13791";
+  public static final String s13792 = "13792";
+  public static final String s13793 = "13793";
+  public static final String s13794 = "13794";
+  public static final String s13795 = "13795";
+  public static final String s13796 = "13796";
+  public static final String s13797 = "13797";
+  public static final String s13798 = "13798";
+  public static final String s13799 = "13799";
+  public static final String s13800 = "13800";
+  public static final String s13801 = "13801";
+  public static final String s13802 = "13802";
+  public static final String s13803 = "13803";
+  public static final String s13804 = "13804";
+  public static final String s13805 = "13805";
+  public static final String s13806 = "13806";
+  public static final String s13807 = "13807";
+  public static final String s13808 = "13808";
+  public static final String s13809 = "13809";
+  public static final String s13810 = "13810";
+  public static final String s13811 = "13811";
+  public static final String s13812 = "13812";
+  public static final String s13813 = "13813";
+  public static final String s13814 = "13814";
+  public static final String s13815 = "13815";
+  public static final String s13816 = "13816";
+  public static final String s13817 = "13817";
+  public static final String s13818 = "13818";
+  public static final String s13819 = "13819";
+  public static final String s13820 = "13820";
+  public static final String s13821 = "13821";
+  public static final String s13822 = "13822";
+  public static final String s13823 = "13823";
+  public static final String s13824 = "13824";
+  public static final String s13825 = "13825";
+  public static final String s13826 = "13826";
+  public static final String s13827 = "13827";
+  public static final String s13828 = "13828";
+  public static final String s13829 = "13829";
+  public static final String s13830 = "13830";
+  public static final String s13831 = "13831";
+  public static final String s13832 = "13832";
+  public static final String s13833 = "13833";
+  public static final String s13834 = "13834";
+  public static final String s13835 = "13835";
+  public static final String s13836 = "13836";
+  public static final String s13837 = "13837";
+  public static final String s13838 = "13838";
+  public static final String s13839 = "13839";
+  public static final String s13840 = "13840";
+  public static final String s13841 = "13841";
+  public static final String s13842 = "13842";
+  public static final String s13843 = "13843";
+  public static final String s13844 = "13844";
+  public static final String s13845 = "13845";
+  public static final String s13846 = "13846";
+  public static final String s13847 = "13847";
+  public static final String s13848 = "13848";
+  public static final String s13849 = "13849";
+  public static final String s13850 = "13850";
+  public static final String s13851 = "13851";
+  public static final String s13852 = "13852";
+  public static final String s13853 = "13853";
+  public static final String s13854 = "13854";
+  public static final String s13855 = "13855";
+  public static final String s13856 = "13856";
+  public static final String s13857 = "13857";
+  public static final String s13858 = "13858";
+  public static final String s13859 = "13859";
+  public static final String s13860 = "13860";
+  public static final String s13861 = "13861";
+  public static final String s13862 = "13862";
+  public static final String s13863 = "13863";
+  public static final String s13864 = "13864";
+  public static final String s13865 = "13865";
+  public static final String s13866 = "13866";
+  public static final String s13867 = "13867";
+  public static final String s13868 = "13868";
+  public static final String s13869 = "13869";
+  public static final String s13870 = "13870";
+  public static final String s13871 = "13871";
+  public static final String s13872 = "13872";
+  public static final String s13873 = "13873";
+  public static final String s13874 = "13874";
+  public static final String s13875 = "13875";
+  public static final String s13876 = "13876";
+  public static final String s13877 = "13877";
+  public static final String s13878 = "13878";
+  public static final String s13879 = "13879";
+  public static final String s13880 = "13880";
+  public static final String s13881 = "13881";
+  public static final String s13882 = "13882";
+  public static final String s13883 = "13883";
+  public static final String s13884 = "13884";
+  public static final String s13885 = "13885";
+  public static final String s13886 = "13886";
+  public static final String s13887 = "13887";
+  public static final String s13888 = "13888";
+  public static final String s13889 = "13889";
+  public static final String s13890 = "13890";
+  public static final String s13891 = "13891";
+  public static final String s13892 = "13892";
+  public static final String s13893 = "13893";
+  public static final String s13894 = "13894";
+  public static final String s13895 = "13895";
+  public static final String s13896 = "13896";
+  public static final String s13897 = "13897";
+  public static final String s13898 = "13898";
+  public static final String s13899 = "13899";
+  public static final String s13900 = "13900";
+  public static final String s13901 = "13901";
+  public static final String s13902 = "13902";
+  public static final String s13903 = "13903";
+  public static final String s13904 = "13904";
+  public static final String s13905 = "13905";
+  public static final String s13906 = "13906";
+  public static final String s13907 = "13907";
+  public static final String s13908 = "13908";
+  public static final String s13909 = "13909";
+  public static final String s13910 = "13910";
+  public static final String s13911 = "13911";
+  public static final String s13912 = "13912";
+  public static final String s13913 = "13913";
+  public static final String s13914 = "13914";
+  public static final String s13915 = "13915";
+  public static final String s13916 = "13916";
+  public static final String s13917 = "13917";
+  public static final String s13918 = "13918";
+  public static final String s13919 = "13919";
+  public static final String s13920 = "13920";
+  public static final String s13921 = "13921";
+  public static final String s13922 = "13922";
+  public static final String s13923 = "13923";
+  public static final String s13924 = "13924";
+  public static final String s13925 = "13925";
+  public static final String s13926 = "13926";
+  public static final String s13927 = "13927";
+  public static final String s13928 = "13928";
+  public static final String s13929 = "13929";
+  public static final String s13930 = "13930";
+  public static final String s13931 = "13931";
+  public static final String s13932 = "13932";
+  public static final String s13933 = "13933";
+  public static final String s13934 = "13934";
+  public static final String s13935 = "13935";
+  public static final String s13936 = "13936";
+  public static final String s13937 = "13937";
+  public static final String s13938 = "13938";
+  public static final String s13939 = "13939";
+  public static final String s13940 = "13940";
+  public static final String s13941 = "13941";
+  public static final String s13942 = "13942";
+  public static final String s13943 = "13943";
+  public static final String s13944 = "13944";
+  public static final String s13945 = "13945";
+  public static final String s13946 = "13946";
+  public static final String s13947 = "13947";
+  public static final String s13948 = "13948";
+  public static final String s13949 = "13949";
+  public static final String s13950 = "13950";
+  public static final String s13951 = "13951";
+  public static final String s13952 = "13952";
+  public static final String s13953 = "13953";
+  public static final String s13954 = "13954";
+  public static final String s13955 = "13955";
+  public static final String s13956 = "13956";
+  public static final String s13957 = "13957";
+  public static final String s13958 = "13958";
+  public static final String s13959 = "13959";
+  public static final String s13960 = "13960";
+  public static final String s13961 = "13961";
+  public static final String s13962 = "13962";
+  public static final String s13963 = "13963";
+  public static final String s13964 = "13964";
+  public static final String s13965 = "13965";
+  public static final String s13966 = "13966";
+  public static final String s13967 = "13967";
+  public static final String s13968 = "13968";
+  public static final String s13969 = "13969";
+  public static final String s13970 = "13970";
+  public static final String s13971 = "13971";
+  public static final String s13972 = "13972";
+  public static final String s13973 = "13973";
+  public static final String s13974 = "13974";
+  public static final String s13975 = "13975";
+  public static final String s13976 = "13976";
+  public static final String s13977 = "13977";
+  public static final String s13978 = "13978";
+  public static final String s13979 = "13979";
+  public static final String s13980 = "13980";
+  public static final String s13981 = "13981";
+  public static final String s13982 = "13982";
+  public static final String s13983 = "13983";
+  public static final String s13984 = "13984";
+  public static final String s13985 = "13985";
+  public static final String s13986 = "13986";
+  public static final String s13987 = "13987";
+  public static final String s13988 = "13988";
+  public static final String s13989 = "13989";
+  public static final String s13990 = "13990";
+  public static final String s13991 = "13991";
+  public static final String s13992 = "13992";
+  public static final String s13993 = "13993";
+  public static final String s13994 = "13994";
+  public static final String s13995 = "13995";
+  public static final String s13996 = "13996";
+  public static final String s13997 = "13997";
+  public static final String s13998 = "13998";
+  public static final String s13999 = "13999";
+  public static final String s14000 = "14000";
+  public static final String s14001 = "14001";
+  public static final String s14002 = "14002";
+  public static final String s14003 = "14003";
+  public static final String s14004 = "14004";
+  public static final String s14005 = "14005";
+  public static final String s14006 = "14006";
+  public static final String s14007 = "14007";
+  public static final String s14008 = "14008";
+  public static final String s14009 = "14009";
+  public static final String s14010 = "14010";
+  public static final String s14011 = "14011";
+  public static final String s14012 = "14012";
+  public static final String s14013 = "14013";
+  public static final String s14014 = "14014";
+  public static final String s14015 = "14015";
+  public static final String s14016 = "14016";
+  public static final String s14017 = "14017";
+  public static final String s14018 = "14018";
+  public static final String s14019 = "14019";
+  public static final String s14020 = "14020";
+  public static final String s14021 = "14021";
+  public static final String s14022 = "14022";
+  public static final String s14023 = "14023";
+  public static final String s14024 = "14024";
+  public static final String s14025 = "14025";
+  public static final String s14026 = "14026";
+  public static final String s14027 = "14027";
+  public static final String s14028 = "14028";
+  public static final String s14029 = "14029";
+  public static final String s14030 = "14030";
+  public static final String s14031 = "14031";
+  public static final String s14032 = "14032";
+  public static final String s14033 = "14033";
+  public static final String s14034 = "14034";
+  public static final String s14035 = "14035";
+  public static final String s14036 = "14036";
+  public static final String s14037 = "14037";
+  public static final String s14038 = "14038";
+  public static final String s14039 = "14039";
+  public static final String s14040 = "14040";
+  public static final String s14041 = "14041";
+  public static final String s14042 = "14042";
+  public static final String s14043 = "14043";
+  public static final String s14044 = "14044";
+  public static final String s14045 = "14045";
+  public static final String s14046 = "14046";
+  public static final String s14047 = "14047";
+  public static final String s14048 = "14048";
+  public static final String s14049 = "14049";
+  public static final String s14050 = "14050";
+  public static final String s14051 = "14051";
+  public static final String s14052 = "14052";
+  public static final String s14053 = "14053";
+  public static final String s14054 = "14054";
+  public static final String s14055 = "14055";
+  public static final String s14056 = "14056";
+  public static final String s14057 = "14057";
+  public static final String s14058 = "14058";
+  public static final String s14059 = "14059";
+  public static final String s14060 = "14060";
+  public static final String s14061 = "14061";
+  public static final String s14062 = "14062";
+  public static final String s14063 = "14063";
+  public static final String s14064 = "14064";
+  public static final String s14065 = "14065";
+  public static final String s14066 = "14066";
+  public static final String s14067 = "14067";
+  public static final String s14068 = "14068";
+  public static final String s14069 = "14069";
+  public static final String s14070 = "14070";
+  public static final String s14071 = "14071";
+  public static final String s14072 = "14072";
+  public static final String s14073 = "14073";
+  public static final String s14074 = "14074";
+  public static final String s14075 = "14075";
+  public static final String s14076 = "14076";
+  public static final String s14077 = "14077";
+  public static final String s14078 = "14078";
+  public static final String s14079 = "14079";
+  public static final String s14080 = "14080";
+  public static final String s14081 = "14081";
+  public static final String s14082 = "14082";
+  public static final String s14083 = "14083";
+  public static final String s14084 = "14084";
+  public static final String s14085 = "14085";
+  public static final String s14086 = "14086";
+  public static final String s14087 = "14087";
+  public static final String s14088 = "14088";
+  public static final String s14089 = "14089";
+  public static final String s14090 = "14090";
+  public static final String s14091 = "14091";
+  public static final String s14092 = "14092";
+  public static final String s14093 = "14093";
+  public static final String s14094 = "14094";
+  public static final String s14095 = "14095";
+  public static final String s14096 = "14096";
+  public static final String s14097 = "14097";
+  public static final String s14098 = "14098";
+  public static final String s14099 = "14099";
+  public static final String s14100 = "14100";
+  public static final String s14101 = "14101";
+  public static final String s14102 = "14102";
+  public static final String s14103 = "14103";
+  public static final String s14104 = "14104";
+  public static final String s14105 = "14105";
+  public static final String s14106 = "14106";
+  public static final String s14107 = "14107";
+  public static final String s14108 = "14108";
+  public static final String s14109 = "14109";
+  public static final String s14110 = "14110";
+  public static final String s14111 = "14111";
+  public static final String s14112 = "14112";
+  public static final String s14113 = "14113";
+  public static final String s14114 = "14114";
+  public static final String s14115 = "14115";
+  public static final String s14116 = "14116";
+  public static final String s14117 = "14117";
+  public static final String s14118 = "14118";
+  public static final String s14119 = "14119";
+  public static final String s14120 = "14120";
+  public static final String s14121 = "14121";
+  public static final String s14122 = "14122";
+  public static final String s14123 = "14123";
+  public static final String s14124 = "14124";
+  public static final String s14125 = "14125";
+  public static final String s14126 = "14126";
+  public static final String s14127 = "14127";
+  public static final String s14128 = "14128";
+  public static final String s14129 = "14129";
+  public static final String s14130 = "14130";
+  public static final String s14131 = "14131";
+  public static final String s14132 = "14132";
+  public static final String s14133 = "14133";
+  public static final String s14134 = "14134";
+  public static final String s14135 = "14135";
+  public static final String s14136 = "14136";
+  public static final String s14137 = "14137";
+  public static final String s14138 = "14138";
+  public static final String s14139 = "14139";
+  public static final String s14140 = "14140";
+  public static final String s14141 = "14141";
+  public static final String s14142 = "14142";
+  public static final String s14143 = "14143";
+  public static final String s14144 = "14144";
+  public static final String s14145 = "14145";
+  public static final String s14146 = "14146";
+  public static final String s14147 = "14147";
+  public static final String s14148 = "14148";
+  public static final String s14149 = "14149";
+  public static final String s14150 = "14150";
+  public static final String s14151 = "14151";
+  public static final String s14152 = "14152";
+  public static final String s14153 = "14153";
+  public static final String s14154 = "14154";
+  public static final String s14155 = "14155";
+  public static final String s14156 = "14156";
+  public static final String s14157 = "14157";
+  public static final String s14158 = "14158";
+  public static final String s14159 = "14159";
+  public static final String s14160 = "14160";
+  public static final String s14161 = "14161";
+  public static final String s14162 = "14162";
+  public static final String s14163 = "14163";
+  public static final String s14164 = "14164";
+  public static final String s14165 = "14165";
+  public static final String s14166 = "14166";
+  public static final String s14167 = "14167";
+  public static final String s14168 = "14168";
+  public static final String s14169 = "14169";
+  public static final String s14170 = "14170";
+  public static final String s14171 = "14171";
+  public static final String s14172 = "14172";
+  public static final String s14173 = "14173";
+  public static final String s14174 = "14174";
+  public static final String s14175 = "14175";
+  public static final String s14176 = "14176";
+  public static final String s14177 = "14177";
+  public static final String s14178 = "14178";
+  public static final String s14179 = "14179";
+  public static final String s14180 = "14180";
+  public static final String s14181 = "14181";
+  public static final String s14182 = "14182";
+  public static final String s14183 = "14183";
+  public static final String s14184 = "14184";
+  public static final String s14185 = "14185";
+  public static final String s14186 = "14186";
+  public static final String s14187 = "14187";
+  public static final String s14188 = "14188";
+  public static final String s14189 = "14189";
+  public static final String s14190 = "14190";
+  public static final String s14191 = "14191";
+  public static final String s14192 = "14192";
+  public static final String s14193 = "14193";
+  public static final String s14194 = "14194";
+  public static final String s14195 = "14195";
+  public static final String s14196 = "14196";
+  public static final String s14197 = "14197";
+  public static final String s14198 = "14198";
+  public static final String s14199 = "14199";
+  public static final String s14200 = "14200";
+  public static final String s14201 = "14201";
+  public static final String s14202 = "14202";
+  public static final String s14203 = "14203";
+  public static final String s14204 = "14204";
+  public static final String s14205 = "14205";
+  public static final String s14206 = "14206";
+  public static final String s14207 = "14207";
+  public static final String s14208 = "14208";
+  public static final String s14209 = "14209";
+  public static final String s14210 = "14210";
+  public static final String s14211 = "14211";
+  public static final String s14212 = "14212";
+  public static final String s14213 = "14213";
+  public static final String s14214 = "14214";
+  public static final String s14215 = "14215";
+  public static final String s14216 = "14216";
+  public static final String s14217 = "14217";
+  public static final String s14218 = "14218";
+  public static final String s14219 = "14219";
+  public static final String s14220 = "14220";
+  public static final String s14221 = "14221";
+  public static final String s14222 = "14222";
+  public static final String s14223 = "14223";
+  public static final String s14224 = "14224";
+  public static final String s14225 = "14225";
+  public static final String s14226 = "14226";
+  public static final String s14227 = "14227";
+  public static final String s14228 = "14228";
+  public static final String s14229 = "14229";
+  public static final String s14230 = "14230";
+  public static final String s14231 = "14231";
+  public static final String s14232 = "14232";
+  public static final String s14233 = "14233";
+  public static final String s14234 = "14234";
+  public static final String s14235 = "14235";
+  public static final String s14236 = "14236";
+  public static final String s14237 = "14237";
+  public static final String s14238 = "14238";
+  public static final String s14239 = "14239";
+  public static final String s14240 = "14240";
+  public static final String s14241 = "14241";
+  public static final String s14242 = "14242";
+  public static final String s14243 = "14243";
+  public static final String s14244 = "14244";
+  public static final String s14245 = "14245";
+  public static final String s14246 = "14246";
+  public static final String s14247 = "14247";
+  public static final String s14248 = "14248";
+  public static final String s14249 = "14249";
+  public static final String s14250 = "14250";
+  public static final String s14251 = "14251";
+  public static final String s14252 = "14252";
+  public static final String s14253 = "14253";
+  public static final String s14254 = "14254";
+  public static final String s14255 = "14255";
+  public static final String s14256 = "14256";
+  public static final String s14257 = "14257";
+  public static final String s14258 = "14258";
+  public static final String s14259 = "14259";
+  public static final String s14260 = "14260";
+  public static final String s14261 = "14261";
+  public static final String s14262 = "14262";
+  public static final String s14263 = "14263";
+  public static final String s14264 = "14264";
+  public static final String s14265 = "14265";
+  public static final String s14266 = "14266";
+  public static final String s14267 = "14267";
+  public static final String s14268 = "14268";
+  public static final String s14269 = "14269";
+  public static final String s14270 = "14270";
+  public static final String s14271 = "14271";
+  public static final String s14272 = "14272";
+  public static final String s14273 = "14273";
+  public static final String s14274 = "14274";
+  public static final String s14275 = "14275";
+  public static final String s14276 = "14276";
+  public static final String s14277 = "14277";
+  public static final String s14278 = "14278";
+  public static final String s14279 = "14279";
+  public static final String s14280 = "14280";
+  public static final String s14281 = "14281";
+  public static final String s14282 = "14282";
+  public static final String s14283 = "14283";
+  public static final String s14284 = "14284";
+  public static final String s14285 = "14285";
+  public static final String s14286 = "14286";
+  public static final String s14287 = "14287";
+  public static final String s14288 = "14288";
+  public static final String s14289 = "14289";
+  public static final String s14290 = "14290";
+  public static final String s14291 = "14291";
+  public static final String s14292 = "14292";
+  public static final String s14293 = "14293";
+  public static final String s14294 = "14294";
+  public static final String s14295 = "14295";
+  public static final String s14296 = "14296";
+  public static final String s14297 = "14297";
+  public static final String s14298 = "14298";
+  public static final String s14299 = "14299";
+  public static final String s14300 = "14300";
+  public static final String s14301 = "14301";
+  public static final String s14302 = "14302";
+  public static final String s14303 = "14303";
+  public static final String s14304 = "14304";
+  public static final String s14305 = "14305";
+  public static final String s14306 = "14306";
+  public static final String s14307 = "14307";
+  public static final String s14308 = "14308";
+  public static final String s14309 = "14309";
+  public static final String s14310 = "14310";
+  public static final String s14311 = "14311";
+  public static final String s14312 = "14312";
+  public static final String s14313 = "14313";
+  public static final String s14314 = "14314";
+  public static final String s14315 = "14315";
+  public static final String s14316 = "14316";
+  public static final String s14317 = "14317";
+  public static final String s14318 = "14318";
+  public static final String s14319 = "14319";
+  public static final String s14320 = "14320";
+  public static final String s14321 = "14321";
+  public static final String s14322 = "14322";
+  public static final String s14323 = "14323";
+  public static final String s14324 = "14324";
+  public static final String s14325 = "14325";
+  public static final String s14326 = "14326";
+  public static final String s14327 = "14327";
+  public static final String s14328 = "14328";
+  public static final String s14329 = "14329";
+  public static final String s14330 = "14330";
+  public static final String s14331 = "14331";
+  public static final String s14332 = "14332";
+  public static final String s14333 = "14333";
+  public static final String s14334 = "14334";
+  public static final String s14335 = "14335";
+  public static final String s14336 = "14336";
+  public static final String s14337 = "14337";
+  public static final String s14338 = "14338";
+  public static final String s14339 = "14339";
+  public static final String s14340 = "14340";
+  public static final String s14341 = "14341";
+  public static final String s14342 = "14342";
+  public static final String s14343 = "14343";
+  public static final String s14344 = "14344";
+  public static final String s14345 = "14345";
+  public static final String s14346 = "14346";
+  public static final String s14347 = "14347";
+  public static final String s14348 = "14348";
+  public static final String s14349 = "14349";
+  public static final String s14350 = "14350";
+  public static final String s14351 = "14351";
+  public static final String s14352 = "14352";
+  public static final String s14353 = "14353";
+  public static final String s14354 = "14354";
+  public static final String s14355 = "14355";
+  public static final String s14356 = "14356";
+  public static final String s14357 = "14357";
+  public static final String s14358 = "14358";
+  public static final String s14359 = "14359";
+  public static final String s14360 = "14360";
+  public static final String s14361 = "14361";
+  public static final String s14362 = "14362";
+  public static final String s14363 = "14363";
+  public static final String s14364 = "14364";
+  public static final String s14365 = "14365";
+  public static final String s14366 = "14366";
+  public static final String s14367 = "14367";
+  public static final String s14368 = "14368";
+  public static final String s14369 = "14369";
+  public static final String s14370 = "14370";
+  public static final String s14371 = "14371";
+  public static final String s14372 = "14372";
+  public static final String s14373 = "14373";
+  public static final String s14374 = "14374";
+  public static final String s14375 = "14375";
+  public static final String s14376 = "14376";
+  public static final String s14377 = "14377";
+  public static final String s14378 = "14378";
+  public static final String s14379 = "14379";
+  public static final String s14380 = "14380";
+  public static final String s14381 = "14381";
+  public static final String s14382 = "14382";
+  public static final String s14383 = "14383";
+  public static final String s14384 = "14384";
+  public static final String s14385 = "14385";
+  public static final String s14386 = "14386";
+  public static final String s14387 = "14387";
+  public static final String s14388 = "14388";
+  public static final String s14389 = "14389";
+  public static final String s14390 = "14390";
+  public static final String s14391 = "14391";
+  public static final String s14392 = "14392";
+  public static final String s14393 = "14393";
+  public static final String s14394 = "14394";
+  public static final String s14395 = "14395";
+  public static final String s14396 = "14396";
+  public static final String s14397 = "14397";
+  public static final String s14398 = "14398";
+  public static final String s14399 = "14399";
+  public static final String s14400 = "14400";
+  public static final String s14401 = "14401";
+  public static final String s14402 = "14402";
+  public static final String s14403 = "14403";
+  public static final String s14404 = "14404";
+  public static final String s14405 = "14405";
+  public static final String s14406 = "14406";
+  public static final String s14407 = "14407";
+  public static final String s14408 = "14408";
+  public static final String s14409 = "14409";
+  public static final String s14410 = "14410";
+  public static final String s14411 = "14411";
+  public static final String s14412 = "14412";
+  public static final String s14413 = "14413";
+  public static final String s14414 = "14414";
+  public static final String s14415 = "14415";
+  public static final String s14416 = "14416";
+  public static final String s14417 = "14417";
+  public static final String s14418 = "14418";
+  public static final String s14419 = "14419";
+  public static final String s14420 = "14420";
+  public static final String s14421 = "14421";
+  public static final String s14422 = "14422";
+  public static final String s14423 = "14423";
+  public static final String s14424 = "14424";
+  public static final String s14425 = "14425";
+  public static final String s14426 = "14426";
+  public static final String s14427 = "14427";
+  public static final String s14428 = "14428";
+  public static final String s14429 = "14429";
+  public static final String s14430 = "14430";
+  public static final String s14431 = "14431";
+  public static final String s14432 = "14432";
+  public static final String s14433 = "14433";
+  public static final String s14434 = "14434";
+  public static final String s14435 = "14435";
+  public static final String s14436 = "14436";
+  public static final String s14437 = "14437";
+  public static final String s14438 = "14438";
+  public static final String s14439 = "14439";
+  public static final String s14440 = "14440";
+  public static final String s14441 = "14441";
+  public static final String s14442 = "14442";
+  public static final String s14443 = "14443";
+  public static final String s14444 = "14444";
+  public static final String s14445 = "14445";
+  public static final String s14446 = "14446";
+  public static final String s14447 = "14447";
+  public static final String s14448 = "14448";
+  public static final String s14449 = "14449";
+  public static final String s14450 = "14450";
+  public static final String s14451 = "14451";
+  public static final String s14452 = "14452";
+  public static final String s14453 = "14453";
+  public static final String s14454 = "14454";
+  public static final String s14455 = "14455";
+  public static final String s14456 = "14456";
+  public static final String s14457 = "14457";
+  public static final String s14458 = "14458";
+  public static final String s14459 = "14459";
+  public static final String s14460 = "14460";
+  public static final String s14461 = "14461";
+  public static final String s14462 = "14462";
+  public static final String s14463 = "14463";
+  public static final String s14464 = "14464";
+  public static final String s14465 = "14465";
+  public static final String s14466 = "14466";
+  public static final String s14467 = "14467";
+  public static final String s14468 = "14468";
+  public static final String s14469 = "14469";
+  public static final String s14470 = "14470";
+  public static final String s14471 = "14471";
+  public static final String s14472 = "14472";
+  public static final String s14473 = "14473";
+  public static final String s14474 = "14474";
+  public static final String s14475 = "14475";
+  public static final String s14476 = "14476";
+  public static final String s14477 = "14477";
+  public static final String s14478 = "14478";
+  public static final String s14479 = "14479";
+  public static final String s14480 = "14480";
+  public static final String s14481 = "14481";
+  public static final String s14482 = "14482";
+  public static final String s14483 = "14483";
+  public static final String s14484 = "14484";
+  public static final String s14485 = "14485";
+  public static final String s14486 = "14486";
+  public static final String s14487 = "14487";
+  public static final String s14488 = "14488";
+  public static final String s14489 = "14489";
+  public static final String s14490 = "14490";
+  public static final String s14491 = "14491";
+  public static final String s14492 = "14492";
+  public static final String s14493 = "14493";
+  public static final String s14494 = "14494";
+  public static final String s14495 = "14495";
+  public static final String s14496 = "14496";
+  public static final String s14497 = "14497";
+  public static final String s14498 = "14498";
+  public static final String s14499 = "14499";
+  public static final String s14500 = "14500";
+  public static final String s14501 = "14501";
+  public static final String s14502 = "14502";
+  public static final String s14503 = "14503";
+  public static final String s14504 = "14504";
+  public static final String s14505 = "14505";
+  public static final String s14506 = "14506";
+  public static final String s14507 = "14507";
+  public static final String s14508 = "14508";
+  public static final String s14509 = "14509";
+  public static final String s14510 = "14510";
+  public static final String s14511 = "14511";
+  public static final String s14512 = "14512";
+  public static final String s14513 = "14513";
+  public static final String s14514 = "14514";
+  public static final String s14515 = "14515";
+  public static final String s14516 = "14516";
+  public static final String s14517 = "14517";
+  public static final String s14518 = "14518";
+  public static final String s14519 = "14519";
+  public static final String s14520 = "14520";
+  public static final String s14521 = "14521";
+  public static final String s14522 = "14522";
+  public static final String s14523 = "14523";
+  public static final String s14524 = "14524";
+  public static final String s14525 = "14525";
+  public static final String s14526 = "14526";
+  public static final String s14527 = "14527";
+  public static final String s14528 = "14528";
+  public static final String s14529 = "14529";
+  public static final String s14530 = "14530";
+  public static final String s14531 = "14531";
+  public static final String s14532 = "14532";
+  public static final String s14533 = "14533";
+  public static final String s14534 = "14534";
+  public static final String s14535 = "14535";
+  public static final String s14536 = "14536";
+  public static final String s14537 = "14537";
+  public static final String s14538 = "14538";
+  public static final String s14539 = "14539";
+  public static final String s14540 = "14540";
+  public static final String s14541 = "14541";
+  public static final String s14542 = "14542";
+  public static final String s14543 = "14543";
+  public static final String s14544 = "14544";
+  public static final String s14545 = "14545";
+  public static final String s14546 = "14546";
+  public static final String s14547 = "14547";
+  public static final String s14548 = "14548";
+  public static final String s14549 = "14549";
+  public static final String s14550 = "14550";
+  public static final String s14551 = "14551";
+  public static final String s14552 = "14552";
+  public static final String s14553 = "14553";
+  public static final String s14554 = "14554";
+  public static final String s14555 = "14555";
+  public static final String s14556 = "14556";
+  public static final String s14557 = "14557";
+  public static final String s14558 = "14558";
+  public static final String s14559 = "14559";
+  public static final String s14560 = "14560";
+  public static final String s14561 = "14561";
+  public static final String s14562 = "14562";
+  public static final String s14563 = "14563";
+  public static final String s14564 = "14564";
+  public static final String s14565 = "14565";
+  public static final String s14566 = "14566";
+  public static final String s14567 = "14567";
+  public static final String s14568 = "14568";
+  public static final String s14569 = "14569";
+  public static final String s14570 = "14570";
+  public static final String s14571 = "14571";
+  public static final String s14572 = "14572";
+  public static final String s14573 = "14573";
+  public static final String s14574 = "14574";
+  public static final String s14575 = "14575";
+  public static final String s14576 = "14576";
+  public static final String s14577 = "14577";
+  public static final String s14578 = "14578";
+  public static final String s14579 = "14579";
+  public static final String s14580 = "14580";
+  public static final String s14581 = "14581";
+  public static final String s14582 = "14582";
+  public static final String s14583 = "14583";
+  public static final String s14584 = "14584";
+  public static final String s14585 = "14585";
+  public static final String s14586 = "14586";
+  public static final String s14587 = "14587";
+  public static final String s14588 = "14588";
+  public static final String s14589 = "14589";
+  public static final String s14590 = "14590";
+  public static final String s14591 = "14591";
+  public static final String s14592 = "14592";
+  public static final String s14593 = "14593";
+  public static final String s14594 = "14594";
+  public static final String s14595 = "14595";
+  public static final String s14596 = "14596";
+  public static final String s14597 = "14597";
+  public static final String s14598 = "14598";
+  public static final String s14599 = "14599";
+  public static final String s14600 = "14600";
+  public static final String s14601 = "14601";
+  public static final String s14602 = "14602";
+  public static final String s14603 = "14603";
+  public static final String s14604 = "14604";
+  public static final String s14605 = "14605";
+  public static final String s14606 = "14606";
+  public static final String s14607 = "14607";
+  public static final String s14608 = "14608";
+  public static final String s14609 = "14609";
+  public static final String s14610 = "14610";
+  public static final String s14611 = "14611";
+  public static final String s14612 = "14612";
+  public static final String s14613 = "14613";
+  public static final String s14614 = "14614";
+  public static final String s14615 = "14615";
+  public static final String s14616 = "14616";
+  public static final String s14617 = "14617";
+  public static final String s14618 = "14618";
+  public static final String s14619 = "14619";
+  public static final String s14620 = "14620";
+  public static final String s14621 = "14621";
+  public static final String s14622 = "14622";
+  public static final String s14623 = "14623";
+  public static final String s14624 = "14624";
+  public static final String s14625 = "14625";
+  public static final String s14626 = "14626";
+  public static final String s14627 = "14627";
+  public static final String s14628 = "14628";
+  public static final String s14629 = "14629";
+  public static final String s14630 = "14630";
+  public static final String s14631 = "14631";
+  public static final String s14632 = "14632";
+  public static final String s14633 = "14633";
+  public static final String s14634 = "14634";
+  public static final String s14635 = "14635";
+  public static final String s14636 = "14636";
+  public static final String s14637 = "14637";
+  public static final String s14638 = "14638";
+  public static final String s14639 = "14639";
+  public static final String s14640 = "14640";
+  public static final String s14641 = "14641";
+  public static final String s14642 = "14642";
+  public static final String s14643 = "14643";
+  public static final String s14644 = "14644";
+  public static final String s14645 = "14645";
+  public static final String s14646 = "14646";
+  public static final String s14647 = "14647";
+  public static final String s14648 = "14648";
+  public static final String s14649 = "14649";
+  public static final String s14650 = "14650";
+  public static final String s14651 = "14651";
+  public static final String s14652 = "14652";
+  public static final String s14653 = "14653";
+  public static final String s14654 = "14654";
+  public static final String s14655 = "14655";
+  public static final String s14656 = "14656";
+  public static final String s14657 = "14657";
+  public static final String s14658 = "14658";
+  public static final String s14659 = "14659";
+  public static final String s14660 = "14660";
+  public static final String s14661 = "14661";
+  public static final String s14662 = "14662";
+  public static final String s14663 = "14663";
+  public static final String s14664 = "14664";
+  public static final String s14665 = "14665";
+  public static final String s14666 = "14666";
+  public static final String s14667 = "14667";
+  public static final String s14668 = "14668";
+  public static final String s14669 = "14669";
+  public static final String s14670 = "14670";
+  public static final String s14671 = "14671";
+  public static final String s14672 = "14672";
+  public static final String s14673 = "14673";
+  public static final String s14674 = "14674";
+  public static final String s14675 = "14675";
+  public static final String s14676 = "14676";
+  public static final String s14677 = "14677";
+  public static final String s14678 = "14678";
+  public static final String s14679 = "14679";
+  public static final String s14680 = "14680";
+  public static final String s14681 = "14681";
+  public static final String s14682 = "14682";
+  public static final String s14683 = "14683";
+  public static final String s14684 = "14684";
+  public static final String s14685 = "14685";
+  public static final String s14686 = "14686";
+  public static final String s14687 = "14687";
+  public static final String s14688 = "14688";
+  public static final String s14689 = "14689";
+  public static final String s14690 = "14690";
+  public static final String s14691 = "14691";
+  public static final String s14692 = "14692";
+  public static final String s14693 = "14693";
+  public static final String s14694 = "14694";
+  public static final String s14695 = "14695";
+  public static final String s14696 = "14696";
+  public static final String s14697 = "14697";
+  public static final String s14698 = "14698";
+  public static final String s14699 = "14699";
+  public static final String s14700 = "14700";
+  public static final String s14701 = "14701";
+  public static final String s14702 = "14702";
+  public static final String s14703 = "14703";
+  public static final String s14704 = "14704";
+  public static final String s14705 = "14705";
+  public static final String s14706 = "14706";
+  public static final String s14707 = "14707";
+  public static final String s14708 = "14708";
+  public static final String s14709 = "14709";
+  public static final String s14710 = "14710";
+  public static final String s14711 = "14711";
+  public static final String s14712 = "14712";
+  public static final String s14713 = "14713";
+  public static final String s14714 = "14714";
+  public static final String s14715 = "14715";
+  public static final String s14716 = "14716";
+  public static final String s14717 = "14717";
+  public static final String s14718 = "14718";
+  public static final String s14719 = "14719";
+  public static final String s14720 = "14720";
+  public static final String s14721 = "14721";
+  public static final String s14722 = "14722";
+  public static final String s14723 = "14723";
+  public static final String s14724 = "14724";
+  public static final String s14725 = "14725";
+  public static final String s14726 = "14726";
+  public static final String s14727 = "14727";
+  public static final String s14728 = "14728";
+  public static final String s14729 = "14729";
+  public static final String s14730 = "14730";
+  public static final String s14731 = "14731";
+  public static final String s14732 = "14732";
+  public static final String s14733 = "14733";
+  public static final String s14734 = "14734";
+  public static final String s14735 = "14735";
+  public static final String s14736 = "14736";
+  public static final String s14737 = "14737";
+  public static final String s14738 = "14738";
+  public static final String s14739 = "14739";
+  public static final String s14740 = "14740";
+  public static final String s14741 = "14741";
+  public static final String s14742 = "14742";
+  public static final String s14743 = "14743";
+  public static final String s14744 = "14744";
+  public static final String s14745 = "14745";
+  public static final String s14746 = "14746";
+  public static final String s14747 = "14747";
+  public static final String s14748 = "14748";
+  public static final String s14749 = "14749";
+  public static final String s14750 = "14750";
+  public static final String s14751 = "14751";
+  public static final String s14752 = "14752";
+  public static final String s14753 = "14753";
+  public static final String s14754 = "14754";
+  public static final String s14755 = "14755";
+  public static final String s14756 = "14756";
+  public static final String s14757 = "14757";
+  public static final String s14758 = "14758";
+  public static final String s14759 = "14759";
+  public static final String s14760 = "14760";
+  public static final String s14761 = "14761";
+  public static final String s14762 = "14762";
+  public static final String s14763 = "14763";
+  public static final String s14764 = "14764";
+  public static final String s14765 = "14765";
+  public static final String s14766 = "14766";
+  public static final String s14767 = "14767";
+  public static final String s14768 = "14768";
+  public static final String s14769 = "14769";
+  public static final String s14770 = "14770";
+  public static final String s14771 = "14771";
+  public static final String s14772 = "14772";
+  public static final String s14773 = "14773";
+  public static final String s14774 = "14774";
+  public static final String s14775 = "14775";
+  public static final String s14776 = "14776";
+  public static final String s14777 = "14777";
+  public static final String s14778 = "14778";
+  public static final String s14779 = "14779";
+  public static final String s14780 = "14780";
+  public static final String s14781 = "14781";
+  public static final String s14782 = "14782";
+  public static final String s14783 = "14783";
+  public static final String s14784 = "14784";
+  public static final String s14785 = "14785";
+  public static final String s14786 = "14786";
+  public static final String s14787 = "14787";
+  public static final String s14788 = "14788";
+  public static final String s14789 = "14789";
+  public static final String s14790 = "14790";
+  public static final String s14791 = "14791";
+  public static final String s14792 = "14792";
+  public static final String s14793 = "14793";
+  public static final String s14794 = "14794";
+  public static final String s14795 = "14795";
+  public static final String s14796 = "14796";
+  public static final String s14797 = "14797";
+  public static final String s14798 = "14798";
+  public static final String s14799 = "14799";
+  public static final String s14800 = "14800";
+  public static final String s14801 = "14801";
+  public static final String s14802 = "14802";
+  public static final String s14803 = "14803";
+  public static final String s14804 = "14804";
+  public static final String s14805 = "14805";
+  public static final String s14806 = "14806";
+  public static final String s14807 = "14807";
+  public static final String s14808 = "14808";
+  public static final String s14809 = "14809";
+  public static final String s14810 = "14810";
+  public static final String s14811 = "14811";
+  public static final String s14812 = "14812";
+  public static final String s14813 = "14813";
+  public static final String s14814 = "14814";
+  public static final String s14815 = "14815";
+  public static final String s14816 = "14816";
+  public static final String s14817 = "14817";
+  public static final String s14818 = "14818";
+  public static final String s14819 = "14819";
+  public static final String s14820 = "14820";
+  public static final String s14821 = "14821";
+  public static final String s14822 = "14822";
+  public static final String s14823 = "14823";
+  public static final String s14824 = "14824";
+  public static final String s14825 = "14825";
+  public static final String s14826 = "14826";
+  public static final String s14827 = "14827";
+  public static final String s14828 = "14828";
+  public static final String s14829 = "14829";
+  public static final String s14830 = "14830";
+  public static final String s14831 = "14831";
+  public static final String s14832 = "14832";
+  public static final String s14833 = "14833";
+  public static final String s14834 = "14834";
+  public static final String s14835 = "14835";
+  public static final String s14836 = "14836";
+  public static final String s14837 = "14837";
+  public static final String s14838 = "14838";
+  public static final String s14839 = "14839";
+  public static final String s14840 = "14840";
+  public static final String s14841 = "14841";
+  public static final String s14842 = "14842";
+  public static final String s14843 = "14843";
+  public static final String s14844 = "14844";
+  public static final String s14845 = "14845";
+  public static final String s14846 = "14846";
+  public static final String s14847 = "14847";
+  public static final String s14848 = "14848";
+  public static final String s14849 = "14849";
+  public static final String s14850 = "14850";
+  public static final String s14851 = "14851";
+  public static final String s14852 = "14852";
+  public static final String s14853 = "14853";
+  public static final String s14854 = "14854";
+  public static final String s14855 = "14855";
+  public static final String s14856 = "14856";
+  public static final String s14857 = "14857";
+  public static final String s14858 = "14858";
+  public static final String s14859 = "14859";
+  public static final String s14860 = "14860";
+  public static final String s14861 = "14861";
+  public static final String s14862 = "14862";
+  public static final String s14863 = "14863";
+  public static final String s14864 = "14864";
+  public static final String s14865 = "14865";
+  public static final String s14866 = "14866";
+  public static final String s14867 = "14867";
+  public static final String s14868 = "14868";
+  public static final String s14869 = "14869";
+  public static final String s14870 = "14870";
+  public static final String s14871 = "14871";
+  public static final String s14872 = "14872";
+  public static final String s14873 = "14873";
+  public static final String s14874 = "14874";
+  public static final String s14875 = "14875";
+  public static final String s14876 = "14876";
+  public static final String s14877 = "14877";
+  public static final String s14878 = "14878";
+  public static final String s14879 = "14879";
+  public static final String s14880 = "14880";
+  public static final String s14881 = "14881";
+  public static final String s14882 = "14882";
+  public static final String s14883 = "14883";
+  public static final String s14884 = "14884";
+  public static final String s14885 = "14885";
+  public static final String s14886 = "14886";
+  public static final String s14887 = "14887";
+  public static final String s14888 = "14888";
+  public static final String s14889 = "14889";
+  public static final String s14890 = "14890";
+  public static final String s14891 = "14891";
+  public static final String s14892 = "14892";
+  public static final String s14893 = "14893";
+  public static final String s14894 = "14894";
+  public static final String s14895 = "14895";
+  public static final String s14896 = "14896";
+  public static final String s14897 = "14897";
+  public static final String s14898 = "14898";
+  public static final String s14899 = "14899";
+  public static final String s14900 = "14900";
+  public static final String s14901 = "14901";
+  public static final String s14902 = "14902";
+  public static final String s14903 = "14903";
+  public static final String s14904 = "14904";
+  public static final String s14905 = "14905";
+  public static final String s14906 = "14906";
+  public static final String s14907 = "14907";
+  public static final String s14908 = "14908";
+  public static final String s14909 = "14909";
+  public static final String s14910 = "14910";
+  public static final String s14911 = "14911";
+  public static final String s14912 = "14912";
+  public static final String s14913 = "14913";
+  public static final String s14914 = "14914";
+  public static final String s14915 = "14915";
+  public static final String s14916 = "14916";
+  public static final String s14917 = "14917";
+  public static final String s14918 = "14918";
+  public static final String s14919 = "14919";
+  public static final String s14920 = "14920";
+  public static final String s14921 = "14921";
+  public static final String s14922 = "14922";
+  public static final String s14923 = "14923";
+  public static final String s14924 = "14924";
+  public static final String s14925 = "14925";
+  public static final String s14926 = "14926";
+  public static final String s14927 = "14927";
+  public static final String s14928 = "14928";
+  public static final String s14929 = "14929";
+  public static final String s14930 = "14930";
+  public static final String s14931 = "14931";
+  public static final String s14932 = "14932";
+  public static final String s14933 = "14933";
+  public static final String s14934 = "14934";
+  public static final String s14935 = "14935";
+  public static final String s14936 = "14936";
+  public static final String s14937 = "14937";
+  public static final String s14938 = "14938";
+  public static final String s14939 = "14939";
+  public static final String s14940 = "14940";
+  public static final String s14941 = "14941";
+  public static final String s14942 = "14942";
+  public static final String s14943 = "14943";
+  public static final String s14944 = "14944";
+  public static final String s14945 = "14945";
+  public static final String s14946 = "14946";
+  public static final String s14947 = "14947";
+  public static final String s14948 = "14948";
+  public static final String s14949 = "14949";
+  public static final String s14950 = "14950";
+  public static final String s14951 = "14951";
+  public static final String s14952 = "14952";
+  public static final String s14953 = "14953";
+  public static final String s14954 = "14954";
+  public static final String s14955 = "14955";
+  public static final String s14956 = "14956";
+  public static final String s14957 = "14957";
+  public static final String s14958 = "14958";
+  public static final String s14959 = "14959";
+  public static final String s14960 = "14960";
+  public static final String s14961 = "14961";
+  public static final String s14962 = "14962";
+  public static final String s14963 = "14963";
+  public static final String s14964 = "14964";
+  public static final String s14965 = "14965";
+  public static final String s14966 = "14966";
+  public static final String s14967 = "14967";
+  public static final String s14968 = "14968";
+  public static final String s14969 = "14969";
+  public static final String s14970 = "14970";
+  public static final String s14971 = "14971";
+  public static final String s14972 = "14972";
+  public static final String s14973 = "14973";
+  public static final String s14974 = "14974";
+  public static final String s14975 = "14975";
+  public static final String s14976 = "14976";
+  public static final String s14977 = "14977";
+  public static final String s14978 = "14978";
+  public static final String s14979 = "14979";
+  public static final String s14980 = "14980";
+  public static final String s14981 = "14981";
+  public static final String s14982 = "14982";
+  public static final String s14983 = "14983";
+  public static final String s14984 = "14984";
+  public static final String s14985 = "14985";
+  public static final String s14986 = "14986";
+  public static final String s14987 = "14987";
+  public static final String s14988 = "14988";
+  public static final String s14989 = "14989";
+  public static final String s14990 = "14990";
+  public static final String s14991 = "14991";
+  public static final String s14992 = "14992";
+  public static final String s14993 = "14993";
+  public static final String s14994 = "14994";
+  public static final String s14995 = "14995";
+  public static final String s14996 = "14996";
+  public static final String s14997 = "14997";
+  public static final String s14998 = "14998";
+  public static final String s14999 = "14999";
+  public static final String s15000 = "15000";
+  public static final String s15001 = "15001";
+  public static final String s15002 = "15002";
+  public static final String s15003 = "15003";
+  public static final String s15004 = "15004";
+  public static final String s15005 = "15005";
+  public static final String s15006 = "15006";
+  public static final String s15007 = "15007";
+  public static final String s15008 = "15008";
+  public static final String s15009 = "15009";
+  public static final String s15010 = "15010";
+  public static final String s15011 = "15011";
+  public static final String s15012 = "15012";
+  public static final String s15013 = "15013";
+  public static final String s15014 = "15014";
+  public static final String s15015 = "15015";
+  public static final String s15016 = "15016";
+  public static final String s15017 = "15017";
+  public static final String s15018 = "15018";
+  public static final String s15019 = "15019";
+  public static final String s15020 = "15020";
+  public static final String s15021 = "15021";
+  public static final String s15022 = "15022";
+  public static final String s15023 = "15023";
+  public static final String s15024 = "15024";
+  public static final String s15025 = "15025";
+  public static final String s15026 = "15026";
+  public static final String s15027 = "15027";
+  public static final String s15028 = "15028";
+  public static final String s15029 = "15029";
+  public static final String s15030 = "15030";
+  public static final String s15031 = "15031";
+  public static final String s15032 = "15032";
+  public static final String s15033 = "15033";
+  public static final String s15034 = "15034";
+  public static final String s15035 = "15035";
+  public static final String s15036 = "15036";
+  public static final String s15037 = "15037";
+  public static final String s15038 = "15038";
+  public static final String s15039 = "15039";
+  public static final String s15040 = "15040";
+  public static final String s15041 = "15041";
+  public static final String s15042 = "15042";
+  public static final String s15043 = "15043";
+  public static final String s15044 = "15044";
+  public static final String s15045 = "15045";
+  public static final String s15046 = "15046";
+  public static final String s15047 = "15047";
+  public static final String s15048 = "15048";
+  public static final String s15049 = "15049";
+  public static final String s15050 = "15050";
+  public static final String s15051 = "15051";
+  public static final String s15052 = "15052";
+  public static final String s15053 = "15053";
+  public static final String s15054 = "15054";
+  public static final String s15055 = "15055";
+  public static final String s15056 = "15056";
+  public static final String s15057 = "15057";
+  public static final String s15058 = "15058";
+  public static final String s15059 = "15059";
+  public static final String s15060 = "15060";
+  public static final String s15061 = "15061";
+  public static final String s15062 = "15062";
+  public static final String s15063 = "15063";
+  public static final String s15064 = "15064";
+  public static final String s15065 = "15065";
+  public static final String s15066 = "15066";
+  public static final String s15067 = "15067";
+  public static final String s15068 = "15068";
+  public static final String s15069 = "15069";
+  public static final String s15070 = "15070";
+  public static final String s15071 = "15071";
+  public static final String s15072 = "15072";
+  public static final String s15073 = "15073";
+  public static final String s15074 = "15074";
+  public static final String s15075 = "15075";
+  public static final String s15076 = "15076";
+  public static final String s15077 = "15077";
+  public static final String s15078 = "15078";
+  public static final String s15079 = "15079";
+  public static final String s15080 = "15080";
+  public static final String s15081 = "15081";
+  public static final String s15082 = "15082";
+  public static final String s15083 = "15083";
+  public static final String s15084 = "15084";
+  public static final String s15085 = "15085";
+  public static final String s15086 = "15086";
+  public static final String s15087 = "15087";
+  public static final String s15088 = "15088";
+  public static final String s15089 = "15089";
+  public static final String s15090 = "15090";
+  public static final String s15091 = "15091";
+  public static final String s15092 = "15092";
+  public static final String s15093 = "15093";
+  public static final String s15094 = "15094";
+  public static final String s15095 = "15095";
+  public static final String s15096 = "15096";
+  public static final String s15097 = "15097";
+  public static final String s15098 = "15098";
+  public static final String s15099 = "15099";
+  public static final String s15100 = "15100";
+  public static final String s15101 = "15101";
+  public static final String s15102 = "15102";
+  public static final String s15103 = "15103";
+  public static final String s15104 = "15104";
+  public static final String s15105 = "15105";
+  public static final String s15106 = "15106";
+  public static final String s15107 = "15107";
+  public static final String s15108 = "15108";
+  public static final String s15109 = "15109";
+  public static final String s15110 = "15110";
+  public static final String s15111 = "15111";
+  public static final String s15112 = "15112";
+  public static final String s15113 = "15113";
+  public static final String s15114 = "15114";
+  public static final String s15115 = "15115";
+  public static final String s15116 = "15116";
+  public static final String s15117 = "15117";
+  public static final String s15118 = "15118";
+  public static final String s15119 = "15119";
+  public static final String s15120 = "15120";
+  public static final String s15121 = "15121";
+  public static final String s15122 = "15122";
+  public static final String s15123 = "15123";
+  public static final String s15124 = "15124";
+  public static final String s15125 = "15125";
+  public static final String s15126 = "15126";
+  public static final String s15127 = "15127";
+  public static final String s15128 = "15128";
+  public static final String s15129 = "15129";
+  public static final String s15130 = "15130";
+  public static final String s15131 = "15131";
+  public static final String s15132 = "15132";
+  public static final String s15133 = "15133";
+  public static final String s15134 = "15134";
+  public static final String s15135 = "15135";
+  public static final String s15136 = "15136";
+  public static final String s15137 = "15137";
+  public static final String s15138 = "15138";
+  public static final String s15139 = "15139";
+  public static final String s15140 = "15140";
+  public static final String s15141 = "15141";
+  public static final String s15142 = "15142";
+  public static final String s15143 = "15143";
+  public static final String s15144 = "15144";
+  public static final String s15145 = "15145";
+  public static final String s15146 = "15146";
+  public static final String s15147 = "15147";
+  public static final String s15148 = "15148";
+  public static final String s15149 = "15149";
+  public static final String s15150 = "15150";
+  public static final String s15151 = "15151";
+  public static final String s15152 = "15152";
+  public static final String s15153 = "15153";
+  public static final String s15154 = "15154";
+  public static final String s15155 = "15155";
+  public static final String s15156 = "15156";
+  public static final String s15157 = "15157";
+  public static final String s15158 = "15158";
+  public static final String s15159 = "15159";
+  public static final String s15160 = "15160";
+  public static final String s15161 = "15161";
+  public static final String s15162 = "15162";
+  public static final String s15163 = "15163";
+  public static final String s15164 = "15164";
+  public static final String s15165 = "15165";
+  public static final String s15166 = "15166";
+  public static final String s15167 = "15167";
+  public static final String s15168 = "15168";
+  public static final String s15169 = "15169";
+  public static final String s15170 = "15170";
+  public static final String s15171 = "15171";
+  public static final String s15172 = "15172";
+  public static final String s15173 = "15173";
+  public static final String s15174 = "15174";
+  public static final String s15175 = "15175";
+  public static final String s15176 = "15176";
+  public static final String s15177 = "15177";
+  public static final String s15178 = "15178";
+  public static final String s15179 = "15179";
+  public static final String s15180 = "15180";
+  public static final String s15181 = "15181";
+  public static final String s15182 = "15182";
+  public static final String s15183 = "15183";
+  public static final String s15184 = "15184";
+  public static final String s15185 = "15185";
+  public static final String s15186 = "15186";
+  public static final String s15187 = "15187";
+  public static final String s15188 = "15188";
+  public static final String s15189 = "15189";
+  public static final String s15190 = "15190";
+  public static final String s15191 = "15191";
+  public static final String s15192 = "15192";
+  public static final String s15193 = "15193";
+  public static final String s15194 = "15194";
+  public static final String s15195 = "15195";
+  public static final String s15196 = "15196";
+  public static final String s15197 = "15197";
+  public static final String s15198 = "15198";
+  public static final String s15199 = "15199";
+  public static final String s15200 = "15200";
+  public static final String s15201 = "15201";
+  public static final String s15202 = "15202";
+  public static final String s15203 = "15203";
+  public static final String s15204 = "15204";
+  public static final String s15205 = "15205";
+  public static final String s15206 = "15206";
+  public static final String s15207 = "15207";
+  public static final String s15208 = "15208";
+  public static final String s15209 = "15209";
+  public static final String s15210 = "15210";
+  public static final String s15211 = "15211";
+  public static final String s15212 = "15212";
+  public static final String s15213 = "15213";
+  public static final String s15214 = "15214";
+  public static final String s15215 = "15215";
+  public static final String s15216 = "15216";
+  public static final String s15217 = "15217";
+  public static final String s15218 = "15218";
+  public static final String s15219 = "15219";
+  public static final String s15220 = "15220";
+  public static final String s15221 = "15221";
+  public static final String s15222 = "15222";
+  public static final String s15223 = "15223";
+  public static final String s15224 = "15224";
+  public static final String s15225 = "15225";
+  public static final String s15226 = "15226";
+  public static final String s15227 = "15227";
+  public static final String s15228 = "15228";
+  public static final String s15229 = "15229";
+  public static final String s15230 = "15230";
+  public static final String s15231 = "15231";
+  public static final String s15232 = "15232";
+  public static final String s15233 = "15233";
+  public static final String s15234 = "15234";
+  public static final String s15235 = "15235";
+  public static final String s15236 = "15236";
+  public static final String s15237 = "15237";
+  public static final String s15238 = "15238";
+  public static final String s15239 = "15239";
+  public static final String s15240 = "15240";
+  public static final String s15241 = "15241";
+  public static final String s15242 = "15242";
+  public static final String s15243 = "15243";
+  public static final String s15244 = "15244";
+  public static final String s15245 = "15245";
+  public static final String s15246 = "15246";
+  public static final String s15247 = "15247";
+  public static final String s15248 = "15248";
+  public static final String s15249 = "15249";
+  public static final String s15250 = "15250";
+  public static final String s15251 = "15251";
+  public static final String s15252 = "15252";
+  public static final String s15253 = "15253";
+  public static final String s15254 = "15254";
+  public static final String s15255 = "15255";
+  public static final String s15256 = "15256";
+  public static final String s15257 = "15257";
+  public static final String s15258 = "15258";
+  public static final String s15259 = "15259";
+  public static final String s15260 = "15260";
+  public static final String s15261 = "15261";
+  public static final String s15262 = "15262";
+  public static final String s15263 = "15263";
+  public static final String s15264 = "15264";
+  public static final String s15265 = "15265";
+  public static final String s15266 = "15266";
+  public static final String s15267 = "15267";
+  public static final String s15268 = "15268";
+  public static final String s15269 = "15269";
+  public static final String s15270 = "15270";
+  public static final String s15271 = "15271";
+  public static final String s15272 = "15272";
+  public static final String s15273 = "15273";
+  public static final String s15274 = "15274";
+  public static final String s15275 = "15275";
+  public static final String s15276 = "15276";
+  public static final String s15277 = "15277";
+  public static final String s15278 = "15278";
+  public static final String s15279 = "15279";
+  public static final String s15280 = "15280";
+  public static final String s15281 = "15281";
+  public static final String s15282 = "15282";
+  public static final String s15283 = "15283";
+  public static final String s15284 = "15284";
+  public static final String s15285 = "15285";
+  public static final String s15286 = "15286";
+  public static final String s15287 = "15287";
+  public static final String s15288 = "15288";
+  public static final String s15289 = "15289";
+  public static final String s15290 = "15290";
+  public static final String s15291 = "15291";
+  public static final String s15292 = "15292";
+  public static final String s15293 = "15293";
+  public static final String s15294 = "15294";
+  public static final String s15295 = "15295";
+  public static final String s15296 = "15296";
+  public static final String s15297 = "15297";
+  public static final String s15298 = "15298";
+  public static final String s15299 = "15299";
+  public static final String s15300 = "15300";
+  public static final String s15301 = "15301";
+  public static final String s15302 = "15302";
+  public static final String s15303 = "15303";
+  public static final String s15304 = "15304";
+  public static final String s15305 = "15305";
+  public static final String s15306 = "15306";
+  public static final String s15307 = "15307";
+  public static final String s15308 = "15308";
+  public static final String s15309 = "15309";
+  public static final String s15310 = "15310";
+  public static final String s15311 = "15311";
+  public static final String s15312 = "15312";
+  public static final String s15313 = "15313";
+  public static final String s15314 = "15314";
+  public static final String s15315 = "15315";
+  public static final String s15316 = "15316";
+  public static final String s15317 = "15317";
+  public static final String s15318 = "15318";
+  public static final String s15319 = "15319";
+  public static final String s15320 = "15320";
+  public static final String s15321 = "15321";
+  public static final String s15322 = "15322";
+  public static final String s15323 = "15323";
+  public static final String s15324 = "15324";
+  public static final String s15325 = "15325";
+  public static final String s15326 = "15326";
+  public static final String s15327 = "15327";
+  public static final String s15328 = "15328";
+  public static final String s15329 = "15329";
+  public static final String s15330 = "15330";
+  public static final String s15331 = "15331";
+  public static final String s15332 = "15332";
+  public static final String s15333 = "15333";
+  public static final String s15334 = "15334";
+  public static final String s15335 = "15335";
+  public static final String s15336 = "15336";
+  public static final String s15337 = "15337";
+  public static final String s15338 = "15338";
+  public static final String s15339 = "15339";
+  public static final String s15340 = "15340";
+  public static final String s15341 = "15341";
+  public static final String s15342 = "15342";
+  public static final String s15343 = "15343";
+  public static final String s15344 = "15344";
+  public static final String s15345 = "15345";
+  public static final String s15346 = "15346";
+  public static final String s15347 = "15347";
+  public static final String s15348 = "15348";
+  public static final String s15349 = "15349";
+  public static final String s15350 = "15350";
+  public static final String s15351 = "15351";
+  public static final String s15352 = "15352";
+  public static final String s15353 = "15353";
+  public static final String s15354 = "15354";
+  public static final String s15355 = "15355";
+  public static final String s15356 = "15356";
+  public static final String s15357 = "15357";
+  public static final String s15358 = "15358";
+  public static final String s15359 = "15359";
+  public static final String s15360 = "15360";
+  public static final String s15361 = "15361";
+  public static final String s15362 = "15362";
+  public static final String s15363 = "15363";
+  public static final String s15364 = "15364";
+  public static final String s15365 = "15365";
+  public static final String s15366 = "15366";
+  public static final String s15367 = "15367";
+  public static final String s15368 = "15368";
+  public static final String s15369 = "15369";
+  public static final String s15370 = "15370";
+  public static final String s15371 = "15371";
+  public static final String s15372 = "15372";
+  public static final String s15373 = "15373";
+  public static final String s15374 = "15374";
+  public static final String s15375 = "15375";
+  public static final String s15376 = "15376";
+  public static final String s15377 = "15377";
+  public static final String s15378 = "15378";
+  public static final String s15379 = "15379";
+  public static final String s15380 = "15380";
+  public static final String s15381 = "15381";
+  public static final String s15382 = "15382";
+  public static final String s15383 = "15383";
+  public static final String s15384 = "15384";
+  public static final String s15385 = "15385";
+  public static final String s15386 = "15386";
+  public static final String s15387 = "15387";
+  public static final String s15388 = "15388";
+  public static final String s15389 = "15389";
+  public static final String s15390 = "15390";
+  public static final String s15391 = "15391";
+  public static final String s15392 = "15392";
+  public static final String s15393 = "15393";
+  public static final String s15394 = "15394";
+  public static final String s15395 = "15395";
+  public static final String s15396 = "15396";
+  public static final String s15397 = "15397";
+  public static final String s15398 = "15398";
+  public static final String s15399 = "15399";
+  public static final String s15400 = "15400";
+  public static final String s15401 = "15401";
+  public static final String s15402 = "15402";
+  public static final String s15403 = "15403";
+  public static final String s15404 = "15404";
+  public static final String s15405 = "15405";
+  public static final String s15406 = "15406";
+  public static final String s15407 = "15407";
+  public static final String s15408 = "15408";
+  public static final String s15409 = "15409";
+  public static final String s15410 = "15410";
+  public static final String s15411 = "15411";
+  public static final String s15412 = "15412";
+  public static final String s15413 = "15413";
+  public static final String s15414 = "15414";
+  public static final String s15415 = "15415";
+  public static final String s15416 = "15416";
+  public static final String s15417 = "15417";
+  public static final String s15418 = "15418";
+  public static final String s15419 = "15419";
+  public static final String s15420 = "15420";
+  public static final String s15421 = "15421";
+  public static final String s15422 = "15422";
+  public static final String s15423 = "15423";
+  public static final String s15424 = "15424";
+  public static final String s15425 = "15425";
+  public static final String s15426 = "15426";
+  public static final String s15427 = "15427";
+  public static final String s15428 = "15428";
+  public static final String s15429 = "15429";
+  public static final String s15430 = "15430";
+  public static final String s15431 = "15431";
+  public static final String s15432 = "15432";
+  public static final String s15433 = "15433";
+  public static final String s15434 = "15434";
+  public static final String s15435 = "15435";
+  public static final String s15436 = "15436";
+  public static final String s15437 = "15437";
+  public static final String s15438 = "15438";
+  public static final String s15439 = "15439";
+  public static final String s15440 = "15440";
+  public static final String s15441 = "15441";
+  public static final String s15442 = "15442";
+  public static final String s15443 = "15443";
+  public static final String s15444 = "15444";
+  public static final String s15445 = "15445";
+  public static final String s15446 = "15446";
+  public static final String s15447 = "15447";
+  public static final String s15448 = "15448";
+  public static final String s15449 = "15449";
+  public static final String s15450 = "15450";
+  public static final String s15451 = "15451";
+  public static final String s15452 = "15452";
+  public static final String s15453 = "15453";
+  public static final String s15454 = "15454";
+  public static final String s15455 = "15455";
+  public static final String s15456 = "15456";
+  public static final String s15457 = "15457";
+  public static final String s15458 = "15458";
+  public static final String s15459 = "15459";
+  public static final String s15460 = "15460";
+  public static final String s15461 = "15461";
+  public static final String s15462 = "15462";
+  public static final String s15463 = "15463";
+  public static final String s15464 = "15464";
+  public static final String s15465 = "15465";
+  public static final String s15466 = "15466";
+  public static final String s15467 = "15467";
+  public static final String s15468 = "15468";
+  public static final String s15469 = "15469";
+  public static final String s15470 = "15470";
+  public static final String s15471 = "15471";
+  public static final String s15472 = "15472";
+  public static final String s15473 = "15473";
+  public static final String s15474 = "15474";
+  public static final String s15475 = "15475";
+  public static final String s15476 = "15476";
+  public static final String s15477 = "15477";
+  public static final String s15478 = "15478";
+  public static final String s15479 = "15479";
+  public static final String s15480 = "15480";
+  public static final String s15481 = "15481";
+  public static final String s15482 = "15482";
+  public static final String s15483 = "15483";
+  public static final String s15484 = "15484";
+  public static final String s15485 = "15485";
+  public static final String s15486 = "15486";
+  public static final String s15487 = "15487";
+  public static final String s15488 = "15488";
+  public static final String s15489 = "15489";
+  public static final String s15490 = "15490";
+  public static final String s15491 = "15491";
+  public static final String s15492 = "15492";
+  public static final String s15493 = "15493";
+  public static final String s15494 = "15494";
+  public static final String s15495 = "15495";
+  public static final String s15496 = "15496";
+  public static final String s15497 = "15497";
+  public static final String s15498 = "15498";
+  public static final String s15499 = "15499";
+  public static final String s15500 = "15500";
+  public static final String s15501 = "15501";
+  public static final String s15502 = "15502";
+  public static final String s15503 = "15503";
+  public static final String s15504 = "15504";
+  public static final String s15505 = "15505";
+  public static final String s15506 = "15506";
+  public static final String s15507 = "15507";
+  public static final String s15508 = "15508";
+  public static final String s15509 = "15509";
+  public static final String s15510 = "15510";
+  public static final String s15511 = "15511";
+  public static final String s15512 = "15512";
+  public static final String s15513 = "15513";
+  public static final String s15514 = "15514";
+  public static final String s15515 = "15515";
+  public static final String s15516 = "15516";
+  public static final String s15517 = "15517";
+  public static final String s15518 = "15518";
+  public static final String s15519 = "15519";
+  public static final String s15520 = "15520";
+  public static final String s15521 = "15521";
+  public static final String s15522 = "15522";
+  public static final String s15523 = "15523";
+  public static final String s15524 = "15524";
+  public static final String s15525 = "15525";
+  public static final String s15526 = "15526";
+  public static final String s15527 = "15527";
+  public static final String s15528 = "15528";
+  public static final String s15529 = "15529";
+  public static final String s15530 = "15530";
+  public static final String s15531 = "15531";
+  public static final String s15532 = "15532";
+  public static final String s15533 = "15533";
+  public static final String s15534 = "15534";
+  public static final String s15535 = "15535";
+  public static final String s15536 = "15536";
+  public static final String s15537 = "15537";
+  public static final String s15538 = "15538";
+  public static final String s15539 = "15539";
+  public static final String s15540 = "15540";
+  public static final String s15541 = "15541";
+  public static final String s15542 = "15542";
+  public static final String s15543 = "15543";
+  public static final String s15544 = "15544";
+  public static final String s15545 = "15545";
+  public static final String s15546 = "15546";
+  public static final String s15547 = "15547";
+  public static final String s15548 = "15548";
+  public static final String s15549 = "15549";
+  public static final String s15550 = "15550";
+  public static final String s15551 = "15551";
+  public static final String s15552 = "15552";
+  public static final String s15553 = "15553";
+  public static final String s15554 = "15554";
+  public static final String s15555 = "15555";
+  public static final String s15556 = "15556";
+  public static final String s15557 = "15557";
+  public static final String s15558 = "15558";
+  public static final String s15559 = "15559";
+  public static final String s15560 = "15560";
+  public static final String s15561 = "15561";
+  public static final String s15562 = "15562";
+  public static final String s15563 = "15563";
+  public static final String s15564 = "15564";
+  public static final String s15565 = "15565";
+  public static final String s15566 = "15566";
+  public static final String s15567 = "15567";
+  public static final String s15568 = "15568";
+  public static final String s15569 = "15569";
+  public static final String s15570 = "15570";
+  public static final String s15571 = "15571";
+  public static final String s15572 = "15572";
+  public static final String s15573 = "15573";
+  public static final String s15574 = "15574";
+  public static final String s15575 = "15575";
+  public static final String s15576 = "15576";
+  public static final String s15577 = "15577";
+  public static final String s15578 = "15578";
+  public static final String s15579 = "15579";
+  public static final String s15580 = "15580";
+  public static final String s15581 = "15581";
+  public static final String s15582 = "15582";
+  public static final String s15583 = "15583";
+  public static final String s15584 = "15584";
+  public static final String s15585 = "15585";
+  public static final String s15586 = "15586";
+  public static final String s15587 = "15587";
+  public static final String s15588 = "15588";
+  public static final String s15589 = "15589";
+  public static final String s15590 = "15590";
+  public static final String s15591 = "15591";
+  public static final String s15592 = "15592";
+  public static final String s15593 = "15593";
+  public static final String s15594 = "15594";
+  public static final String s15595 = "15595";
+  public static final String s15596 = "15596";
+  public static final String s15597 = "15597";
+  public static final String s15598 = "15598";
+  public static final String s15599 = "15599";
+  public static final String s15600 = "15600";
+  public static final String s15601 = "15601";
+  public static final String s15602 = "15602";
+  public static final String s15603 = "15603";
+  public static final String s15604 = "15604";
+  public static final String s15605 = "15605";
+  public static final String s15606 = "15606";
+  public static final String s15607 = "15607";
+  public static final String s15608 = "15608";
+  public static final String s15609 = "15609";
+  public static final String s15610 = "15610";
+  public static final String s15611 = "15611";
+  public static final String s15612 = "15612";
+  public static final String s15613 = "15613";
+  public static final String s15614 = "15614";
+  public static final String s15615 = "15615";
+  public static final String s15616 = "15616";
+  public static final String s15617 = "15617";
+  public static final String s15618 = "15618";
+  public static final String s15619 = "15619";
+  public static final String s15620 = "15620";
+  public static final String s15621 = "15621";
+  public static final String s15622 = "15622";
+  public static final String s15623 = "15623";
+  public static final String s15624 = "15624";
+  public static final String s15625 = "15625";
+  public static final String s15626 = "15626";
+  public static final String s15627 = "15627";
+  public static final String s15628 = "15628";
+  public static final String s15629 = "15629";
+  public static final String s15630 = "15630";
+  public static final String s15631 = "15631";
+  public static final String s15632 = "15632";
+  public static final String s15633 = "15633";
+  public static final String s15634 = "15634";
+  public static final String s15635 = "15635";
+  public static final String s15636 = "15636";
+  public static final String s15637 = "15637";
+  public static final String s15638 = "15638";
+  public static final String s15639 = "15639";
+  public static final String s15640 = "15640";
+  public static final String s15641 = "15641";
+  public static final String s15642 = "15642";
+  public static final String s15643 = "15643";
+  public static final String s15644 = "15644";
+  public static final String s15645 = "15645";
+  public static final String s15646 = "15646";
+  public static final String s15647 = "15647";
+  public static final String s15648 = "15648";
+  public static final String s15649 = "15649";
+  public static final String s15650 = "15650";
+  public static final String s15651 = "15651";
+  public static final String s15652 = "15652";
+  public static final String s15653 = "15653";
+  public static final String s15654 = "15654";
+  public static final String s15655 = "15655";
+  public static final String s15656 = "15656";
+  public static final String s15657 = "15657";
+  public static final String s15658 = "15658";
+  public static final String s15659 = "15659";
+  public static final String s15660 = "15660";
+  public static final String s15661 = "15661";
+  public static final String s15662 = "15662";
+  public static final String s15663 = "15663";
+  public static final String s15664 = "15664";
+  public static final String s15665 = "15665";
+  public static final String s15666 = "15666";
+  public static final String s15667 = "15667";
+  public static final String s15668 = "15668";
+  public static final String s15669 = "15669";
+  public static final String s15670 = "15670";
+  public static final String s15671 = "15671";
+  public static final String s15672 = "15672";
+  public static final String s15673 = "15673";
+  public static final String s15674 = "15674";
+  public static final String s15675 = "15675";
+  public static final String s15676 = "15676";
+  public static final String s15677 = "15677";
+  public static final String s15678 = "15678";
+  public static final String s15679 = "15679";
+  public static final String s15680 = "15680";
+  public static final String s15681 = "15681";
+  public static final String s15682 = "15682";
+  public static final String s15683 = "15683";
+  public static final String s15684 = "15684";
+  public static final String s15685 = "15685";
+  public static final String s15686 = "15686";
+  public static final String s15687 = "15687";
+  public static final String s15688 = "15688";
+  public static final String s15689 = "15689";
+  public static final String s15690 = "15690";
+  public static final String s15691 = "15691";
+  public static final String s15692 = "15692";
+  public static final String s15693 = "15693";
+  public static final String s15694 = "15694";
+  public static final String s15695 = "15695";
+  public static final String s15696 = "15696";
+  public static final String s15697 = "15697";
+  public static final String s15698 = "15698";
+  public static final String s15699 = "15699";
+  public static final String s15700 = "15700";
+  public static final String s15701 = "15701";
+  public static final String s15702 = "15702";
+  public static final String s15703 = "15703";
+  public static final String s15704 = "15704";
+  public static final String s15705 = "15705";
+  public static final String s15706 = "15706";
+  public static final String s15707 = "15707";
+  public static final String s15708 = "15708";
+  public static final String s15709 = "15709";
+  public static final String s15710 = "15710";
+  public static final String s15711 = "15711";
+  public static final String s15712 = "15712";
+  public static final String s15713 = "15713";
+  public static final String s15714 = "15714";
+  public static final String s15715 = "15715";
+  public static final String s15716 = "15716";
+  public static final String s15717 = "15717";
+  public static final String s15718 = "15718";
+  public static final String s15719 = "15719";
+  public static final String s15720 = "15720";
+  public static final String s15721 = "15721";
+  public static final String s15722 = "15722";
+  public static final String s15723 = "15723";
+  public static final String s15724 = "15724";
+  public static final String s15725 = "15725";
+  public static final String s15726 = "15726";
+  public static final String s15727 = "15727";
+  public static final String s15728 = "15728";
+  public static final String s15729 = "15729";
+  public static final String s15730 = "15730";
+  public static final String s15731 = "15731";
+  public static final String s15732 = "15732";
+  public static final String s15733 = "15733";
+  public static final String s15734 = "15734";
+  public static final String s15735 = "15735";
+  public static final String s15736 = "15736";
+  public static final String s15737 = "15737";
+  public static final String s15738 = "15738";
+  public static final String s15739 = "15739";
+  public static final String s15740 = "15740";
+  public static final String s15741 = "15741";
+  public static final String s15742 = "15742";
+  public static final String s15743 = "15743";
+  public static final String s15744 = "15744";
+  public static final String s15745 = "15745";
+  public static final String s15746 = "15746";
+  public static final String s15747 = "15747";
+  public static final String s15748 = "15748";
+  public static final String s15749 = "15749";
+  public static final String s15750 = "15750";
+  public static final String s15751 = "15751";
+  public static final String s15752 = "15752";
+  public static final String s15753 = "15753";
+  public static final String s15754 = "15754";
+  public static final String s15755 = "15755";
+  public static final String s15756 = "15756";
+  public static final String s15757 = "15757";
+  public static final String s15758 = "15758";
+  public static final String s15759 = "15759";
+  public static final String s15760 = "15760";
+  public static final String s15761 = "15761";
+  public static final String s15762 = "15762";
+  public static final String s15763 = "15763";
+  public static final String s15764 = "15764";
+  public static final String s15765 = "15765";
+  public static final String s15766 = "15766";
+  public static final String s15767 = "15767";
+  public static final String s15768 = "15768";
+  public static final String s15769 = "15769";
+  public static final String s15770 = "15770";
+  public static final String s15771 = "15771";
+  public static final String s15772 = "15772";
+  public static final String s15773 = "15773";
+  public static final String s15774 = "15774";
+  public static final String s15775 = "15775";
+  public static final String s15776 = "15776";
+  public static final String s15777 = "15777";
+  public static final String s15778 = "15778";
+  public static final String s15779 = "15779";
+  public static final String s15780 = "15780";
+  public static final String s15781 = "15781";
+  public static final String s15782 = "15782";
+  public static final String s15783 = "15783";
+  public static final String s15784 = "15784";
+  public static final String s15785 = "15785";
+  public static final String s15786 = "15786";
+  public static final String s15787 = "15787";
+  public static final String s15788 = "15788";
+  public static final String s15789 = "15789";
+  public static final String s15790 = "15790";
+  public static final String s15791 = "15791";
+  public static final String s15792 = "15792";
+  public static final String s15793 = "15793";
+  public static final String s15794 = "15794";
+  public static final String s15795 = "15795";
+  public static final String s15796 = "15796";
+  public static final String s15797 = "15797";
+  public static final String s15798 = "15798";
+  public static final String s15799 = "15799";
+  public static final String s15800 = "15800";
+  public static final String s15801 = "15801";
+  public static final String s15802 = "15802";
+  public static final String s15803 = "15803";
+  public static final String s15804 = "15804";
+  public static final String s15805 = "15805";
+  public static final String s15806 = "15806";
+  public static final String s15807 = "15807";
+  public static final String s15808 = "15808";
+  public static final String s15809 = "15809";
+  public static final String s15810 = "15810";
+  public static final String s15811 = "15811";
+  public static final String s15812 = "15812";
+  public static final String s15813 = "15813";
+  public static final String s15814 = "15814";
+  public static final String s15815 = "15815";
+  public static final String s15816 = "15816";
+  public static final String s15817 = "15817";
+  public static final String s15818 = "15818";
+  public static final String s15819 = "15819";
+  public static final String s15820 = "15820";
+  public static final String s15821 = "15821";
+  public static final String s15822 = "15822";
+  public static final String s15823 = "15823";
+  public static final String s15824 = "15824";
+  public static final String s15825 = "15825";
+  public static final String s15826 = "15826";
+  public static final String s15827 = "15827";
+  public static final String s15828 = "15828";
+  public static final String s15829 = "15829";
+  public static final String s15830 = "15830";
+  public static final String s15831 = "15831";
+  public static final String s15832 = "15832";
+  public static final String s15833 = "15833";
+  public static final String s15834 = "15834";
+  public static final String s15835 = "15835";
+  public static final String s15836 = "15836";
+  public static final String s15837 = "15837";
+  public static final String s15838 = "15838";
+  public static final String s15839 = "15839";
+  public static final String s15840 = "15840";
+  public static final String s15841 = "15841";
+  public static final String s15842 = "15842";
+  public static final String s15843 = "15843";
+  public static final String s15844 = "15844";
+  public static final String s15845 = "15845";
+  public static final String s15846 = "15846";
+  public static final String s15847 = "15847";
+  public static final String s15848 = "15848";
+  public static final String s15849 = "15849";
+  public static final String s15850 = "15850";
+  public static final String s15851 = "15851";
+  public static final String s15852 = "15852";
+  public static final String s15853 = "15853";
+  public static final String s15854 = "15854";
+  public static final String s15855 = "15855";
+  public static final String s15856 = "15856";
+  public static final String s15857 = "15857";
+  public static final String s15858 = "15858";
+  public static final String s15859 = "15859";
+  public static final String s15860 = "15860";
+  public static final String s15861 = "15861";
+  public static final String s15862 = "15862";
+  public static final String s15863 = "15863";
+  public static final String s15864 = "15864";
+  public static final String s15865 = "15865";
+  public static final String s15866 = "15866";
+  public static final String s15867 = "15867";
+  public static final String s15868 = "15868";
+  public static final String s15869 = "15869";
+  public static final String s15870 = "15870";
+  public static final String s15871 = "15871";
+  public static final String s15872 = "15872";
+  public static final String s15873 = "15873";
+  public static final String s15874 = "15874";
+  public static final String s15875 = "15875";
+  public static final String s15876 = "15876";
+  public static final String s15877 = "15877";
+  public static final String s15878 = "15878";
+  public static final String s15879 = "15879";
+  public static final String s15880 = "15880";
+  public static final String s15881 = "15881";
+  public static final String s15882 = "15882";
+  public static final String s15883 = "15883";
+  public static final String s15884 = "15884";
+  public static final String s15885 = "15885";
+  public static final String s15886 = "15886";
+  public static final String s15887 = "15887";
+  public static final String s15888 = "15888";
+  public static final String s15889 = "15889";
+  public static final String s15890 = "15890";
+  public static final String s15891 = "15891";
+  public static final String s15892 = "15892";
+  public static final String s15893 = "15893";
+  public static final String s15894 = "15894";
+  public static final String s15895 = "15895";
+  public static final String s15896 = "15896";
+  public static final String s15897 = "15897";
+  public static final String s15898 = "15898";
+  public static final String s15899 = "15899";
+  public static final String s15900 = "15900";
+  public static final String s15901 = "15901";
+  public static final String s15902 = "15902";
+  public static final String s15903 = "15903";
+  public static final String s15904 = "15904";
+  public static final String s15905 = "15905";
+  public static final String s15906 = "15906";
+  public static final String s15907 = "15907";
+  public static final String s15908 = "15908";
+  public static final String s15909 = "15909";
+  public static final String s15910 = "15910";
+  public static final String s15911 = "15911";
+  public static final String s15912 = "15912";
+  public static final String s15913 = "15913";
+  public static final String s15914 = "15914";
+  public static final String s15915 = "15915";
+  public static final String s15916 = "15916";
+  public static final String s15917 = "15917";
+  public static final String s15918 = "15918";
+  public static final String s15919 = "15919";
+  public static final String s15920 = "15920";
+  public static final String s15921 = "15921";
+  public static final String s15922 = "15922";
+  public static final String s15923 = "15923";
+  public static final String s15924 = "15924";
+  public static final String s15925 = "15925";
+  public static final String s15926 = "15926";
+  public static final String s15927 = "15927";
+  public static final String s15928 = "15928";
+  public static final String s15929 = "15929";
+  public static final String s15930 = "15930";
+  public static final String s15931 = "15931";
+  public static final String s15932 = "15932";
+  public static final String s15933 = "15933";
+  public static final String s15934 = "15934";
+  public static final String s15935 = "15935";
+  public static final String s15936 = "15936";
+  public static final String s15937 = "15937";
+  public static final String s15938 = "15938";
+  public static final String s15939 = "15939";
+  public static final String s15940 = "15940";
+  public static final String s15941 = "15941";
+  public static final String s15942 = "15942";
+  public static final String s15943 = "15943";
+  public static final String s15944 = "15944";
+  public static final String s15945 = "15945";
+  public static final String s15946 = "15946";
+  public static final String s15947 = "15947";
+  public static final String s15948 = "15948";
+  public static final String s15949 = "15949";
+  public static final String s15950 = "15950";
+  public static final String s15951 = "15951";
+  public static final String s15952 = "15952";
+  public static final String s15953 = "15953";
+  public static final String s15954 = "15954";
+  public static final String s15955 = "15955";
+  public static final String s15956 = "15956";
+  public static final String s15957 = "15957";
+  public static final String s15958 = "15958";
+  public static final String s15959 = "15959";
+  public static final String s15960 = "15960";
+  public static final String s15961 = "15961";
+  public static final String s15962 = "15962";
+  public static final String s15963 = "15963";
+  public static final String s15964 = "15964";
+  public static final String s15965 = "15965";
+  public static final String s15966 = "15966";
+  public static final String s15967 = "15967";
+  public static final String s15968 = "15968";
+  public static final String s15969 = "15969";
+  public static final String s15970 = "15970";
+  public static final String s15971 = "15971";
+  public static final String s15972 = "15972";
+  public static final String s15973 = "15973";
+  public static final String s15974 = "15974";
+  public static final String s15975 = "15975";
+  public static final String s15976 = "15976";
+  public static final String s15977 = "15977";
+  public static final String s15978 = "15978";
+  public static final String s15979 = "15979";
+  public static final String s15980 = "15980";
+  public static final String s15981 = "15981";
+  public static final String s15982 = "15982";
+  public static final String s15983 = "15983";
+  public static final String s15984 = "15984";
+  public static final String s15985 = "15985";
+  public static final String s15986 = "15986";
+  public static final String s15987 = "15987";
+  public static final String s15988 = "15988";
+  public static final String s15989 = "15989";
+  public static final String s15990 = "15990";
+  public static final String s15991 = "15991";
+  public static final String s15992 = "15992";
+  public static final String s15993 = "15993";
+  public static final String s15994 = "15994";
+  public static final String s15995 = "15995";
+  public static final String s15996 = "15996";
+  public static final String s15997 = "15997";
+  public static final String s15998 = "15998";
+  public static final String s15999 = "15999";
+  public static final String s16000 = "16000";
+  public static final String s16001 = "16001";
+  public static final String s16002 = "16002";
+  public static final String s16003 = "16003";
+  public static final String s16004 = "16004";
+  public static final String s16005 = "16005";
+  public static final String s16006 = "16006";
+  public static final String s16007 = "16007";
+  public static final String s16008 = "16008";
+  public static final String s16009 = "16009";
+  public static final String s16010 = "16010";
+  public static final String s16011 = "16011";
+  public static final String s16012 = "16012";
+  public static final String s16013 = "16013";
+  public static final String s16014 = "16014";
+  public static final String s16015 = "16015";
+  public static final String s16016 = "16016";
+  public static final String s16017 = "16017";
+  public static final String s16018 = "16018";
+  public static final String s16019 = "16019";
+  public static final String s16020 = "16020";
+  public static final String s16021 = "16021";
+  public static final String s16022 = "16022";
+  public static final String s16023 = "16023";
+  public static final String s16024 = "16024";
+  public static final String s16025 = "16025";
+  public static final String s16026 = "16026";
+  public static final String s16027 = "16027";
+  public static final String s16028 = "16028";
+  public static final String s16029 = "16029";
+  public static final String s16030 = "16030";
+  public static final String s16031 = "16031";
+  public static final String s16032 = "16032";
+  public static final String s16033 = "16033";
+  public static final String s16034 = "16034";
+  public static final String s16035 = "16035";
+  public static final String s16036 = "16036";
+  public static final String s16037 = "16037";
+  public static final String s16038 = "16038";
+  public static final String s16039 = "16039";
+  public static final String s16040 = "16040";
+  public static final String s16041 = "16041";
+  public static final String s16042 = "16042";
+  public static final String s16043 = "16043";
+  public static final String s16044 = "16044";
+  public static final String s16045 = "16045";
+  public static final String s16046 = "16046";
+  public static final String s16047 = "16047";
+  public static final String s16048 = "16048";
+  public static final String s16049 = "16049";
+  public static final String s16050 = "16050";
+  public static final String s16051 = "16051";
+  public static final String s16052 = "16052";
+  public static final String s16053 = "16053";
+  public static final String s16054 = "16054";
+  public static final String s16055 = "16055";
+  public static final String s16056 = "16056";
+  public static final String s16057 = "16057";
+  public static final String s16058 = "16058";
+  public static final String s16059 = "16059";
+  public static final String s16060 = "16060";
+  public static final String s16061 = "16061";
+  public static final String s16062 = "16062";
+  public static final String s16063 = "16063";
+  public static final String s16064 = "16064";
+  public static final String s16065 = "16065";
+  public static final String s16066 = "16066";
+  public static final String s16067 = "16067";
+  public static final String s16068 = "16068";
+  public static final String s16069 = "16069";
+  public static final String s16070 = "16070";
+  public static final String s16071 = "16071";
+  public static final String s16072 = "16072";
+  public static final String s16073 = "16073";
+  public static final String s16074 = "16074";
+  public static final String s16075 = "16075";
+  public static final String s16076 = "16076";
+  public static final String s16077 = "16077";
+  public static final String s16078 = "16078";
+  public static final String s16079 = "16079";
+  public static final String s16080 = "16080";
+  public static final String s16081 = "16081";
+  public static final String s16082 = "16082";
+  public static final String s16083 = "16083";
+  public static final String s16084 = "16084";
+  public static final String s16085 = "16085";
+  public static final String s16086 = "16086";
+  public static final String s16087 = "16087";
+  public static final String s16088 = "16088";
+  public static final String s16089 = "16089";
+  public static final String s16090 = "16090";
+  public static final String s16091 = "16091";
+  public static final String s16092 = "16092";
+  public static final String s16093 = "16093";
+  public static final String s16094 = "16094";
+  public static final String s16095 = "16095";
+  public static final String s16096 = "16096";
+  public static final String s16097 = "16097";
+  public static final String s16098 = "16098";
+  public static final String s16099 = "16099";
+  public static final String s16100 = "16100";
+  public static final String s16101 = "16101";
+  public static final String s16102 = "16102";
+  public static final String s16103 = "16103";
+  public static final String s16104 = "16104";
+  public static final String s16105 = "16105";
+  public static final String s16106 = "16106";
+  public static final String s16107 = "16107";
+  public static final String s16108 = "16108";
+  public static final String s16109 = "16109";
+  public static final String s16110 = "16110";
+  public static final String s16111 = "16111";
+  public static final String s16112 = "16112";
+  public static final String s16113 = "16113";
+  public static final String s16114 = "16114";
+  public static final String s16115 = "16115";
+  public static final String s16116 = "16116";
+  public static final String s16117 = "16117";
+  public static final String s16118 = "16118";
+  public static final String s16119 = "16119";
+  public static final String s16120 = "16120";
+  public static final String s16121 = "16121";
+  public static final String s16122 = "16122";
+  public static final String s16123 = "16123";
+  public static final String s16124 = "16124";
+  public static final String s16125 = "16125";
+  public static final String s16126 = "16126";
+  public static final String s16127 = "16127";
+  public static final String s16128 = "16128";
+  public static final String s16129 = "16129";
+  public static final String s16130 = "16130";
+  public static final String s16131 = "16131";
+  public static final String s16132 = "16132";
+  public static final String s16133 = "16133";
+  public static final String s16134 = "16134";
+  public static final String s16135 = "16135";
+  public static final String s16136 = "16136";
+  public static final String s16137 = "16137";
+  public static final String s16138 = "16138";
+  public static final String s16139 = "16139";
+  public static final String s16140 = "16140";
+  public static final String s16141 = "16141";
+  public static final String s16142 = "16142";
+  public static final String s16143 = "16143";
+  public static final String s16144 = "16144";
+  public static final String s16145 = "16145";
+  public static final String s16146 = "16146";
+  public static final String s16147 = "16147";
+  public static final String s16148 = "16148";
+  public static final String s16149 = "16149";
+  public static final String s16150 = "16150";
+  public static final String s16151 = "16151";
+  public static final String s16152 = "16152";
+  public static final String s16153 = "16153";
+  public static final String s16154 = "16154";
+  public static final String s16155 = "16155";
+  public static final String s16156 = "16156";
+  public static final String s16157 = "16157";
+  public static final String s16158 = "16158";
+  public static final String s16159 = "16159";
+  public static final String s16160 = "16160";
+  public static final String s16161 = "16161";
+  public static final String s16162 = "16162";
+  public static final String s16163 = "16163";
+  public static final String s16164 = "16164";
+  public static final String s16165 = "16165";
+  public static final String s16166 = "16166";
+  public static final String s16167 = "16167";
+  public static final String s16168 = "16168";
+  public static final String s16169 = "16169";
+  public static final String s16170 = "16170";
+  public static final String s16171 = "16171";
+  public static final String s16172 = "16172";
+  public static final String s16173 = "16173";
+  public static final String s16174 = "16174";
+  public static final String s16175 = "16175";
+  public static final String s16176 = "16176";
+  public static final String s16177 = "16177";
+  public static final String s16178 = "16178";
+  public static final String s16179 = "16179";
+  public static final String s16180 = "16180";
+  public static final String s16181 = "16181";
+  public static final String s16182 = "16182";
+  public static final String s16183 = "16183";
+  public static final String s16184 = "16184";
+  public static final String s16185 = "16185";
+  public static final String s16186 = "16186";
+  public static final String s16187 = "16187";
+  public static final String s16188 = "16188";
+  public static final String s16189 = "16189";
+  public static final String s16190 = "16190";
+  public static final String s16191 = "16191";
+  public static final String s16192 = "16192";
+  public static final String s16193 = "16193";
+  public static final String s16194 = "16194";
+  public static final String s16195 = "16195";
+  public static final String s16196 = "16196";
+  public static final String s16197 = "16197";
+  public static final String s16198 = "16198";
+  public static final String s16199 = "16199";
+  public static final String s16200 = "16200";
+  public static final String s16201 = "16201";
+  public static final String s16202 = "16202";
+  public static final String s16203 = "16203";
+  public static final String s16204 = "16204";
+  public static final String s16205 = "16205";
+  public static final String s16206 = "16206";
+  public static final String s16207 = "16207";
+  public static final String s16208 = "16208";
+  public static final String s16209 = "16209";
+  public static final String s16210 = "16210";
+  public static final String s16211 = "16211";
+  public static final String s16212 = "16212";
+  public static final String s16213 = "16213";
+  public static final String s16214 = "16214";
+  public static final String s16215 = "16215";
+  public static final String s16216 = "16216";
+  public static final String s16217 = "16217";
+  public static final String s16218 = "16218";
+  public static final String s16219 = "16219";
+  public static final String s16220 = "16220";
+  public static final String s16221 = "16221";
+  public static final String s16222 = "16222";
+  public static final String s16223 = "16223";
+  public static final String s16224 = "16224";
+  public static final String s16225 = "16225";
+  public static final String s16226 = "16226";
+  public static final String s16227 = "16227";
+  public static final String s16228 = "16228";
+  public static final String s16229 = "16229";
+  public static final String s16230 = "16230";
+  public static final String s16231 = "16231";
+  public static final String s16232 = "16232";
+  public static final String s16233 = "16233";
+  public static final String s16234 = "16234";
+  public static final String s16235 = "16235";
+  public static final String s16236 = "16236";
+  public static final String s16237 = "16237";
+  public static final String s16238 = "16238";
+  public static final String s16239 = "16239";
+  public static final String s16240 = "16240";
+  public static final String s16241 = "16241";
+  public static final String s16242 = "16242";
+  public static final String s16243 = "16243";
+  public static final String s16244 = "16244";
+  public static final String s16245 = "16245";
+  public static final String s16246 = "16246";
+  public static final String s16247 = "16247";
+  public static final String s16248 = "16248";
+  public static final String s16249 = "16249";
+  public static final String s16250 = "16250";
+  public static final String s16251 = "16251";
+  public static final String s16252 = "16252";
+  public static final String s16253 = "16253";
+  public static final String s16254 = "16254";
+  public static final String s16255 = "16255";
+  public static final String s16256 = "16256";
+  public static final String s16257 = "16257";
+  public static final String s16258 = "16258";
+  public static final String s16259 = "16259";
+  public static final String s16260 = "16260";
+  public static final String s16261 = "16261";
+  public static final String s16262 = "16262";
+  public static final String s16263 = "16263";
+  public static final String s16264 = "16264";
+  public static final String s16265 = "16265";
+  public static final String s16266 = "16266";
+  public static final String s16267 = "16267";
+  public static final String s16268 = "16268";
+  public static final String s16269 = "16269";
+  public static final String s16270 = "16270";
+  public static final String s16271 = "16271";
+  public static final String s16272 = "16272";
+  public static final String s16273 = "16273";
+  public static final String s16274 = "16274";
+  public static final String s16275 = "16275";
+  public static final String s16276 = "16276";
+  public static final String s16277 = "16277";
+  public static final String s16278 = "16278";
+  public static final String s16279 = "16279";
+  public static final String s16280 = "16280";
+  public static final String s16281 = "16281";
+  public static final String s16282 = "16282";
+  public static final String s16283 = "16283";
+  public static final String s16284 = "16284";
+  public static final String s16285 = "16285";
+  public static final String s16286 = "16286";
+  public static final String s16287 = "16287";
+  public static final String s16288 = "16288";
+  public static final String s16289 = "16289";
+  public static final String s16290 = "16290";
+  public static final String s16291 = "16291";
+  public static final String s16292 = "16292";
+  public static final String s16293 = "16293";
+  public static final String s16294 = "16294";
+  public static final String s16295 = "16295";
+  public static final String s16296 = "16296";
+  public static final String s16297 = "16297";
+  public static final String s16298 = "16298";
+  public static final String s16299 = "16299";
+  public static final String s16300 = "16300";
+  public static final String s16301 = "16301";
+  public static final String s16302 = "16302";
+  public static final String s16303 = "16303";
+  public static final String s16304 = "16304";
+  public static final String s16305 = "16305";
+  public static final String s16306 = "16306";
+  public static final String s16307 = "16307";
+  public static final String s16308 = "16308";
+  public static final String s16309 = "16309";
+  public static final String s16310 = "16310";
+  public static final String s16311 = "16311";
+  public static final String s16312 = "16312";
+  public static final String s16313 = "16313";
+  public static final String s16314 = "16314";
+  public static final String s16315 = "16315";
+  public static final String s16316 = "16316";
+  public static final String s16317 = "16317";
+  public static final String s16318 = "16318";
+  public static final String s16319 = "16319";
+  public static final String s16320 = "16320";
+  public static final String s16321 = "16321";
+  public static final String s16322 = "16322";
+  public static final String s16323 = "16323";
+  public static final String s16324 = "16324";
+  public static final String s16325 = "16325";
+  public static final String s16326 = "16326";
+  public static final String s16327 = "16327";
+  public static final String s16328 = "16328";
+  public static final String s16329 = "16329";
+  public static final String s16330 = "16330";
+  public static final String s16331 = "16331";
+  public static final String s16332 = "16332";
+  public static final String s16333 = "16333";
+  public static final String s16334 = "16334";
+  public static final String s16335 = "16335";
+  public static final String s16336 = "16336";
+  public static final String s16337 = "16337";
+  public static final String s16338 = "16338";
+  public static final String s16339 = "16339";
+  public static final String s16340 = "16340";
+  public static final String s16341 = "16341";
+  public static final String s16342 = "16342";
+  public static final String s16343 = "16343";
+  public static final String s16344 = "16344";
+  public static final String s16345 = "16345";
+  public static final String s16346 = "16346";
+  public static final String s16347 = "16347";
+  public static final String s16348 = "16348";
+  public static final String s16349 = "16349";
+  public static final String s16350 = "16350";
+  public static final String s16351 = "16351";
+  public static final String s16352 = "16352";
+  public static final String s16353 = "16353";
+  public static final String s16354 = "16354";
+  public static final String s16355 = "16355";
+  public static final String s16356 = "16356";
+  public static final String s16357 = "16357";
+  public static final String s16358 = "16358";
+  public static final String s16359 = "16359";
+  public static final String s16360 = "16360";
+  public static final String s16361 = "16361";
+  public static final String s16362 = "16362";
+  public static final String s16363 = "16363";
+  public static final String s16364 = "16364";
+  public static final String s16365 = "16365";
+  public static final String s16366 = "16366";
+  public static final String s16367 = "16367";
+  public static final String s16368 = "16368";
+  public static final String s16369 = "16369";
+  public static final String s16370 = "16370";
+  public static final String s16371 = "16371";
+  public static final String s16372 = "16372";
+  public static final String s16373 = "16373";
+  public static final String s16374 = "16374";
+  public static final String s16375 = "16375";
+  public static final String s16376 = "16376";
+  public static final String s16377 = "16377";
+  public static final String s16378 = "16378";
+  public static final String s16379 = "16379";
+  public static final String s16380 = "16380";
+  public static final String s16381 = "16381";
+  public static final String s16382 = "16382";
+  public static final String s16383 = "16383";
+}
diff --git a/src/test/examples/jumbostring/StringPool1.java b/src/test/examples/jumbostring/StringPool1.java
new file mode 100644
index 0000000..8539fb2
--- /dev/null
+++ b/src/test/examples/jumbostring/StringPool1.java
@@ -0,0 +1,16391 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package jumbostring;
+
+class StringPool1 {
+  public static final String s16384 = "16384";
+  public static final String s16385 = "16385";
+  public static final String s16386 = "16386";
+  public static final String s16387 = "16387";
+  public static final String s16388 = "16388";
+  public static final String s16389 = "16389";
+  public static final String s16390 = "16390";
+  public static final String s16391 = "16391";
+  public static final String s16392 = "16392";
+  public static final String s16393 = "16393";
+  public static final String s16394 = "16394";
+  public static final String s16395 = "16395";
+  public static final String s16396 = "16396";
+  public static final String s16397 = "16397";
+  public static final String s16398 = "16398";
+  public static final String s16399 = "16399";
+  public static final String s16400 = "16400";
+  public static final String s16401 = "16401";
+  public static final String s16402 = "16402";
+  public static final String s16403 = "16403";
+  public static final String s16404 = "16404";
+  public static final String s16405 = "16405";
+  public static final String s16406 = "16406";
+  public static final String s16407 = "16407";
+  public static final String s16408 = "16408";
+  public static final String s16409 = "16409";
+  public static final String s16410 = "16410";
+  public static final String s16411 = "16411";
+  public static final String s16412 = "16412";
+  public static final String s16413 = "16413";
+  public static final String s16414 = "16414";
+  public static final String s16415 = "16415";
+  public static final String s16416 = "16416";
+  public static final String s16417 = "16417";
+  public static final String s16418 = "16418";
+  public static final String s16419 = "16419";
+  public static final String s16420 = "16420";
+  public static final String s16421 = "16421";
+  public static final String s16422 = "16422";
+  public static final String s16423 = "16423";
+  public static final String s16424 = "16424";
+  public static final String s16425 = "16425";
+  public static final String s16426 = "16426";
+  public static final String s16427 = "16427";
+  public static final String s16428 = "16428";
+  public static final String s16429 = "16429";
+  public static final String s16430 = "16430";
+  public static final String s16431 = "16431";
+  public static final String s16432 = "16432";
+  public static final String s16433 = "16433";
+  public static final String s16434 = "16434";
+  public static final String s16435 = "16435";
+  public static final String s16436 = "16436";
+  public static final String s16437 = "16437";
+  public static final String s16438 = "16438";
+  public static final String s16439 = "16439";
+  public static final String s16440 = "16440";
+  public static final String s16441 = "16441";
+  public static final String s16442 = "16442";
+  public static final String s16443 = "16443";
+  public static final String s16444 = "16444";
+  public static final String s16445 = "16445";
+  public static final String s16446 = "16446";
+  public static final String s16447 = "16447";
+  public static final String s16448 = "16448";
+  public static final String s16449 = "16449";
+  public static final String s16450 = "16450";
+  public static final String s16451 = "16451";
+  public static final String s16452 = "16452";
+  public static final String s16453 = "16453";
+  public static final String s16454 = "16454";
+  public static final String s16455 = "16455";
+  public static final String s16456 = "16456";
+  public static final String s16457 = "16457";
+  public static final String s16458 = "16458";
+  public static final String s16459 = "16459";
+  public static final String s16460 = "16460";
+  public static final String s16461 = "16461";
+  public static final String s16462 = "16462";
+  public static final String s16463 = "16463";
+  public static final String s16464 = "16464";
+  public static final String s16465 = "16465";
+  public static final String s16466 = "16466";
+  public static final String s16467 = "16467";
+  public static final String s16468 = "16468";
+  public static final String s16469 = "16469";
+  public static final String s16470 = "16470";
+  public static final String s16471 = "16471";
+  public static final String s16472 = "16472";
+  public static final String s16473 = "16473";
+  public static final String s16474 = "16474";
+  public static final String s16475 = "16475";
+  public static final String s16476 = "16476";
+  public static final String s16477 = "16477";
+  public static final String s16478 = "16478";
+  public static final String s16479 = "16479";
+  public static final String s16480 = "16480";
+  public static final String s16481 = "16481";
+  public static final String s16482 = "16482";
+  public static final String s16483 = "16483";
+  public static final String s16484 = "16484";
+  public static final String s16485 = "16485";
+  public static final String s16486 = "16486";
+  public static final String s16487 = "16487";
+  public static final String s16488 = "16488";
+  public static final String s16489 = "16489";
+  public static final String s16490 = "16490";
+  public static final String s16491 = "16491";
+  public static final String s16492 = "16492";
+  public static final String s16493 = "16493";
+  public static final String s16494 = "16494";
+  public static final String s16495 = "16495";
+  public static final String s16496 = "16496";
+  public static final String s16497 = "16497";
+  public static final String s16498 = "16498";
+  public static final String s16499 = "16499";
+  public static final String s16500 = "16500";
+  public static final String s16501 = "16501";
+  public static final String s16502 = "16502";
+  public static final String s16503 = "16503";
+  public static final String s16504 = "16504";
+  public static final String s16505 = "16505";
+  public static final String s16506 = "16506";
+  public static final String s16507 = "16507";
+  public static final String s16508 = "16508";
+  public static final String s16509 = "16509";
+  public static final String s16510 = "16510";
+  public static final String s16511 = "16511";
+  public static final String s16512 = "16512";
+  public static final String s16513 = "16513";
+  public static final String s16514 = "16514";
+  public static final String s16515 = "16515";
+  public static final String s16516 = "16516";
+  public static final String s16517 = "16517";
+  public static final String s16518 = "16518";
+  public static final String s16519 = "16519";
+  public static final String s16520 = "16520";
+  public static final String s16521 = "16521";
+  public static final String s16522 = "16522";
+  public static final String s16523 = "16523";
+  public static final String s16524 = "16524";
+  public static final String s16525 = "16525";
+  public static final String s16526 = "16526";
+  public static final String s16527 = "16527";
+  public static final String s16528 = "16528";
+  public static final String s16529 = "16529";
+  public static final String s16530 = "16530";
+  public static final String s16531 = "16531";
+  public static final String s16532 = "16532";
+  public static final String s16533 = "16533";
+  public static final String s16534 = "16534";
+  public static final String s16535 = "16535";
+  public static final String s16536 = "16536";
+  public static final String s16537 = "16537";
+  public static final String s16538 = "16538";
+  public static final String s16539 = "16539";
+  public static final String s16540 = "16540";
+  public static final String s16541 = "16541";
+  public static final String s16542 = "16542";
+  public static final String s16543 = "16543";
+  public static final String s16544 = "16544";
+  public static final String s16545 = "16545";
+  public static final String s16546 = "16546";
+  public static final String s16547 = "16547";
+  public static final String s16548 = "16548";
+  public static final String s16549 = "16549";
+  public static final String s16550 = "16550";
+  public static final String s16551 = "16551";
+  public static final String s16552 = "16552";
+  public static final String s16553 = "16553";
+  public static final String s16554 = "16554";
+  public static final String s16555 = "16555";
+  public static final String s16556 = "16556";
+  public static final String s16557 = "16557";
+  public static final String s16558 = "16558";
+  public static final String s16559 = "16559";
+  public static final String s16560 = "16560";
+  public static final String s16561 = "16561";
+  public static final String s16562 = "16562";
+  public static final String s16563 = "16563";
+  public static final String s16564 = "16564";
+  public static final String s16565 = "16565";
+  public static final String s16566 = "16566";
+  public static final String s16567 = "16567";
+  public static final String s16568 = "16568";
+  public static final String s16569 = "16569";
+  public static final String s16570 = "16570";
+  public static final String s16571 = "16571";
+  public static final String s16572 = "16572";
+  public static final String s16573 = "16573";
+  public static final String s16574 = "16574";
+  public static final String s16575 = "16575";
+  public static final String s16576 = "16576";
+  public static final String s16577 = "16577";
+  public static final String s16578 = "16578";
+  public static final String s16579 = "16579";
+  public static final String s16580 = "16580";
+  public static final String s16581 = "16581";
+  public static final String s16582 = "16582";
+  public static final String s16583 = "16583";
+  public static final String s16584 = "16584";
+  public static final String s16585 = "16585";
+  public static final String s16586 = "16586";
+  public static final String s16587 = "16587";
+  public static final String s16588 = "16588";
+  public static final String s16589 = "16589";
+  public static final String s16590 = "16590";
+  public static final String s16591 = "16591";
+  public static final String s16592 = "16592";
+  public static final String s16593 = "16593";
+  public static final String s16594 = "16594";
+  public static final String s16595 = "16595";
+  public static final String s16596 = "16596";
+  public static final String s16597 = "16597";
+  public static final String s16598 = "16598";
+  public static final String s16599 = "16599";
+  public static final String s16600 = "16600";
+  public static final String s16601 = "16601";
+  public static final String s16602 = "16602";
+  public static final String s16603 = "16603";
+  public static final String s16604 = "16604";
+  public static final String s16605 = "16605";
+  public static final String s16606 = "16606";
+  public static final String s16607 = "16607";
+  public static final String s16608 = "16608";
+  public static final String s16609 = "16609";
+  public static final String s16610 = "16610";
+  public static final String s16611 = "16611";
+  public static final String s16612 = "16612";
+  public static final String s16613 = "16613";
+  public static final String s16614 = "16614";
+  public static final String s16615 = "16615";
+  public static final String s16616 = "16616";
+  public static final String s16617 = "16617";
+  public static final String s16618 = "16618";
+  public static final String s16619 = "16619";
+  public static final String s16620 = "16620";
+  public static final String s16621 = "16621";
+  public static final String s16622 = "16622";
+  public static final String s16623 = "16623";
+  public static final String s16624 = "16624";
+  public static final String s16625 = "16625";
+  public static final String s16626 = "16626";
+  public static final String s16627 = "16627";
+  public static final String s16628 = "16628";
+  public static final String s16629 = "16629";
+  public static final String s16630 = "16630";
+  public static final String s16631 = "16631";
+  public static final String s16632 = "16632";
+  public static final String s16633 = "16633";
+  public static final String s16634 = "16634";
+  public static final String s16635 = "16635";
+  public static final String s16636 = "16636";
+  public static final String s16637 = "16637";
+  public static final String s16638 = "16638";
+  public static final String s16639 = "16639";
+  public static final String s16640 = "16640";
+  public static final String s16641 = "16641";
+  public static final String s16642 = "16642";
+  public static final String s16643 = "16643";
+  public static final String s16644 = "16644";
+  public static final String s16645 = "16645";
+  public static final String s16646 = "16646";
+  public static final String s16647 = "16647";
+  public static final String s16648 = "16648";
+  public static final String s16649 = "16649";
+  public static final String s16650 = "16650";
+  public static final String s16651 = "16651";
+  public static final String s16652 = "16652";
+  public static final String s16653 = "16653";
+  public static final String s16654 = "16654";
+  public static final String s16655 = "16655";
+  public static final String s16656 = "16656";
+  public static final String s16657 = "16657";
+  public static final String s16658 = "16658";
+  public static final String s16659 = "16659";
+  public static final String s16660 = "16660";
+  public static final String s16661 = "16661";
+  public static final String s16662 = "16662";
+  public static final String s16663 = "16663";
+  public static final String s16664 = "16664";
+  public static final String s16665 = "16665";
+  public static final String s16666 = "16666";
+  public static final String s16667 = "16667";
+  public static final String s16668 = "16668";
+  public static final String s16669 = "16669";
+  public static final String s16670 = "16670";
+  public static final String s16671 = "16671";
+  public static final String s16672 = "16672";
+  public static final String s16673 = "16673";
+  public static final String s16674 = "16674";
+  public static final String s16675 = "16675";
+  public static final String s16676 = "16676";
+  public static final String s16677 = "16677";
+  public static final String s16678 = "16678";
+  public static final String s16679 = "16679";
+  public static final String s16680 = "16680";
+  public static final String s16681 = "16681";
+  public static final String s16682 = "16682";
+  public static final String s16683 = "16683";
+  public static final String s16684 = "16684";
+  public static final String s16685 = "16685";
+  public static final String s16686 = "16686";
+  public static final String s16687 = "16687";
+  public static final String s16688 = "16688";
+  public static final String s16689 = "16689";
+  public static final String s16690 = "16690";
+  public static final String s16691 = "16691";
+  public static final String s16692 = "16692";
+  public static final String s16693 = "16693";
+  public static final String s16694 = "16694";
+  public static final String s16695 = "16695";
+  public static final String s16696 = "16696";
+  public static final String s16697 = "16697";
+  public static final String s16698 = "16698";
+  public static final String s16699 = "16699";
+  public static final String s16700 = "16700";
+  public static final String s16701 = "16701";
+  public static final String s16702 = "16702";
+  public static final String s16703 = "16703";
+  public static final String s16704 = "16704";
+  public static final String s16705 = "16705";
+  public static final String s16706 = "16706";
+  public static final String s16707 = "16707";
+  public static final String s16708 = "16708";
+  public static final String s16709 = "16709";
+  public static final String s16710 = "16710";
+  public static final String s16711 = "16711";
+  public static final String s16712 = "16712";
+  public static final String s16713 = "16713";
+  public static final String s16714 = "16714";
+  public static final String s16715 = "16715";
+  public static final String s16716 = "16716";
+  public static final String s16717 = "16717";
+  public static final String s16718 = "16718";
+  public static final String s16719 = "16719";
+  public static final String s16720 = "16720";
+  public static final String s16721 = "16721";
+  public static final String s16722 = "16722";
+  public static final String s16723 = "16723";
+  public static final String s16724 = "16724";
+  public static final String s16725 = "16725";
+  public static final String s16726 = "16726";
+  public static final String s16727 = "16727";
+  public static final String s16728 = "16728";
+  public static final String s16729 = "16729";
+  public static final String s16730 = "16730";
+  public static final String s16731 = "16731";
+  public static final String s16732 = "16732";
+  public static final String s16733 = "16733";
+  public static final String s16734 = "16734";
+  public static final String s16735 = "16735";
+  public static final String s16736 = "16736";
+  public static final String s16737 = "16737";
+  public static final String s16738 = "16738";
+  public static final String s16739 = "16739";
+  public static final String s16740 = "16740";
+  public static final String s16741 = "16741";
+  public static final String s16742 = "16742";
+  public static final String s16743 = "16743";
+  public static final String s16744 = "16744";
+  public static final String s16745 = "16745";
+  public static final String s16746 = "16746";
+  public static final String s16747 = "16747";
+  public static final String s16748 = "16748";
+  public static final String s16749 = "16749";
+  public static final String s16750 = "16750";
+  public static final String s16751 = "16751";
+  public static final String s16752 = "16752";
+  public static final String s16753 = "16753";
+  public static final String s16754 = "16754";
+  public static final String s16755 = "16755";
+  public static final String s16756 = "16756";
+  public static final String s16757 = "16757";
+  public static final String s16758 = "16758";
+  public static final String s16759 = "16759";
+  public static final String s16760 = "16760";
+  public static final String s16761 = "16761";
+  public static final String s16762 = "16762";
+  public static final String s16763 = "16763";
+  public static final String s16764 = "16764";
+  public static final String s16765 = "16765";
+  public static final String s16766 = "16766";
+  public static final String s16767 = "16767";
+  public static final String s16768 = "16768";
+  public static final String s16769 = "16769";
+  public static final String s16770 = "16770";
+  public static final String s16771 = "16771";
+  public static final String s16772 = "16772";
+  public static final String s16773 = "16773";
+  public static final String s16774 = "16774";
+  public static final String s16775 = "16775";
+  public static final String s16776 = "16776";
+  public static final String s16777 = "16777";
+  public static final String s16778 = "16778";
+  public static final String s16779 = "16779";
+  public static final String s16780 = "16780";
+  public static final String s16781 = "16781";
+  public static final String s16782 = "16782";
+  public static final String s16783 = "16783";
+  public static final String s16784 = "16784";
+  public static final String s16785 = "16785";
+  public static final String s16786 = "16786";
+  public static final String s16787 = "16787";
+  public static final String s16788 = "16788";
+  public static final String s16789 = "16789";
+  public static final String s16790 = "16790";
+  public static final String s16791 = "16791";
+  public static final String s16792 = "16792";
+  public static final String s16793 = "16793";
+  public static final String s16794 = "16794";
+  public static final String s16795 = "16795";
+  public static final String s16796 = "16796";
+  public static final String s16797 = "16797";
+  public static final String s16798 = "16798";
+  public static final String s16799 = "16799";
+  public static final String s16800 = "16800";
+  public static final String s16801 = "16801";
+  public static final String s16802 = "16802";
+  public static final String s16803 = "16803";
+  public static final String s16804 = "16804";
+  public static final String s16805 = "16805";
+  public static final String s16806 = "16806";
+  public static final String s16807 = "16807";
+  public static final String s16808 = "16808";
+  public static final String s16809 = "16809";
+  public static final String s16810 = "16810";
+  public static final String s16811 = "16811";
+  public static final String s16812 = "16812";
+  public static final String s16813 = "16813";
+  public static final String s16814 = "16814";
+  public static final String s16815 = "16815";
+  public static final String s16816 = "16816";
+  public static final String s16817 = "16817";
+  public static final String s16818 = "16818";
+  public static final String s16819 = "16819";
+  public static final String s16820 = "16820";
+  public static final String s16821 = "16821";
+  public static final String s16822 = "16822";
+  public static final String s16823 = "16823";
+  public static final String s16824 = "16824";
+  public static final String s16825 = "16825";
+  public static final String s16826 = "16826";
+  public static final String s16827 = "16827";
+  public static final String s16828 = "16828";
+  public static final String s16829 = "16829";
+  public static final String s16830 = "16830";
+  public static final String s16831 = "16831";
+  public static final String s16832 = "16832";
+  public static final String s16833 = "16833";
+  public static final String s16834 = "16834";
+  public static final String s16835 = "16835";
+  public static final String s16836 = "16836";
+  public static final String s16837 = "16837";
+  public static final String s16838 = "16838";
+  public static final String s16839 = "16839";
+  public static final String s16840 = "16840";
+  public static final String s16841 = "16841";
+  public static final String s16842 = "16842";
+  public static final String s16843 = "16843";
+  public static final String s16844 = "16844";
+  public static final String s16845 = "16845";
+  public static final String s16846 = "16846";
+  public static final String s16847 = "16847";
+  public static final String s16848 = "16848";
+  public static final String s16849 = "16849";
+  public static final String s16850 = "16850";
+  public static final String s16851 = "16851";
+  public static final String s16852 = "16852";
+  public static final String s16853 = "16853";
+  public static final String s16854 = "16854";
+  public static final String s16855 = "16855";
+  public static final String s16856 = "16856";
+  public static final String s16857 = "16857";
+  public static final String s16858 = "16858";
+  public static final String s16859 = "16859";
+  public static final String s16860 = "16860";
+  public static final String s16861 = "16861";
+  public static final String s16862 = "16862";
+  public static final String s16863 = "16863";
+  public static final String s16864 = "16864";
+  public static final String s16865 = "16865";
+  public static final String s16866 = "16866";
+  public static final String s16867 = "16867";
+  public static final String s16868 = "16868";
+  public static final String s16869 = "16869";
+  public static final String s16870 = "16870";
+  public static final String s16871 = "16871";
+  public static final String s16872 = "16872";
+  public static final String s16873 = "16873";
+  public static final String s16874 = "16874";
+  public static final String s16875 = "16875";
+  public static final String s16876 = "16876";
+  public static final String s16877 = "16877";
+  public static final String s16878 = "16878";
+  public static final String s16879 = "16879";
+  public static final String s16880 = "16880";
+  public static final String s16881 = "16881";
+  public static final String s16882 = "16882";
+  public static final String s16883 = "16883";
+  public static final String s16884 = "16884";
+  public static final String s16885 = "16885";
+  public static final String s16886 = "16886";
+  public static final String s16887 = "16887";
+  public static final String s16888 = "16888";
+  public static final String s16889 = "16889";
+  public static final String s16890 = "16890";
+  public static final String s16891 = "16891";
+  public static final String s16892 = "16892";
+  public static final String s16893 = "16893";
+  public static final String s16894 = "16894";
+  public static final String s16895 = "16895";
+  public static final String s16896 = "16896";
+  public static final String s16897 = "16897";
+  public static final String s16898 = "16898";
+  public static final String s16899 = "16899";
+  public static final String s16900 = "16900";
+  public static final String s16901 = "16901";
+  public static final String s16902 = "16902";
+  public static final String s16903 = "16903";
+  public static final String s16904 = "16904";
+  public static final String s16905 = "16905";
+  public static final String s16906 = "16906";
+  public static final String s16907 = "16907";
+  public static final String s16908 = "16908";
+  public static final String s16909 = "16909";
+  public static final String s16910 = "16910";
+  public static final String s16911 = "16911";
+  public static final String s16912 = "16912";
+  public static final String s16913 = "16913";
+  public static final String s16914 = "16914";
+  public static final String s16915 = "16915";
+  public static final String s16916 = "16916";
+  public static final String s16917 = "16917";
+  public static final String s16918 = "16918";
+  public static final String s16919 = "16919";
+  public static final String s16920 = "16920";
+  public static final String s16921 = "16921";
+  public static final String s16922 = "16922";
+  public static final String s16923 = "16923";
+  public static final String s16924 = "16924";
+  public static final String s16925 = "16925";
+  public static final String s16926 = "16926";
+  public static final String s16927 = "16927";
+  public static final String s16928 = "16928";
+  public static final String s16929 = "16929";
+  public static final String s16930 = "16930";
+  public static final String s16931 = "16931";
+  public static final String s16932 = "16932";
+  public static final String s16933 = "16933";
+  public static final String s16934 = "16934";
+  public static final String s16935 = "16935";
+  public static final String s16936 = "16936";
+  public static final String s16937 = "16937";
+  public static final String s16938 = "16938";
+  public static final String s16939 = "16939";
+  public static final String s16940 = "16940";
+  public static final String s16941 = "16941";
+  public static final String s16942 = "16942";
+  public static final String s16943 = "16943";
+  public static final String s16944 = "16944";
+  public static final String s16945 = "16945";
+  public static final String s16946 = "16946";
+  public static final String s16947 = "16947";
+  public static final String s16948 = "16948";
+  public static final String s16949 = "16949";
+  public static final String s16950 = "16950";
+  public static final String s16951 = "16951";
+  public static final String s16952 = "16952";
+  public static final String s16953 = "16953";
+  public static final String s16954 = "16954";
+  public static final String s16955 = "16955";
+  public static final String s16956 = "16956";
+  public static final String s16957 = "16957";
+  public static final String s16958 = "16958";
+  public static final String s16959 = "16959";
+  public static final String s16960 = "16960";
+  public static final String s16961 = "16961";
+  public static final String s16962 = "16962";
+  public static final String s16963 = "16963";
+  public static final String s16964 = "16964";
+  public static final String s16965 = "16965";
+  public static final String s16966 = "16966";
+  public static final String s16967 = "16967";
+  public static final String s16968 = "16968";
+  public static final String s16969 = "16969";
+  public static final String s16970 = "16970";
+  public static final String s16971 = "16971";
+  public static final String s16972 = "16972";
+  public static final String s16973 = "16973";
+  public static final String s16974 = "16974";
+  public static final String s16975 = "16975";
+  public static final String s16976 = "16976";
+  public static final String s16977 = "16977";
+  public static final String s16978 = "16978";
+  public static final String s16979 = "16979";
+  public static final String s16980 = "16980";
+  public static final String s16981 = "16981";
+  public static final String s16982 = "16982";
+  public static final String s16983 = "16983";
+  public static final String s16984 = "16984";
+  public static final String s16985 = "16985";
+  public static final String s16986 = "16986";
+  public static final String s16987 = "16987";
+  public static final String s16988 = "16988";
+  public static final String s16989 = "16989";
+  public static final String s16990 = "16990";
+  public static final String s16991 = "16991";
+  public static final String s16992 = "16992";
+  public static final String s16993 = "16993";
+  public static final String s16994 = "16994";
+  public static final String s16995 = "16995";
+  public static final String s16996 = "16996";
+  public static final String s16997 = "16997";
+  public static final String s16998 = "16998";
+  public static final String s16999 = "16999";
+  public static final String s17000 = "17000";
+  public static final String s17001 = "17001";
+  public static final String s17002 = "17002";
+  public static final String s17003 = "17003";
+  public static final String s17004 = "17004";
+  public static final String s17005 = "17005";
+  public static final String s17006 = "17006";
+  public static final String s17007 = "17007";
+  public static final String s17008 = "17008";
+  public static final String s17009 = "17009";
+  public static final String s17010 = "17010";
+  public static final String s17011 = "17011";
+  public static final String s17012 = "17012";
+  public static final String s17013 = "17013";
+  public static final String s17014 = "17014";
+  public static final String s17015 = "17015";
+  public static final String s17016 = "17016";
+  public static final String s17017 = "17017";
+  public static final String s17018 = "17018";
+  public static final String s17019 = "17019";
+  public static final String s17020 = "17020";
+  public static final String s17021 = "17021";
+  public static final String s17022 = "17022";
+  public static final String s17023 = "17023";
+  public static final String s17024 = "17024";
+  public static final String s17025 = "17025";
+  public static final String s17026 = "17026";
+  public static final String s17027 = "17027";
+  public static final String s17028 = "17028";
+  public static final String s17029 = "17029";
+  public static final String s17030 = "17030";
+  public static final String s17031 = "17031";
+  public static final String s17032 = "17032";
+  public static final String s17033 = "17033";
+  public static final String s17034 = "17034";
+  public static final String s17035 = "17035";
+  public static final String s17036 = "17036";
+  public static final String s17037 = "17037";
+  public static final String s17038 = "17038";
+  public static final String s17039 = "17039";
+  public static final String s17040 = "17040";
+  public static final String s17041 = "17041";
+  public static final String s17042 = "17042";
+  public static final String s17043 = "17043";
+  public static final String s17044 = "17044";
+  public static final String s17045 = "17045";
+  public static final String s17046 = "17046";
+  public static final String s17047 = "17047";
+  public static final String s17048 = "17048";
+  public static final String s17049 = "17049";
+  public static final String s17050 = "17050";
+  public static final String s17051 = "17051";
+  public static final String s17052 = "17052";
+  public static final String s17053 = "17053";
+  public static final String s17054 = "17054";
+  public static final String s17055 = "17055";
+  public static final String s17056 = "17056";
+  public static final String s17057 = "17057";
+  public static final String s17058 = "17058";
+  public static final String s17059 = "17059";
+  public static final String s17060 = "17060";
+  public static final String s17061 = "17061";
+  public static final String s17062 = "17062";
+  public static final String s17063 = "17063";
+  public static final String s17064 = "17064";
+  public static final String s17065 = "17065";
+  public static final String s17066 = "17066";
+  public static final String s17067 = "17067";
+  public static final String s17068 = "17068";
+  public static final String s17069 = "17069";
+  public static final String s17070 = "17070";
+  public static final String s17071 = "17071";
+  public static final String s17072 = "17072";
+  public static final String s17073 = "17073";
+  public static final String s17074 = "17074";
+  public static final String s17075 = "17075";
+  public static final String s17076 = "17076";
+  public static final String s17077 = "17077";
+  public static final String s17078 = "17078";
+  public static final String s17079 = "17079";
+  public static final String s17080 = "17080";
+  public static final String s17081 = "17081";
+  public static final String s17082 = "17082";
+  public static final String s17083 = "17083";
+  public static final String s17084 = "17084";
+  public static final String s17085 = "17085";
+  public static final String s17086 = "17086";
+  public static final String s17087 = "17087";
+  public static final String s17088 = "17088";
+  public static final String s17089 = "17089";
+  public static final String s17090 = "17090";
+  public static final String s17091 = "17091";
+  public static final String s17092 = "17092";
+  public static final String s17093 = "17093";
+  public static final String s17094 = "17094";
+  public static final String s17095 = "17095";
+  public static final String s17096 = "17096";
+  public static final String s17097 = "17097";
+  public static final String s17098 = "17098";
+  public static final String s17099 = "17099";
+  public static final String s17100 = "17100";
+  public static final String s17101 = "17101";
+  public static final String s17102 = "17102";
+  public static final String s17103 = "17103";
+  public static final String s17104 = "17104";
+  public static final String s17105 = "17105";
+  public static final String s17106 = "17106";
+  public static final String s17107 = "17107";
+  public static final String s17108 = "17108";
+  public static final String s17109 = "17109";
+  public static final String s17110 = "17110";
+  public static final String s17111 = "17111";
+  public static final String s17112 = "17112";
+  public static final String s17113 = "17113";
+  public static final String s17114 = "17114";
+  public static final String s17115 = "17115";
+  public static final String s17116 = "17116";
+  public static final String s17117 = "17117";
+  public static final String s17118 = "17118";
+  public static final String s17119 = "17119";
+  public static final String s17120 = "17120";
+  public static final String s17121 = "17121";
+  public static final String s17122 = "17122";
+  public static final String s17123 = "17123";
+  public static final String s17124 = "17124";
+  public static final String s17125 = "17125";
+  public static final String s17126 = "17126";
+  public static final String s17127 = "17127";
+  public static final String s17128 = "17128";
+  public static final String s17129 = "17129";
+  public static final String s17130 = "17130";
+  public static final String s17131 = "17131";
+  public static final String s17132 = "17132";
+  public static final String s17133 = "17133";
+  public static final String s17134 = "17134";
+  public static final String s17135 = "17135";
+  public static final String s17136 = "17136";
+  public static final String s17137 = "17137";
+  public static final String s17138 = "17138";
+  public static final String s17139 = "17139";
+  public static final String s17140 = "17140";
+  public static final String s17141 = "17141";
+  public static final String s17142 = "17142";
+  public static final String s17143 = "17143";
+  public static final String s17144 = "17144";
+  public static final String s17145 = "17145";
+  public static final String s17146 = "17146";
+  public static final String s17147 = "17147";
+  public static final String s17148 = "17148";
+  public static final String s17149 = "17149";
+  public static final String s17150 = "17150";
+  public static final String s17151 = "17151";
+  public static final String s17152 = "17152";
+  public static final String s17153 = "17153";
+  public static final String s17154 = "17154";
+  public static final String s17155 = "17155";
+  public static final String s17156 = "17156";
+  public static final String s17157 = "17157";
+  public static final String s17158 = "17158";
+  public static final String s17159 = "17159";
+  public static final String s17160 = "17160";
+  public static final String s17161 = "17161";
+  public static final String s17162 = "17162";
+  public static final String s17163 = "17163";
+  public static final String s17164 = "17164";
+  public static final String s17165 = "17165";
+  public static final String s17166 = "17166";
+  public static final String s17167 = "17167";
+  public static final String s17168 = "17168";
+  public static final String s17169 = "17169";
+  public static final String s17170 = "17170";
+  public static final String s17171 = "17171";
+  public static final String s17172 = "17172";
+  public static final String s17173 = "17173";
+  public static final String s17174 = "17174";
+  public static final String s17175 = "17175";
+  public static final String s17176 = "17176";
+  public static final String s17177 = "17177";
+  public static final String s17178 = "17178";
+  public static final String s17179 = "17179";
+  public static final String s17180 = "17180";
+  public static final String s17181 = "17181";
+  public static final String s17182 = "17182";
+  public static final String s17183 = "17183";
+  public static final String s17184 = "17184";
+  public static final String s17185 = "17185";
+  public static final String s17186 = "17186";
+  public static final String s17187 = "17187";
+  public static final String s17188 = "17188";
+  public static final String s17189 = "17189";
+  public static final String s17190 = "17190";
+  public static final String s17191 = "17191";
+  public static final String s17192 = "17192";
+  public static final String s17193 = "17193";
+  public static final String s17194 = "17194";
+  public static final String s17195 = "17195";
+  public static final String s17196 = "17196";
+  public static final String s17197 = "17197";
+  public static final String s17198 = "17198";
+  public static final String s17199 = "17199";
+  public static final String s17200 = "17200";
+  public static final String s17201 = "17201";
+  public static final String s17202 = "17202";
+  public static final String s17203 = "17203";
+  public static final String s17204 = "17204";
+  public static final String s17205 = "17205";
+  public static final String s17206 = "17206";
+  public static final String s17207 = "17207";
+  public static final String s17208 = "17208";
+  public static final String s17209 = "17209";
+  public static final String s17210 = "17210";
+  public static final String s17211 = "17211";
+  public static final String s17212 = "17212";
+  public static final String s17213 = "17213";
+  public static final String s17214 = "17214";
+  public static final String s17215 = "17215";
+  public static final String s17216 = "17216";
+  public static final String s17217 = "17217";
+  public static final String s17218 = "17218";
+  public static final String s17219 = "17219";
+  public static final String s17220 = "17220";
+  public static final String s17221 = "17221";
+  public static final String s17222 = "17222";
+  public static final String s17223 = "17223";
+  public static final String s17224 = "17224";
+  public static final String s17225 = "17225";
+  public static final String s17226 = "17226";
+  public static final String s17227 = "17227";
+  public static final String s17228 = "17228";
+  public static final String s17229 = "17229";
+  public static final String s17230 = "17230";
+  public static final String s17231 = "17231";
+  public static final String s17232 = "17232";
+  public static final String s17233 = "17233";
+  public static final String s17234 = "17234";
+  public static final String s17235 = "17235";
+  public static final String s17236 = "17236";
+  public static final String s17237 = "17237";
+  public static final String s17238 = "17238";
+  public static final String s17239 = "17239";
+  public static final String s17240 = "17240";
+  public static final String s17241 = "17241";
+  public static final String s17242 = "17242";
+  public static final String s17243 = "17243";
+  public static final String s17244 = "17244";
+  public static final String s17245 = "17245";
+  public static final String s17246 = "17246";
+  public static final String s17247 = "17247";
+  public static final String s17248 = "17248";
+  public static final String s17249 = "17249";
+  public static final String s17250 = "17250";
+  public static final String s17251 = "17251";
+  public static final String s17252 = "17252";
+  public static final String s17253 = "17253";
+  public static final String s17254 = "17254";
+  public static final String s17255 = "17255";
+  public static final String s17256 = "17256";
+  public static final String s17257 = "17257";
+  public static final String s17258 = "17258";
+  public static final String s17259 = "17259";
+  public static final String s17260 = "17260";
+  public static final String s17261 = "17261";
+  public static final String s17262 = "17262";
+  public static final String s17263 = "17263";
+  public static final String s17264 = "17264";
+  public static final String s17265 = "17265";
+  public static final String s17266 = "17266";
+  public static final String s17267 = "17267";
+  public static final String s17268 = "17268";
+  public static final String s17269 = "17269";
+  public static final String s17270 = "17270";
+  public static final String s17271 = "17271";
+  public static final String s17272 = "17272";
+  public static final String s17273 = "17273";
+  public static final String s17274 = "17274";
+  public static final String s17275 = "17275";
+  public static final String s17276 = "17276";
+  public static final String s17277 = "17277";
+  public static final String s17278 = "17278";
+  public static final String s17279 = "17279";
+  public static final String s17280 = "17280";
+  public static final String s17281 = "17281";
+  public static final String s17282 = "17282";
+  public static final String s17283 = "17283";
+  public static final String s17284 = "17284";
+  public static final String s17285 = "17285";
+  public static final String s17286 = "17286";
+  public static final String s17287 = "17287";
+  public static final String s17288 = "17288";
+  public static final String s17289 = "17289";
+  public static final String s17290 = "17290";
+  public static final String s17291 = "17291";
+  public static final String s17292 = "17292";
+  public static final String s17293 = "17293";
+  public static final String s17294 = "17294";
+  public static final String s17295 = "17295";
+  public static final String s17296 = "17296";
+  public static final String s17297 = "17297";
+  public static final String s17298 = "17298";
+  public static final String s17299 = "17299";
+  public static final String s17300 = "17300";
+  public static final String s17301 = "17301";
+  public static final String s17302 = "17302";
+  public static final String s17303 = "17303";
+  public static final String s17304 = "17304";
+  public static final String s17305 = "17305";
+  public static final String s17306 = "17306";
+  public static final String s17307 = "17307";
+  public static final String s17308 = "17308";
+  public static final String s17309 = "17309";
+  public static final String s17310 = "17310";
+  public static final String s17311 = "17311";
+  public static final String s17312 = "17312";
+  public static final String s17313 = "17313";
+  public static final String s17314 = "17314";
+  public static final String s17315 = "17315";
+  public static final String s17316 = "17316";
+  public static final String s17317 = "17317";
+  public static final String s17318 = "17318";
+  public static final String s17319 = "17319";
+  public static final String s17320 = "17320";
+  public static final String s17321 = "17321";
+  public static final String s17322 = "17322";
+  public static final String s17323 = "17323";
+  public static final String s17324 = "17324";
+  public static final String s17325 = "17325";
+  public static final String s17326 = "17326";
+  public static final String s17327 = "17327";
+  public static final String s17328 = "17328";
+  public static final String s17329 = "17329";
+  public static final String s17330 = "17330";
+  public static final String s17331 = "17331";
+  public static final String s17332 = "17332";
+  public static final String s17333 = "17333";
+  public static final String s17334 = "17334";
+  public static final String s17335 = "17335";
+  public static final String s17336 = "17336";
+  public static final String s17337 = "17337";
+  public static final String s17338 = "17338";
+  public static final String s17339 = "17339";
+  public static final String s17340 = "17340";
+  public static final String s17341 = "17341";
+  public static final String s17342 = "17342";
+  public static final String s17343 = "17343";
+  public static final String s17344 = "17344";
+  public static final String s17345 = "17345";
+  public static final String s17346 = "17346";
+  public static final String s17347 = "17347";
+  public static final String s17348 = "17348";
+  public static final String s17349 = "17349";
+  public static final String s17350 = "17350";
+  public static final String s17351 = "17351";
+  public static final String s17352 = "17352";
+  public static final String s17353 = "17353";
+  public static final String s17354 = "17354";
+  public static final String s17355 = "17355";
+  public static final String s17356 = "17356";
+  public static final String s17357 = "17357";
+  public static final String s17358 = "17358";
+  public static final String s17359 = "17359";
+  public static final String s17360 = "17360";
+  public static final String s17361 = "17361";
+  public static final String s17362 = "17362";
+  public static final String s17363 = "17363";
+  public static final String s17364 = "17364";
+  public static final String s17365 = "17365";
+  public static final String s17366 = "17366";
+  public static final String s17367 = "17367";
+  public static final String s17368 = "17368";
+  public static final String s17369 = "17369";
+  public static final String s17370 = "17370";
+  public static final String s17371 = "17371";
+  public static final String s17372 = "17372";
+  public static final String s17373 = "17373";
+  public static final String s17374 = "17374";
+  public static final String s17375 = "17375";
+  public static final String s17376 = "17376";
+  public static final String s17377 = "17377";
+  public static final String s17378 = "17378";
+  public static final String s17379 = "17379";
+  public static final String s17380 = "17380";
+  public static final String s17381 = "17381";
+  public static final String s17382 = "17382";
+  public static final String s17383 = "17383";
+  public static final String s17384 = "17384";
+  public static final String s17385 = "17385";
+  public static final String s17386 = "17386";
+  public static final String s17387 = "17387";
+  public static final String s17388 = "17388";
+  public static final String s17389 = "17389";
+  public static final String s17390 = "17390";
+  public static final String s17391 = "17391";
+  public static final String s17392 = "17392";
+  public static final String s17393 = "17393";
+  public static final String s17394 = "17394";
+  public static final String s17395 = "17395";
+  public static final String s17396 = "17396";
+  public static final String s17397 = "17397";
+  public static final String s17398 = "17398";
+  public static final String s17399 = "17399";
+  public static final String s17400 = "17400";
+  public static final String s17401 = "17401";
+  public static final String s17402 = "17402";
+  public static final String s17403 = "17403";
+  public static final String s17404 = "17404";
+  public static final String s17405 = "17405";
+  public static final String s17406 = "17406";
+  public static final String s17407 = "17407";
+  public static final String s17408 = "17408";
+  public static final String s17409 = "17409";
+  public static final String s17410 = "17410";
+  public static final String s17411 = "17411";
+  public static final String s17412 = "17412";
+  public static final String s17413 = "17413";
+  public static final String s17414 = "17414";
+  public static final String s17415 = "17415";
+  public static final String s17416 = "17416";
+  public static final String s17417 = "17417";
+  public static final String s17418 = "17418";
+  public static final String s17419 = "17419";
+  public static final String s17420 = "17420";
+  public static final String s17421 = "17421";
+  public static final String s17422 = "17422";
+  public static final String s17423 = "17423";
+  public static final String s17424 = "17424";
+  public static final String s17425 = "17425";
+  public static final String s17426 = "17426";
+  public static final String s17427 = "17427";
+  public static final String s17428 = "17428";
+  public static final String s17429 = "17429";
+  public static final String s17430 = "17430";
+  public static final String s17431 = "17431";
+  public static final String s17432 = "17432";
+  public static final String s17433 = "17433";
+  public static final String s17434 = "17434";
+  public static final String s17435 = "17435";
+  public static final String s17436 = "17436";
+  public static final String s17437 = "17437";
+  public static final String s17438 = "17438";
+  public static final String s17439 = "17439";
+  public static final String s17440 = "17440";
+  public static final String s17441 = "17441";
+  public static final String s17442 = "17442";
+  public static final String s17443 = "17443";
+  public static final String s17444 = "17444";
+  public static final String s17445 = "17445";
+  public static final String s17446 = "17446";
+  public static final String s17447 = "17447";
+  public static final String s17448 = "17448";
+  public static final String s17449 = "17449";
+  public static final String s17450 = "17450";
+  public static final String s17451 = "17451";
+  public static final String s17452 = "17452";
+  public static final String s17453 = "17453";
+  public static final String s17454 = "17454";
+  public static final String s17455 = "17455";
+  public static final String s17456 = "17456";
+  public static final String s17457 = "17457";
+  public static final String s17458 = "17458";
+  public static final String s17459 = "17459";
+  public static final String s17460 = "17460";
+  public static final String s17461 = "17461";
+  public static final String s17462 = "17462";
+  public static final String s17463 = "17463";
+  public static final String s17464 = "17464";
+  public static final String s17465 = "17465";
+  public static final String s17466 = "17466";
+  public static final String s17467 = "17467";
+  public static final String s17468 = "17468";
+  public static final String s17469 = "17469";
+  public static final String s17470 = "17470";
+  public static final String s17471 = "17471";
+  public static final String s17472 = "17472";
+  public static final String s17473 = "17473";
+  public static final String s17474 = "17474";
+  public static final String s17475 = "17475";
+  public static final String s17476 = "17476";
+  public static final String s17477 = "17477";
+  public static final String s17478 = "17478";
+  public static final String s17479 = "17479";
+  public static final String s17480 = "17480";
+  public static final String s17481 = "17481";
+  public static final String s17482 = "17482";
+  public static final String s17483 = "17483";
+  public static final String s17484 = "17484";
+  public static final String s17485 = "17485";
+  public static final String s17486 = "17486";
+  public static final String s17487 = "17487";
+  public static final String s17488 = "17488";
+  public static final String s17489 = "17489";
+  public static final String s17490 = "17490";
+  public static final String s17491 = "17491";
+  public static final String s17492 = "17492";
+  public static final String s17493 = "17493";
+  public static final String s17494 = "17494";
+  public static final String s17495 = "17495";
+  public static final String s17496 = "17496";
+  public static final String s17497 = "17497";
+  public static final String s17498 = "17498";
+  public static final String s17499 = "17499";
+  public static final String s17500 = "17500";
+  public static final String s17501 = "17501";
+  public static final String s17502 = "17502";
+  public static final String s17503 = "17503";
+  public static final String s17504 = "17504";
+  public static final String s17505 = "17505";
+  public static final String s17506 = "17506";
+  public static final String s17507 = "17507";
+  public static final String s17508 = "17508";
+  public static final String s17509 = "17509";
+  public static final String s17510 = "17510";
+  public static final String s17511 = "17511";
+  public static final String s17512 = "17512";
+  public static final String s17513 = "17513";
+  public static final String s17514 = "17514";
+  public static final String s17515 = "17515";
+  public static final String s17516 = "17516";
+  public static final String s17517 = "17517";
+  public static final String s17518 = "17518";
+  public static final String s17519 = "17519";
+  public static final String s17520 = "17520";
+  public static final String s17521 = "17521";
+  public static final String s17522 = "17522";
+  public static final String s17523 = "17523";
+  public static final String s17524 = "17524";
+  public static final String s17525 = "17525";
+  public static final String s17526 = "17526";
+  public static final String s17527 = "17527";
+  public static final String s17528 = "17528";
+  public static final String s17529 = "17529";
+  public static final String s17530 = "17530";
+  public static final String s17531 = "17531";
+  public static final String s17532 = "17532";
+  public static final String s17533 = "17533";
+  public static final String s17534 = "17534";
+  public static final String s17535 = "17535";
+  public static final String s17536 = "17536";
+  public static final String s17537 = "17537";
+  public static final String s17538 = "17538";
+  public static final String s17539 = "17539";
+  public static final String s17540 = "17540";
+  public static final String s17541 = "17541";
+  public static final String s17542 = "17542";
+  public static final String s17543 = "17543";
+  public static final String s17544 = "17544";
+  public static final String s17545 = "17545";
+  public static final String s17546 = "17546";
+  public static final String s17547 = "17547";
+  public static final String s17548 = "17548";
+  public static final String s17549 = "17549";
+  public static final String s17550 = "17550";
+  public static final String s17551 = "17551";
+  public static final String s17552 = "17552";
+  public static final String s17553 = "17553";
+  public static final String s17554 = "17554";
+  public static final String s17555 = "17555";
+  public static final String s17556 = "17556";
+  public static final String s17557 = "17557";
+  public static final String s17558 = "17558";
+  public static final String s17559 = "17559";
+  public static final String s17560 = "17560";
+  public static final String s17561 = "17561";
+  public static final String s17562 = "17562";
+  public static final String s17563 = "17563";
+  public static final String s17564 = "17564";
+  public static final String s17565 = "17565";
+  public static final String s17566 = "17566";
+  public static final String s17567 = "17567";
+  public static final String s17568 = "17568";
+  public static final String s17569 = "17569";
+  public static final String s17570 = "17570";
+  public static final String s17571 = "17571";
+  public static final String s17572 = "17572";
+  public static final String s17573 = "17573";
+  public static final String s17574 = "17574";
+  public static final String s17575 = "17575";
+  public static final String s17576 = "17576";
+  public static final String s17577 = "17577";
+  public static final String s17578 = "17578";
+  public static final String s17579 = "17579";
+  public static final String s17580 = "17580";
+  public static final String s17581 = "17581";
+  public static final String s17582 = "17582";
+  public static final String s17583 = "17583";
+  public static final String s17584 = "17584";
+  public static final String s17585 = "17585";
+  public static final String s17586 = "17586";
+  public static final String s17587 = "17587";
+  public static final String s17588 = "17588";
+  public static final String s17589 = "17589";
+  public static final String s17590 = "17590";
+  public static final String s17591 = "17591";
+  public static final String s17592 = "17592";
+  public static final String s17593 = "17593";
+  public static final String s17594 = "17594";
+  public static final String s17595 = "17595";
+  public static final String s17596 = "17596";
+  public static final String s17597 = "17597";
+  public static final String s17598 = "17598";
+  public static final String s17599 = "17599";
+  public static final String s17600 = "17600";
+  public static final String s17601 = "17601";
+  public static final String s17602 = "17602";
+  public static final String s17603 = "17603";
+  public static final String s17604 = "17604";
+  public static final String s17605 = "17605";
+  public static final String s17606 = "17606";
+  public static final String s17607 = "17607";
+  public static final String s17608 = "17608";
+  public static final String s17609 = "17609";
+  public static final String s17610 = "17610";
+  public static final String s17611 = "17611";
+  public static final String s17612 = "17612";
+  public static final String s17613 = "17613";
+  public static final String s17614 = "17614";
+  public static final String s17615 = "17615";
+  public static final String s17616 = "17616";
+  public static final String s17617 = "17617";
+  public static final String s17618 = "17618";
+  public static final String s17619 = "17619";
+  public static final String s17620 = "17620";
+  public static final String s17621 = "17621";
+  public static final String s17622 = "17622";
+  public static final String s17623 = "17623";
+  public static final String s17624 = "17624";
+  public static final String s17625 = "17625";
+  public static final String s17626 = "17626";
+  public static final String s17627 = "17627";
+  public static final String s17628 = "17628";
+  public static final String s17629 = "17629";
+  public static final String s17630 = "17630";
+  public static final String s17631 = "17631";
+  public static final String s17632 = "17632";
+  public static final String s17633 = "17633";
+  public static final String s17634 = "17634";
+  public static final String s17635 = "17635";
+  public static final String s17636 = "17636";
+  public static final String s17637 = "17637";
+  public static final String s17638 = "17638";
+  public static final String s17639 = "17639";
+  public static final String s17640 = "17640";
+  public static final String s17641 = "17641";
+  public static final String s17642 = "17642";
+  public static final String s17643 = "17643";
+  public static final String s17644 = "17644";
+  public static final String s17645 = "17645";
+  public static final String s17646 = "17646";
+  public static final String s17647 = "17647";
+  public static final String s17648 = "17648";
+  public static final String s17649 = "17649";
+  public static final String s17650 = "17650";
+  public static final String s17651 = "17651";
+  public static final String s17652 = "17652";
+  public static final String s17653 = "17653";
+  public static final String s17654 = "17654";
+  public static final String s17655 = "17655";
+  public static final String s17656 = "17656";
+  public static final String s17657 = "17657";
+  public static final String s17658 = "17658";
+  public static final String s17659 = "17659";
+  public static final String s17660 = "17660";
+  public static final String s17661 = "17661";
+  public static final String s17662 = "17662";
+  public static final String s17663 = "17663";
+  public static final String s17664 = "17664";
+  public static final String s17665 = "17665";
+  public static final String s17666 = "17666";
+  public static final String s17667 = "17667";
+  public static final String s17668 = "17668";
+  public static final String s17669 = "17669";
+  public static final String s17670 = "17670";
+  public static final String s17671 = "17671";
+  public static final String s17672 = "17672";
+  public static final String s17673 = "17673";
+  public static final String s17674 = "17674";
+  public static final String s17675 = "17675";
+  public static final String s17676 = "17676";
+  public static final String s17677 = "17677";
+  public static final String s17678 = "17678";
+  public static final String s17679 = "17679";
+  public static final String s17680 = "17680";
+  public static final String s17681 = "17681";
+  public static final String s17682 = "17682";
+  public static final String s17683 = "17683";
+  public static final String s17684 = "17684";
+  public static final String s17685 = "17685";
+  public static final String s17686 = "17686";
+  public static final String s17687 = "17687";
+  public static final String s17688 = "17688";
+  public static final String s17689 = "17689";
+  public static final String s17690 = "17690";
+  public static final String s17691 = "17691";
+  public static final String s17692 = "17692";
+  public static final String s17693 = "17693";
+  public static final String s17694 = "17694";
+  public static final String s17695 = "17695";
+  public static final String s17696 = "17696";
+  public static final String s17697 = "17697";
+  public static final String s17698 = "17698";
+  public static final String s17699 = "17699";
+  public static final String s17700 = "17700";
+  public static final String s17701 = "17701";
+  public static final String s17702 = "17702";
+  public static final String s17703 = "17703";
+  public static final String s17704 = "17704";
+  public static final String s17705 = "17705";
+  public static final String s17706 = "17706";
+  public static final String s17707 = "17707";
+  public static final String s17708 = "17708";
+  public static final String s17709 = "17709";
+  public static final String s17710 = "17710";
+  public static final String s17711 = "17711";
+  public static final String s17712 = "17712";
+  public static final String s17713 = "17713";
+  public static final String s17714 = "17714";
+  public static final String s17715 = "17715";
+  public static final String s17716 = "17716";
+  public static final String s17717 = "17717";
+  public static final String s17718 = "17718";
+  public static final String s17719 = "17719";
+  public static final String s17720 = "17720";
+  public static final String s17721 = "17721";
+  public static final String s17722 = "17722";
+  public static final String s17723 = "17723";
+  public static final String s17724 = "17724";
+  public static final String s17725 = "17725";
+  public static final String s17726 = "17726";
+  public static final String s17727 = "17727";
+  public static final String s17728 = "17728";
+  public static final String s17729 = "17729";
+  public static final String s17730 = "17730";
+  public static final String s17731 = "17731";
+  public static final String s17732 = "17732";
+  public static final String s17733 = "17733";
+  public static final String s17734 = "17734";
+  public static final String s17735 = "17735";
+  public static final String s17736 = "17736";
+  public static final String s17737 = "17737";
+  public static final String s17738 = "17738";
+  public static final String s17739 = "17739";
+  public static final String s17740 = "17740";
+  public static final String s17741 = "17741";
+  public static final String s17742 = "17742";
+  public static final String s17743 = "17743";
+  public static final String s17744 = "17744";
+  public static final String s17745 = "17745";
+  public static final String s17746 = "17746";
+  public static final String s17747 = "17747";
+  public static final String s17748 = "17748";
+  public static final String s17749 = "17749";
+  public static final String s17750 = "17750";
+  public static final String s17751 = "17751";
+  public static final String s17752 = "17752";
+  public static final String s17753 = "17753";
+  public static final String s17754 = "17754";
+  public static final String s17755 = "17755";
+  public static final String s17756 = "17756";
+  public static final String s17757 = "17757";
+  public static final String s17758 = "17758";
+  public static final String s17759 = "17759";
+  public static final String s17760 = "17760";
+  public static final String s17761 = "17761";
+  public static final String s17762 = "17762";
+  public static final String s17763 = "17763";
+  public static final String s17764 = "17764";
+  public static final String s17765 = "17765";
+  public static final String s17766 = "17766";
+  public static final String s17767 = "17767";
+  public static final String s17768 = "17768";
+  public static final String s17769 = "17769";
+  public static final String s17770 = "17770";
+  public static final String s17771 = "17771";
+  public static final String s17772 = "17772";
+  public static final String s17773 = "17773";
+  public static final String s17774 = "17774";
+  public static final String s17775 = "17775";
+  public static final String s17776 = "17776";
+  public static final String s17777 = "17777";
+  public static final String s17778 = "17778";
+  public static final String s17779 = "17779";
+  public static final String s17780 = "17780";
+  public static final String s17781 = "17781";
+  public static final String s17782 = "17782";
+  public static final String s17783 = "17783";
+  public static final String s17784 = "17784";
+  public static final String s17785 = "17785";
+  public static final String s17786 = "17786";
+  public static final String s17787 = "17787";
+  public static final String s17788 = "17788";
+  public static final String s17789 = "17789";
+  public static final String s17790 = "17790";
+  public static final String s17791 = "17791";
+  public static final String s17792 = "17792";
+  public static final String s17793 = "17793";
+  public static final String s17794 = "17794";
+  public static final String s17795 = "17795";
+  public static final String s17796 = "17796";
+  public static final String s17797 = "17797";
+  public static final String s17798 = "17798";
+  public static final String s17799 = "17799";
+  public static final String s17800 = "17800";
+  public static final String s17801 = "17801";
+  public static final String s17802 = "17802";
+  public static final String s17803 = "17803";
+  public static final String s17804 = "17804";
+  public static final String s17805 = "17805";
+  public static final String s17806 = "17806";
+  public static final String s17807 = "17807";
+  public static final String s17808 = "17808";
+  public static final String s17809 = "17809";
+  public static final String s17810 = "17810";
+  public static final String s17811 = "17811";
+  public static final String s17812 = "17812";
+  public static final String s17813 = "17813";
+  public static final String s17814 = "17814";
+  public static final String s17815 = "17815";
+  public static final String s17816 = "17816";
+  public static final String s17817 = "17817";
+  public static final String s17818 = "17818";
+  public static final String s17819 = "17819";
+  public static final String s17820 = "17820";
+  public static final String s17821 = "17821";
+  public static final String s17822 = "17822";
+  public static final String s17823 = "17823";
+  public static final String s17824 = "17824";
+  public static final String s17825 = "17825";
+  public static final String s17826 = "17826";
+  public static final String s17827 = "17827";
+  public static final String s17828 = "17828";
+  public static final String s17829 = "17829";
+  public static final String s17830 = "17830";
+  public static final String s17831 = "17831";
+  public static final String s17832 = "17832";
+  public static final String s17833 = "17833";
+  public static final String s17834 = "17834";
+  public static final String s17835 = "17835";
+  public static final String s17836 = "17836";
+  public static final String s17837 = "17837";
+  public static final String s17838 = "17838";
+  public static final String s17839 = "17839";
+  public static final String s17840 = "17840";
+  public static final String s17841 = "17841";
+  public static final String s17842 = "17842";
+  public static final String s17843 = "17843";
+  public static final String s17844 = "17844";
+  public static final String s17845 = "17845";
+  public static final String s17846 = "17846";
+  public static final String s17847 = "17847";
+  public static final String s17848 = "17848";
+  public static final String s17849 = "17849";
+  public static final String s17850 = "17850";
+  public static final String s17851 = "17851";
+  public static final String s17852 = "17852";
+  public static final String s17853 = "17853";
+  public static final String s17854 = "17854";
+  public static final String s17855 = "17855";
+  public static final String s17856 = "17856";
+  public static final String s17857 = "17857";
+  public static final String s17858 = "17858";
+  public static final String s17859 = "17859";
+  public static final String s17860 = "17860";
+  public static final String s17861 = "17861";
+  public static final String s17862 = "17862";
+  public static final String s17863 = "17863";
+  public static final String s17864 = "17864";
+  public static final String s17865 = "17865";
+  public static final String s17866 = "17866";
+  public static final String s17867 = "17867";
+  public static final String s17868 = "17868";
+  public static final String s17869 = "17869";
+  public static final String s17870 = "17870";
+  public static final String s17871 = "17871";
+  public static final String s17872 = "17872";
+  public static final String s17873 = "17873";
+  public static final String s17874 = "17874";
+  public static final String s17875 = "17875";
+  public static final String s17876 = "17876";
+  public static final String s17877 = "17877";
+  public static final String s17878 = "17878";
+  public static final String s17879 = "17879";
+  public static final String s17880 = "17880";
+  public static final String s17881 = "17881";
+  public static final String s17882 = "17882";
+  public static final String s17883 = "17883";
+  public static final String s17884 = "17884";
+  public static final String s17885 = "17885";
+  public static final String s17886 = "17886";
+  public static final String s17887 = "17887";
+  public static final String s17888 = "17888";
+  public static final String s17889 = "17889";
+  public static final String s17890 = "17890";
+  public static final String s17891 = "17891";
+  public static final String s17892 = "17892";
+  public static final String s17893 = "17893";
+  public static final String s17894 = "17894";
+  public static final String s17895 = "17895";
+  public static final String s17896 = "17896";
+  public static final String s17897 = "17897";
+  public static final String s17898 = "17898";
+  public static final String s17899 = "17899";
+  public static final String s17900 = "17900";
+  public static final String s17901 = "17901";
+  public static final String s17902 = "17902";
+  public static final String s17903 = "17903";
+  public static final String s17904 = "17904";
+  public static final String s17905 = "17905";
+  public static final String s17906 = "17906";
+  public static final String s17907 = "17907";
+  public static final String s17908 = "17908";
+  public static final String s17909 = "17909";
+  public static final String s17910 = "17910";
+  public static final String s17911 = "17911";
+  public static final String s17912 = "17912";
+  public static final String s17913 = "17913";
+  public static final String s17914 = "17914";
+  public static final String s17915 = "17915";
+  public static final String s17916 = "17916";
+  public static final String s17917 = "17917";
+  public static final String s17918 = "17918";
+  public static final String s17919 = "17919";
+  public static final String s17920 = "17920";
+  public static final String s17921 = "17921";
+  public static final String s17922 = "17922";
+  public static final String s17923 = "17923";
+  public static final String s17924 = "17924";
+  public static final String s17925 = "17925";
+  public static final String s17926 = "17926";
+  public static final String s17927 = "17927";
+  public static final String s17928 = "17928";
+  public static final String s17929 = "17929";
+  public static final String s17930 = "17930";
+  public static final String s17931 = "17931";
+  public static final String s17932 = "17932";
+  public static final String s17933 = "17933";
+  public static final String s17934 = "17934";
+  public static final String s17935 = "17935";
+  public static final String s17936 = "17936";
+  public static final String s17937 = "17937";
+  public static final String s17938 = "17938";
+  public static final String s17939 = "17939";
+  public static final String s17940 = "17940";
+  public static final String s17941 = "17941";
+  public static final String s17942 = "17942";
+  public static final String s17943 = "17943";
+  public static final String s17944 = "17944";
+  public static final String s17945 = "17945";
+  public static final String s17946 = "17946";
+  public static final String s17947 = "17947";
+  public static final String s17948 = "17948";
+  public static final String s17949 = "17949";
+  public static final String s17950 = "17950";
+  public static final String s17951 = "17951";
+  public static final String s17952 = "17952";
+  public static final String s17953 = "17953";
+  public static final String s17954 = "17954";
+  public static final String s17955 = "17955";
+  public static final String s17956 = "17956";
+  public static final String s17957 = "17957";
+  public static final String s17958 = "17958";
+  public static final String s17959 = "17959";
+  public static final String s17960 = "17960";
+  public static final String s17961 = "17961";
+  public static final String s17962 = "17962";
+  public static final String s17963 = "17963";
+  public static final String s17964 = "17964";
+  public static final String s17965 = "17965";
+  public static final String s17966 = "17966";
+  public static final String s17967 = "17967";
+  public static final String s17968 = "17968";
+  public static final String s17969 = "17969";
+  public static final String s17970 = "17970";
+  public static final String s17971 = "17971";
+  public static final String s17972 = "17972";
+  public static final String s17973 = "17973";
+  public static final String s17974 = "17974";
+  public static final String s17975 = "17975";
+  public static final String s17976 = "17976";
+  public static final String s17977 = "17977";
+  public static final String s17978 = "17978";
+  public static final String s17979 = "17979";
+  public static final String s17980 = "17980";
+  public static final String s17981 = "17981";
+  public static final String s17982 = "17982";
+  public static final String s17983 = "17983";
+  public static final String s17984 = "17984";
+  public static final String s17985 = "17985";
+  public static final String s17986 = "17986";
+  public static final String s17987 = "17987";
+  public static final String s17988 = "17988";
+  public static final String s17989 = "17989";
+  public static final String s17990 = "17990";
+  public static final String s17991 = "17991";
+  public static final String s17992 = "17992";
+  public static final String s17993 = "17993";
+  public static final String s17994 = "17994";
+  public static final String s17995 = "17995";
+  public static final String s17996 = "17996";
+  public static final String s17997 = "17997";
+  public static final String s17998 = "17998";
+  public static final String s17999 = "17999";
+  public static final String s18000 = "18000";
+  public static final String s18001 = "18001";
+  public static final String s18002 = "18002";
+  public static final String s18003 = "18003";
+  public static final String s18004 = "18004";
+  public static final String s18005 = "18005";
+  public static final String s18006 = "18006";
+  public static final String s18007 = "18007";
+  public static final String s18008 = "18008";
+  public static final String s18009 = "18009";
+  public static final String s18010 = "18010";
+  public static final String s18011 = "18011";
+  public static final String s18012 = "18012";
+  public static final String s18013 = "18013";
+  public static final String s18014 = "18014";
+  public static final String s18015 = "18015";
+  public static final String s18016 = "18016";
+  public static final String s18017 = "18017";
+  public static final String s18018 = "18018";
+  public static final String s18019 = "18019";
+  public static final String s18020 = "18020";
+  public static final String s18021 = "18021";
+  public static final String s18022 = "18022";
+  public static final String s18023 = "18023";
+  public static final String s18024 = "18024";
+  public static final String s18025 = "18025";
+  public static final String s18026 = "18026";
+  public static final String s18027 = "18027";
+  public static final String s18028 = "18028";
+  public static final String s18029 = "18029";
+  public static final String s18030 = "18030";
+  public static final String s18031 = "18031";
+  public static final String s18032 = "18032";
+  public static final String s18033 = "18033";
+  public static final String s18034 = "18034";
+  public static final String s18035 = "18035";
+  public static final String s18036 = "18036";
+  public static final String s18037 = "18037";
+  public static final String s18038 = "18038";
+  public static final String s18039 = "18039";
+  public static final String s18040 = "18040";
+  public static final String s18041 = "18041";
+  public static final String s18042 = "18042";
+  public static final String s18043 = "18043";
+  public static final String s18044 = "18044";
+  public static final String s18045 = "18045";
+  public static final String s18046 = "18046";
+  public static final String s18047 = "18047";
+  public static final String s18048 = "18048";
+  public static final String s18049 = "18049";
+  public static final String s18050 = "18050";
+  public static final String s18051 = "18051";
+  public static final String s18052 = "18052";
+  public static final String s18053 = "18053";
+  public static final String s18054 = "18054";
+  public static final String s18055 = "18055";
+  public static final String s18056 = "18056";
+  public static final String s18057 = "18057";
+  public static final String s18058 = "18058";
+  public static final String s18059 = "18059";
+  public static final String s18060 = "18060";
+  public static final String s18061 = "18061";
+  public static final String s18062 = "18062";
+  public static final String s18063 = "18063";
+  public static final String s18064 = "18064";
+  public static final String s18065 = "18065";
+  public static final String s18066 = "18066";
+  public static final String s18067 = "18067";
+  public static final String s18068 = "18068";
+  public static final String s18069 = "18069";
+  public static final String s18070 = "18070";
+  public static final String s18071 = "18071";
+  public static final String s18072 = "18072";
+  public static final String s18073 = "18073";
+  public static final String s18074 = "18074";
+  public static final String s18075 = "18075";
+  public static final String s18076 = "18076";
+  public static final String s18077 = "18077";
+  public static final String s18078 = "18078";
+  public static final String s18079 = "18079";
+  public static final String s18080 = "18080";
+  public static final String s18081 = "18081";
+  public static final String s18082 = "18082";
+  public static final String s18083 = "18083";
+  public static final String s18084 = "18084";
+  public static final String s18085 = "18085";
+  public static final String s18086 = "18086";
+  public static final String s18087 = "18087";
+  public static final String s18088 = "18088";
+  public static final String s18089 = "18089";
+  public static final String s18090 = "18090";
+  public static final String s18091 = "18091";
+  public static final String s18092 = "18092";
+  public static final String s18093 = "18093";
+  public static final String s18094 = "18094";
+  public static final String s18095 = "18095";
+  public static final String s18096 = "18096";
+  public static final String s18097 = "18097";
+  public static final String s18098 = "18098";
+  public static final String s18099 = "18099";
+  public static final String s18100 = "18100";
+  public static final String s18101 = "18101";
+  public static final String s18102 = "18102";
+  public static final String s18103 = "18103";
+  public static final String s18104 = "18104";
+  public static final String s18105 = "18105";
+  public static final String s18106 = "18106";
+  public static final String s18107 = "18107";
+  public static final String s18108 = "18108";
+  public static final String s18109 = "18109";
+  public static final String s18110 = "18110";
+  public static final String s18111 = "18111";
+  public static final String s18112 = "18112";
+  public static final String s18113 = "18113";
+  public static final String s18114 = "18114";
+  public static final String s18115 = "18115";
+  public static final String s18116 = "18116";
+  public static final String s18117 = "18117";
+  public static final String s18118 = "18118";
+  public static final String s18119 = "18119";
+  public static final String s18120 = "18120";
+  public static final String s18121 = "18121";
+  public static final String s18122 = "18122";
+  public static final String s18123 = "18123";
+  public static final String s18124 = "18124";
+  public static final String s18125 = "18125";
+  public static final String s18126 = "18126";
+  public static final String s18127 = "18127";
+  public static final String s18128 = "18128";
+  public static final String s18129 = "18129";
+  public static final String s18130 = "18130";
+  public static final String s18131 = "18131";
+  public static final String s18132 = "18132";
+  public static final String s18133 = "18133";
+  public static final String s18134 = "18134";
+  public static final String s18135 = "18135";
+  public static final String s18136 = "18136";
+  public static final String s18137 = "18137";
+  public static final String s18138 = "18138";
+  public static final String s18139 = "18139";
+  public static final String s18140 = "18140";
+  public static final String s18141 = "18141";
+  public static final String s18142 = "18142";
+  public static final String s18143 = "18143";
+  public static final String s18144 = "18144";
+  public static final String s18145 = "18145";
+  public static final String s18146 = "18146";
+  public static final String s18147 = "18147";
+  public static final String s18148 = "18148";
+  public static final String s18149 = "18149";
+  public static final String s18150 = "18150";
+  public static final String s18151 = "18151";
+  public static final String s18152 = "18152";
+  public static final String s18153 = "18153";
+  public static final String s18154 = "18154";
+  public static final String s18155 = "18155";
+  public static final String s18156 = "18156";
+  public static final String s18157 = "18157";
+  public static final String s18158 = "18158";
+  public static final String s18159 = "18159";
+  public static final String s18160 = "18160";
+  public static final String s18161 = "18161";
+  public static final String s18162 = "18162";
+  public static final String s18163 = "18163";
+  public static final String s18164 = "18164";
+  public static final String s18165 = "18165";
+  public static final String s18166 = "18166";
+  public static final String s18167 = "18167";
+  public static final String s18168 = "18168";
+  public static final String s18169 = "18169";
+  public static final String s18170 = "18170";
+  public static final String s18171 = "18171";
+  public static final String s18172 = "18172";
+  public static final String s18173 = "18173";
+  public static final String s18174 = "18174";
+  public static final String s18175 = "18175";
+  public static final String s18176 = "18176";
+  public static final String s18177 = "18177";
+  public static final String s18178 = "18178";
+  public static final String s18179 = "18179";
+  public static final String s18180 = "18180";
+  public static final String s18181 = "18181";
+  public static final String s18182 = "18182";
+  public static final String s18183 = "18183";
+  public static final String s18184 = "18184";
+  public static final String s18185 = "18185";
+  public static final String s18186 = "18186";
+  public static final String s18187 = "18187";
+  public static final String s18188 = "18188";
+  public static final String s18189 = "18189";
+  public static final String s18190 = "18190";
+  public static final String s18191 = "18191";
+  public static final String s18192 = "18192";
+  public static final String s18193 = "18193";
+  public static final String s18194 = "18194";
+  public static final String s18195 = "18195";
+  public static final String s18196 = "18196";
+  public static final String s18197 = "18197";
+  public static final String s18198 = "18198";
+  public static final String s18199 = "18199";
+  public static final String s18200 = "18200";
+  public static final String s18201 = "18201";
+  public static final String s18202 = "18202";
+  public static final String s18203 = "18203";
+  public static final String s18204 = "18204";
+  public static final String s18205 = "18205";
+  public static final String s18206 = "18206";
+  public static final String s18207 = "18207";
+  public static final String s18208 = "18208";
+  public static final String s18209 = "18209";
+  public static final String s18210 = "18210";
+  public static final String s18211 = "18211";
+  public static final String s18212 = "18212";
+  public static final String s18213 = "18213";
+  public static final String s18214 = "18214";
+  public static final String s18215 = "18215";
+  public static final String s18216 = "18216";
+  public static final String s18217 = "18217";
+  public static final String s18218 = "18218";
+  public static final String s18219 = "18219";
+  public static final String s18220 = "18220";
+  public static final String s18221 = "18221";
+  public static final String s18222 = "18222";
+  public static final String s18223 = "18223";
+  public static final String s18224 = "18224";
+  public static final String s18225 = "18225";
+  public static final String s18226 = "18226";
+  public static final String s18227 = "18227";
+  public static final String s18228 = "18228";
+  public static final String s18229 = "18229";
+  public static final String s18230 = "18230";
+  public static final String s18231 = "18231";
+  public static final String s18232 = "18232";
+  public static final String s18233 = "18233";
+  public static final String s18234 = "18234";
+  public static final String s18235 = "18235";
+  public static final String s18236 = "18236";
+  public static final String s18237 = "18237";
+  public static final String s18238 = "18238";
+  public static final String s18239 = "18239";
+  public static final String s18240 = "18240";
+  public static final String s18241 = "18241";
+  public static final String s18242 = "18242";
+  public static final String s18243 = "18243";
+  public static final String s18244 = "18244";
+  public static final String s18245 = "18245";
+  public static final String s18246 = "18246";
+  public static final String s18247 = "18247";
+  public static final String s18248 = "18248";
+  public static final String s18249 = "18249";
+  public static final String s18250 = "18250";
+  public static final String s18251 = "18251";
+  public static final String s18252 = "18252";
+  public static final String s18253 = "18253";
+  public static final String s18254 = "18254";
+  public static final String s18255 = "18255";
+  public static final String s18256 = "18256";
+  public static final String s18257 = "18257";
+  public static final String s18258 = "18258";
+  public static final String s18259 = "18259";
+  public static final String s18260 = "18260";
+  public static final String s18261 = "18261";
+  public static final String s18262 = "18262";
+  public static final String s18263 = "18263";
+  public static final String s18264 = "18264";
+  public static final String s18265 = "18265";
+  public static final String s18266 = "18266";
+  public static final String s18267 = "18267";
+  public static final String s18268 = "18268";
+  public static final String s18269 = "18269";
+  public static final String s18270 = "18270";
+  public static final String s18271 = "18271";
+  public static final String s18272 = "18272";
+  public static final String s18273 = "18273";
+  public static final String s18274 = "18274";
+  public static final String s18275 = "18275";
+  public static final String s18276 = "18276";
+  public static final String s18277 = "18277";
+  public static final String s18278 = "18278";
+  public static final String s18279 = "18279";
+  public static final String s18280 = "18280";
+  public static final String s18281 = "18281";
+  public static final String s18282 = "18282";
+  public static final String s18283 = "18283";
+  public static final String s18284 = "18284";
+  public static final String s18285 = "18285";
+  public static final String s18286 = "18286";
+  public static final String s18287 = "18287";
+  public static final String s18288 = "18288";
+  public static final String s18289 = "18289";
+  public static final String s18290 = "18290";
+  public static final String s18291 = "18291";
+  public static final String s18292 = "18292";
+  public static final String s18293 = "18293";
+  public static final String s18294 = "18294";
+  public static final String s18295 = "18295";
+  public static final String s18296 = "18296";
+  public static final String s18297 = "18297";
+  public static final String s18298 = "18298";
+  public static final String s18299 = "18299";
+  public static final String s18300 = "18300";
+  public static final String s18301 = "18301";
+  public static final String s18302 = "18302";
+  public static final String s18303 = "18303";
+  public static final String s18304 = "18304";
+  public static final String s18305 = "18305";
+  public static final String s18306 = "18306";
+  public static final String s18307 = "18307";
+  public static final String s18308 = "18308";
+  public static final String s18309 = "18309";
+  public static final String s18310 = "18310";
+  public static final String s18311 = "18311";
+  public static final String s18312 = "18312";
+  public static final String s18313 = "18313";
+  public static final String s18314 = "18314";
+  public static final String s18315 = "18315";
+  public static final String s18316 = "18316";
+  public static final String s18317 = "18317";
+  public static final String s18318 = "18318";
+  public static final String s18319 = "18319";
+  public static final String s18320 = "18320";
+  public static final String s18321 = "18321";
+  public static final String s18322 = "18322";
+  public static final String s18323 = "18323";
+  public static final String s18324 = "18324";
+  public static final String s18325 = "18325";
+  public static final String s18326 = "18326";
+  public static final String s18327 = "18327";
+  public static final String s18328 = "18328";
+  public static final String s18329 = "18329";
+  public static final String s18330 = "18330";
+  public static final String s18331 = "18331";
+  public static final String s18332 = "18332";
+  public static final String s18333 = "18333";
+  public static final String s18334 = "18334";
+  public static final String s18335 = "18335";
+  public static final String s18336 = "18336";
+  public static final String s18337 = "18337";
+  public static final String s18338 = "18338";
+  public static final String s18339 = "18339";
+  public static final String s18340 = "18340";
+  public static final String s18341 = "18341";
+  public static final String s18342 = "18342";
+  public static final String s18343 = "18343";
+  public static final String s18344 = "18344";
+  public static final String s18345 = "18345";
+  public static final String s18346 = "18346";
+  public static final String s18347 = "18347";
+  public static final String s18348 = "18348";
+  public static final String s18349 = "18349";
+  public static final String s18350 = "18350";
+  public static final String s18351 = "18351";
+  public static final String s18352 = "18352";
+  public static final String s18353 = "18353";
+  public static final String s18354 = "18354";
+  public static final String s18355 = "18355";
+  public static final String s18356 = "18356";
+  public static final String s18357 = "18357";
+  public static final String s18358 = "18358";
+  public static final String s18359 = "18359";
+  public static final String s18360 = "18360";
+  public static final String s18361 = "18361";
+  public static final String s18362 = "18362";
+  public static final String s18363 = "18363";
+  public static final String s18364 = "18364";
+  public static final String s18365 = "18365";
+  public static final String s18366 = "18366";
+  public static final String s18367 = "18367";
+  public static final String s18368 = "18368";
+  public static final String s18369 = "18369";
+  public static final String s18370 = "18370";
+  public static final String s18371 = "18371";
+  public static final String s18372 = "18372";
+  public static final String s18373 = "18373";
+  public static final String s18374 = "18374";
+  public static final String s18375 = "18375";
+  public static final String s18376 = "18376";
+  public static final String s18377 = "18377";
+  public static final String s18378 = "18378";
+  public static final String s18379 = "18379";
+  public static final String s18380 = "18380";
+  public static final String s18381 = "18381";
+  public static final String s18382 = "18382";
+  public static final String s18383 = "18383";
+  public static final String s18384 = "18384";
+  public static final String s18385 = "18385";
+  public static final String s18386 = "18386";
+  public static final String s18387 = "18387";
+  public static final String s18388 = "18388";
+  public static final String s18389 = "18389";
+  public static final String s18390 = "18390";
+  public static final String s18391 = "18391";
+  public static final String s18392 = "18392";
+  public static final String s18393 = "18393";
+  public static final String s18394 = "18394";
+  public static final String s18395 = "18395";
+  public static final String s18396 = "18396";
+  public static final String s18397 = "18397";
+  public static final String s18398 = "18398";
+  public static final String s18399 = "18399";
+  public static final String s18400 = "18400";
+  public static final String s18401 = "18401";
+  public static final String s18402 = "18402";
+  public static final String s18403 = "18403";
+  public static final String s18404 = "18404";
+  public static final String s18405 = "18405";
+  public static final String s18406 = "18406";
+  public static final String s18407 = "18407";
+  public static final String s18408 = "18408";
+  public static final String s18409 = "18409";
+  public static final String s18410 = "18410";
+  public static final String s18411 = "18411";
+  public static final String s18412 = "18412";
+  public static final String s18413 = "18413";
+  public static final String s18414 = "18414";
+  public static final String s18415 = "18415";
+  public static final String s18416 = "18416";
+  public static final String s18417 = "18417";
+  public static final String s18418 = "18418";
+  public static final String s18419 = "18419";
+  public static final String s18420 = "18420";
+  public static final String s18421 = "18421";
+  public static final String s18422 = "18422";
+  public static final String s18423 = "18423";
+  public static final String s18424 = "18424";
+  public static final String s18425 = "18425";
+  public static final String s18426 = "18426";
+  public static final String s18427 = "18427";
+  public static final String s18428 = "18428";
+  public static final String s18429 = "18429";
+  public static final String s18430 = "18430";
+  public static final String s18431 = "18431";
+  public static final String s18432 = "18432";
+  public static final String s18433 = "18433";
+  public static final String s18434 = "18434";
+  public static final String s18435 = "18435";
+  public static final String s18436 = "18436";
+  public static final String s18437 = "18437";
+  public static final String s18438 = "18438";
+  public static final String s18439 = "18439";
+  public static final String s18440 = "18440";
+  public static final String s18441 = "18441";
+  public static final String s18442 = "18442";
+  public static final String s18443 = "18443";
+  public static final String s18444 = "18444";
+  public static final String s18445 = "18445";
+  public static final String s18446 = "18446";
+  public static final String s18447 = "18447";
+  public static final String s18448 = "18448";
+  public static final String s18449 = "18449";
+  public static final String s18450 = "18450";
+  public static final String s18451 = "18451";
+  public static final String s18452 = "18452";
+  public static final String s18453 = "18453";
+  public static final String s18454 = "18454";
+  public static final String s18455 = "18455";
+  public static final String s18456 = "18456";
+  public static final String s18457 = "18457";
+  public static final String s18458 = "18458";
+  public static final String s18459 = "18459";
+  public static final String s18460 = "18460";
+  public static final String s18461 = "18461";
+  public static final String s18462 = "18462";
+  public static final String s18463 = "18463";
+  public static final String s18464 = "18464";
+  public static final String s18465 = "18465";
+  public static final String s18466 = "18466";
+  public static final String s18467 = "18467";
+  public static final String s18468 = "18468";
+  public static final String s18469 = "18469";
+  public static final String s18470 = "18470";
+  public static final String s18471 = "18471";
+  public static final String s18472 = "18472";
+  public static final String s18473 = "18473";
+  public static final String s18474 = "18474";
+  public static final String s18475 = "18475";
+  public static final String s18476 = "18476";
+  public static final String s18477 = "18477";
+  public static final String s18478 = "18478";
+  public static final String s18479 = "18479";
+  public static final String s18480 = "18480";
+  public static final String s18481 = "18481";
+  public static final String s18482 = "18482";
+  public static final String s18483 = "18483";
+  public static final String s18484 = "18484";
+  public static final String s18485 = "18485";
+  public static final String s18486 = "18486";
+  public static final String s18487 = "18487";
+  public static final String s18488 = "18488";
+  public static final String s18489 = "18489";
+  public static final String s18490 = "18490";
+  public static final String s18491 = "18491";
+  public static final String s18492 = "18492";
+  public static final String s18493 = "18493";
+  public static final String s18494 = "18494";
+  public static final String s18495 = "18495";
+  public static final String s18496 = "18496";
+  public static final String s18497 = "18497";
+  public static final String s18498 = "18498";
+  public static final String s18499 = "18499";
+  public static final String s18500 = "18500";
+  public static final String s18501 = "18501";
+  public static final String s18502 = "18502";
+  public static final String s18503 = "18503";
+  public static final String s18504 = "18504";
+  public static final String s18505 = "18505";
+  public static final String s18506 = "18506";
+  public static final String s18507 = "18507";
+  public static final String s18508 = "18508";
+  public static final String s18509 = "18509";
+  public static final String s18510 = "18510";
+  public static final String s18511 = "18511";
+  public static final String s18512 = "18512";
+  public static final String s18513 = "18513";
+  public static final String s18514 = "18514";
+  public static final String s18515 = "18515";
+  public static final String s18516 = "18516";
+  public static final String s18517 = "18517";
+  public static final String s18518 = "18518";
+  public static final String s18519 = "18519";
+  public static final String s18520 = "18520";
+  public static final String s18521 = "18521";
+  public static final String s18522 = "18522";
+  public static final String s18523 = "18523";
+  public static final String s18524 = "18524";
+  public static final String s18525 = "18525";
+  public static final String s18526 = "18526";
+  public static final String s18527 = "18527";
+  public static final String s18528 = "18528";
+  public static final String s18529 = "18529";
+  public static final String s18530 = "18530";
+  public static final String s18531 = "18531";
+  public static final String s18532 = "18532";
+  public static final String s18533 = "18533";
+  public static final String s18534 = "18534";
+  public static final String s18535 = "18535";
+  public static final String s18536 = "18536";
+  public static final String s18537 = "18537";
+  public static final String s18538 = "18538";
+  public static final String s18539 = "18539";
+  public static final String s18540 = "18540";
+  public static final String s18541 = "18541";
+  public static final String s18542 = "18542";
+  public static final String s18543 = "18543";
+  public static final String s18544 = "18544";
+  public static final String s18545 = "18545";
+  public static final String s18546 = "18546";
+  public static final String s18547 = "18547";
+  public static final String s18548 = "18548";
+  public static final String s18549 = "18549";
+  public static final String s18550 = "18550";
+  public static final String s18551 = "18551";
+  public static final String s18552 = "18552";
+  public static final String s18553 = "18553";
+  public static final String s18554 = "18554";
+  public static final String s18555 = "18555";
+  public static final String s18556 = "18556";
+  public static final String s18557 = "18557";
+  public static final String s18558 = "18558";
+  public static final String s18559 = "18559";
+  public static final String s18560 = "18560";
+  public static final String s18561 = "18561";
+  public static final String s18562 = "18562";
+  public static final String s18563 = "18563";
+  public static final String s18564 = "18564";
+  public static final String s18565 = "18565";
+  public static final String s18566 = "18566";
+  public static final String s18567 = "18567";
+  public static final String s18568 = "18568";
+  public static final String s18569 = "18569";
+  public static final String s18570 = "18570";
+  public static final String s18571 = "18571";
+  public static final String s18572 = "18572";
+  public static final String s18573 = "18573";
+  public static final String s18574 = "18574";
+  public static final String s18575 = "18575";
+  public static final String s18576 = "18576";
+  public static final String s18577 = "18577";
+  public static final String s18578 = "18578";
+  public static final String s18579 = "18579";
+  public static final String s18580 = "18580";
+  public static final String s18581 = "18581";
+  public static final String s18582 = "18582";
+  public static final String s18583 = "18583";
+  public static final String s18584 = "18584";
+  public static final String s18585 = "18585";
+  public static final String s18586 = "18586";
+  public static final String s18587 = "18587";
+  public static final String s18588 = "18588";
+  public static final String s18589 = "18589";
+  public static final String s18590 = "18590";
+  public static final String s18591 = "18591";
+  public static final String s18592 = "18592";
+  public static final String s18593 = "18593";
+  public static final String s18594 = "18594";
+  public static final String s18595 = "18595";
+  public static final String s18596 = "18596";
+  public static final String s18597 = "18597";
+  public static final String s18598 = "18598";
+  public static final String s18599 = "18599";
+  public static final String s18600 = "18600";
+  public static final String s18601 = "18601";
+  public static final String s18602 = "18602";
+  public static final String s18603 = "18603";
+  public static final String s18604 = "18604";
+  public static final String s18605 = "18605";
+  public static final String s18606 = "18606";
+  public static final String s18607 = "18607";
+  public static final String s18608 = "18608";
+  public static final String s18609 = "18609";
+  public static final String s18610 = "18610";
+  public static final String s18611 = "18611";
+  public static final String s18612 = "18612";
+  public static final String s18613 = "18613";
+  public static final String s18614 = "18614";
+  public static final String s18615 = "18615";
+  public static final String s18616 = "18616";
+  public static final String s18617 = "18617";
+  public static final String s18618 = "18618";
+  public static final String s18619 = "18619";
+  public static final String s18620 = "18620";
+  public static final String s18621 = "18621";
+  public static final String s18622 = "18622";
+  public static final String s18623 = "18623";
+  public static final String s18624 = "18624";
+  public static final String s18625 = "18625";
+  public static final String s18626 = "18626";
+  public static final String s18627 = "18627";
+  public static final String s18628 = "18628";
+  public static final String s18629 = "18629";
+  public static final String s18630 = "18630";
+  public static final String s18631 = "18631";
+  public static final String s18632 = "18632";
+  public static final String s18633 = "18633";
+  public static final String s18634 = "18634";
+  public static final String s18635 = "18635";
+  public static final String s18636 = "18636";
+  public static final String s18637 = "18637";
+  public static final String s18638 = "18638";
+  public static final String s18639 = "18639";
+  public static final String s18640 = "18640";
+  public static final String s18641 = "18641";
+  public static final String s18642 = "18642";
+  public static final String s18643 = "18643";
+  public static final String s18644 = "18644";
+  public static final String s18645 = "18645";
+  public static final String s18646 = "18646";
+  public static final String s18647 = "18647";
+  public static final String s18648 = "18648";
+  public static final String s18649 = "18649";
+  public static final String s18650 = "18650";
+  public static final String s18651 = "18651";
+  public static final String s18652 = "18652";
+  public static final String s18653 = "18653";
+  public static final String s18654 = "18654";
+  public static final String s18655 = "18655";
+  public static final String s18656 = "18656";
+  public static final String s18657 = "18657";
+  public static final String s18658 = "18658";
+  public static final String s18659 = "18659";
+  public static final String s18660 = "18660";
+  public static final String s18661 = "18661";
+  public static final String s18662 = "18662";
+  public static final String s18663 = "18663";
+  public static final String s18664 = "18664";
+  public static final String s18665 = "18665";
+  public static final String s18666 = "18666";
+  public static final String s18667 = "18667";
+  public static final String s18668 = "18668";
+  public static final String s18669 = "18669";
+  public static final String s18670 = "18670";
+  public static final String s18671 = "18671";
+  public static final String s18672 = "18672";
+  public static final String s18673 = "18673";
+  public static final String s18674 = "18674";
+  public static final String s18675 = "18675";
+  public static final String s18676 = "18676";
+  public static final String s18677 = "18677";
+  public static final String s18678 = "18678";
+  public static final String s18679 = "18679";
+  public static final String s18680 = "18680";
+  public static final String s18681 = "18681";
+  public static final String s18682 = "18682";
+  public static final String s18683 = "18683";
+  public static final String s18684 = "18684";
+  public static final String s18685 = "18685";
+  public static final String s18686 = "18686";
+  public static final String s18687 = "18687";
+  public static final String s18688 = "18688";
+  public static final String s18689 = "18689";
+  public static final String s18690 = "18690";
+  public static final String s18691 = "18691";
+  public static final String s18692 = "18692";
+  public static final String s18693 = "18693";
+  public static final String s18694 = "18694";
+  public static final String s18695 = "18695";
+  public static final String s18696 = "18696";
+  public static final String s18697 = "18697";
+  public static final String s18698 = "18698";
+  public static final String s18699 = "18699";
+  public static final String s18700 = "18700";
+  public static final String s18701 = "18701";
+  public static final String s18702 = "18702";
+  public static final String s18703 = "18703";
+  public static final String s18704 = "18704";
+  public static final String s18705 = "18705";
+  public static final String s18706 = "18706";
+  public static final String s18707 = "18707";
+  public static final String s18708 = "18708";
+  public static final String s18709 = "18709";
+  public static final String s18710 = "18710";
+  public static final String s18711 = "18711";
+  public static final String s18712 = "18712";
+  public static final String s18713 = "18713";
+  public static final String s18714 = "18714";
+  public static final String s18715 = "18715";
+  public static final String s18716 = "18716";
+  public static final String s18717 = "18717";
+  public static final String s18718 = "18718";
+  public static final String s18719 = "18719";
+  public static final String s18720 = "18720";
+  public static final String s18721 = "18721";
+  public static final String s18722 = "18722";
+  public static final String s18723 = "18723";
+  public static final String s18724 = "18724";
+  public static final String s18725 = "18725";
+  public static final String s18726 = "18726";
+  public static final String s18727 = "18727";
+  public static final String s18728 = "18728";
+  public static final String s18729 = "18729";
+  public static final String s18730 = "18730";
+  public static final String s18731 = "18731";
+  public static final String s18732 = "18732";
+  public static final String s18733 = "18733";
+  public static final String s18734 = "18734";
+  public static final String s18735 = "18735";
+  public static final String s18736 = "18736";
+  public static final String s18737 = "18737";
+  public static final String s18738 = "18738";
+  public static final String s18739 = "18739";
+  public static final String s18740 = "18740";
+  public static final String s18741 = "18741";
+  public static final String s18742 = "18742";
+  public static final String s18743 = "18743";
+  public static final String s18744 = "18744";
+  public static final String s18745 = "18745";
+  public static final String s18746 = "18746";
+  public static final String s18747 = "18747";
+  public static final String s18748 = "18748";
+  public static final String s18749 = "18749";
+  public static final String s18750 = "18750";
+  public static final String s18751 = "18751";
+  public static final String s18752 = "18752";
+  public static final String s18753 = "18753";
+  public static final String s18754 = "18754";
+  public static final String s18755 = "18755";
+  public static final String s18756 = "18756";
+  public static final String s18757 = "18757";
+  public static final String s18758 = "18758";
+  public static final String s18759 = "18759";
+  public static final String s18760 = "18760";
+  public static final String s18761 = "18761";
+  public static final String s18762 = "18762";
+  public static final String s18763 = "18763";
+  public static final String s18764 = "18764";
+  public static final String s18765 = "18765";
+  public static final String s18766 = "18766";
+  public static final String s18767 = "18767";
+  public static final String s18768 = "18768";
+  public static final String s18769 = "18769";
+  public static final String s18770 = "18770";
+  public static final String s18771 = "18771";
+  public static final String s18772 = "18772";
+  public static final String s18773 = "18773";
+  public static final String s18774 = "18774";
+  public static final String s18775 = "18775";
+  public static final String s18776 = "18776";
+  public static final String s18777 = "18777";
+  public static final String s18778 = "18778";
+  public static final String s18779 = "18779";
+  public static final String s18780 = "18780";
+  public static final String s18781 = "18781";
+  public static final String s18782 = "18782";
+  public static final String s18783 = "18783";
+  public static final String s18784 = "18784";
+  public static final String s18785 = "18785";
+  public static final String s18786 = "18786";
+  public static final String s18787 = "18787";
+  public static final String s18788 = "18788";
+  public static final String s18789 = "18789";
+  public static final String s18790 = "18790";
+  public static final String s18791 = "18791";
+  public static final String s18792 = "18792";
+  public static final String s18793 = "18793";
+  public static final String s18794 = "18794";
+  public static final String s18795 = "18795";
+  public static final String s18796 = "18796";
+  public static final String s18797 = "18797";
+  public static final String s18798 = "18798";
+  public static final String s18799 = "18799";
+  public static final String s18800 = "18800";
+  public static final String s18801 = "18801";
+  public static final String s18802 = "18802";
+  public static final String s18803 = "18803";
+  public static final String s18804 = "18804";
+  public static final String s18805 = "18805";
+  public static final String s18806 = "18806";
+  public static final String s18807 = "18807";
+  public static final String s18808 = "18808";
+  public static final String s18809 = "18809";
+  public static final String s18810 = "18810";
+  public static final String s18811 = "18811";
+  public static final String s18812 = "18812";
+  public static final String s18813 = "18813";
+  public static final String s18814 = "18814";
+  public static final String s18815 = "18815";
+  public static final String s18816 = "18816";
+  public static final String s18817 = "18817";
+  public static final String s18818 = "18818";
+  public static final String s18819 = "18819";
+  public static final String s18820 = "18820";
+  public static final String s18821 = "18821";
+  public static final String s18822 = "18822";
+  public static final String s18823 = "18823";
+  public static final String s18824 = "18824";
+  public static final String s18825 = "18825";
+  public static final String s18826 = "18826";
+  public static final String s18827 = "18827";
+  public static final String s18828 = "18828";
+  public static final String s18829 = "18829";
+  public static final String s18830 = "18830";
+  public static final String s18831 = "18831";
+  public static final String s18832 = "18832";
+  public static final String s18833 = "18833";
+  public static final String s18834 = "18834";
+  public static final String s18835 = "18835";
+  public static final String s18836 = "18836";
+  public static final String s18837 = "18837";
+  public static final String s18838 = "18838";
+  public static final String s18839 = "18839";
+  public static final String s18840 = "18840";
+  public static final String s18841 = "18841";
+  public static final String s18842 = "18842";
+  public static final String s18843 = "18843";
+  public static final String s18844 = "18844";
+  public static final String s18845 = "18845";
+  public static final String s18846 = "18846";
+  public static final String s18847 = "18847";
+  public static final String s18848 = "18848";
+  public static final String s18849 = "18849";
+  public static final String s18850 = "18850";
+  public static final String s18851 = "18851";
+  public static final String s18852 = "18852";
+  public static final String s18853 = "18853";
+  public static final String s18854 = "18854";
+  public static final String s18855 = "18855";
+  public static final String s18856 = "18856";
+  public static final String s18857 = "18857";
+  public static final String s18858 = "18858";
+  public static final String s18859 = "18859";
+  public static final String s18860 = "18860";
+  public static final String s18861 = "18861";
+  public static final String s18862 = "18862";
+  public static final String s18863 = "18863";
+  public static final String s18864 = "18864";
+  public static final String s18865 = "18865";
+  public static final String s18866 = "18866";
+  public static final String s18867 = "18867";
+  public static final String s18868 = "18868";
+  public static final String s18869 = "18869";
+  public static final String s18870 = "18870";
+  public static final String s18871 = "18871";
+  public static final String s18872 = "18872";
+  public static final String s18873 = "18873";
+  public static final String s18874 = "18874";
+  public static final String s18875 = "18875";
+  public static final String s18876 = "18876";
+  public static final String s18877 = "18877";
+  public static final String s18878 = "18878";
+  public static final String s18879 = "18879";
+  public static final String s18880 = "18880";
+  public static final String s18881 = "18881";
+  public static final String s18882 = "18882";
+  public static final String s18883 = "18883";
+  public static final String s18884 = "18884";
+  public static final String s18885 = "18885";
+  public static final String s18886 = "18886";
+  public static final String s18887 = "18887";
+  public static final String s18888 = "18888";
+  public static final String s18889 = "18889";
+  public static final String s18890 = "18890";
+  public static final String s18891 = "18891";
+  public static final String s18892 = "18892";
+  public static final String s18893 = "18893";
+  public static final String s18894 = "18894";
+  public static final String s18895 = "18895";
+  public static final String s18896 = "18896";
+  public static final String s18897 = "18897";
+  public static final String s18898 = "18898";
+  public static final String s18899 = "18899";
+  public static final String s18900 = "18900";
+  public static final String s18901 = "18901";
+  public static final String s18902 = "18902";
+  public static final String s18903 = "18903";
+  public static final String s18904 = "18904";
+  public static final String s18905 = "18905";
+  public static final String s18906 = "18906";
+  public static final String s18907 = "18907";
+  public static final String s18908 = "18908";
+  public static final String s18909 = "18909";
+  public static final String s18910 = "18910";
+  public static final String s18911 = "18911";
+  public static final String s18912 = "18912";
+  public static final String s18913 = "18913";
+  public static final String s18914 = "18914";
+  public static final String s18915 = "18915";
+  public static final String s18916 = "18916";
+  public static final String s18917 = "18917";
+  public static final String s18918 = "18918";
+  public static final String s18919 = "18919";
+  public static final String s18920 = "18920";
+  public static final String s18921 = "18921";
+  public static final String s18922 = "18922";
+  public static final String s18923 = "18923";
+  public static final String s18924 = "18924";
+  public static final String s18925 = "18925";
+  public static final String s18926 = "18926";
+  public static final String s18927 = "18927";
+  public static final String s18928 = "18928";
+  public static final String s18929 = "18929";
+  public static final String s18930 = "18930";
+  public static final String s18931 = "18931";
+  public static final String s18932 = "18932";
+  public static final String s18933 = "18933";
+  public static final String s18934 = "18934";
+  public static final String s18935 = "18935";
+  public static final String s18936 = "18936";
+  public static final String s18937 = "18937";
+  public static final String s18938 = "18938";
+  public static final String s18939 = "18939";
+  public static final String s18940 = "18940";
+  public static final String s18941 = "18941";
+  public static final String s18942 = "18942";
+  public static final String s18943 = "18943";
+  public static final String s18944 = "18944";
+  public static final String s18945 = "18945";
+  public static final String s18946 = "18946";
+  public static final String s18947 = "18947";
+  public static final String s18948 = "18948";
+  public static final String s18949 = "18949";
+  public static final String s18950 = "18950";
+  public static final String s18951 = "18951";
+  public static final String s18952 = "18952";
+  public static final String s18953 = "18953";
+  public static final String s18954 = "18954";
+  public static final String s18955 = "18955";
+  public static final String s18956 = "18956";
+  public static final String s18957 = "18957";
+  public static final String s18958 = "18958";
+  public static final String s18959 = "18959";
+  public static final String s18960 = "18960";
+  public static final String s18961 = "18961";
+  public static final String s18962 = "18962";
+  public static final String s18963 = "18963";
+  public static final String s18964 = "18964";
+  public static final String s18965 = "18965";
+  public static final String s18966 = "18966";
+  public static final String s18967 = "18967";
+  public static final String s18968 = "18968";
+  public static final String s18969 = "18969";
+  public static final String s18970 = "18970";
+  public static final String s18971 = "18971";
+  public static final String s18972 = "18972";
+  public static final String s18973 = "18973";
+  public static final String s18974 = "18974";
+  public static final String s18975 = "18975";
+  public static final String s18976 = "18976";
+  public static final String s18977 = "18977";
+  public static final String s18978 = "18978";
+  public static final String s18979 = "18979";
+  public static final String s18980 = "18980";
+  public static final String s18981 = "18981";
+  public static final String s18982 = "18982";
+  public static final String s18983 = "18983";
+  public static final String s18984 = "18984";
+  public static final String s18985 = "18985";
+  public static final String s18986 = "18986";
+  public static final String s18987 = "18987";
+  public static final String s18988 = "18988";
+  public static final String s18989 = "18989";
+  public static final String s18990 = "18990";
+  public static final String s18991 = "18991";
+  public static final String s18992 = "18992";
+  public static final String s18993 = "18993";
+  public static final String s18994 = "18994";
+  public static final String s18995 = "18995";
+  public static final String s18996 = "18996";
+  public static final String s18997 = "18997";
+  public static final String s18998 = "18998";
+  public static final String s18999 = "18999";
+  public static final String s19000 = "19000";
+  public static final String s19001 = "19001";
+  public static final String s19002 = "19002";
+  public static final String s19003 = "19003";
+  public static final String s19004 = "19004";
+  public static final String s19005 = "19005";
+  public static final String s19006 = "19006";
+  public static final String s19007 = "19007";
+  public static final String s19008 = "19008";
+  public static final String s19009 = "19009";
+  public static final String s19010 = "19010";
+  public static final String s19011 = "19011";
+  public static final String s19012 = "19012";
+  public static final String s19013 = "19013";
+  public static final String s19014 = "19014";
+  public static final String s19015 = "19015";
+  public static final String s19016 = "19016";
+  public static final String s19017 = "19017";
+  public static final String s19018 = "19018";
+  public static final String s19019 = "19019";
+  public static final String s19020 = "19020";
+  public static final String s19021 = "19021";
+  public static final String s19022 = "19022";
+  public static final String s19023 = "19023";
+  public static final String s19024 = "19024";
+  public static final String s19025 = "19025";
+  public static final String s19026 = "19026";
+  public static final String s19027 = "19027";
+  public static final String s19028 = "19028";
+  public static final String s19029 = "19029";
+  public static final String s19030 = "19030";
+  public static final String s19031 = "19031";
+  public static final String s19032 = "19032";
+  public static final String s19033 = "19033";
+  public static final String s19034 = "19034";
+  public static final String s19035 = "19035";
+  public static final String s19036 = "19036";
+  public static final String s19037 = "19037";
+  public static final String s19038 = "19038";
+  public static final String s19039 = "19039";
+  public static final String s19040 = "19040";
+  public static final String s19041 = "19041";
+  public static final String s19042 = "19042";
+  public static final String s19043 = "19043";
+  public static final String s19044 = "19044";
+  public static final String s19045 = "19045";
+  public static final String s19046 = "19046";
+  public static final String s19047 = "19047";
+  public static final String s19048 = "19048";
+  public static final String s19049 = "19049";
+  public static final String s19050 = "19050";
+  public static final String s19051 = "19051";
+  public static final String s19052 = "19052";
+  public static final String s19053 = "19053";
+  public static final String s19054 = "19054";
+  public static final String s19055 = "19055";
+  public static final String s19056 = "19056";
+  public static final String s19057 = "19057";
+  public static final String s19058 = "19058";
+  public static final String s19059 = "19059";
+  public static final String s19060 = "19060";
+  public static final String s19061 = "19061";
+  public static final String s19062 = "19062";
+  public static final String s19063 = "19063";
+  public static final String s19064 = "19064";
+  public static final String s19065 = "19065";
+  public static final String s19066 = "19066";
+  public static final String s19067 = "19067";
+  public static final String s19068 = "19068";
+  public static final String s19069 = "19069";
+  public static final String s19070 = "19070";
+  public static final String s19071 = "19071";
+  public static final String s19072 = "19072";
+  public static final String s19073 = "19073";
+  public static final String s19074 = "19074";
+  public static final String s19075 = "19075";
+  public static final String s19076 = "19076";
+  public static final String s19077 = "19077";
+  public static final String s19078 = "19078";
+  public static final String s19079 = "19079";
+  public static final String s19080 = "19080";
+  public static final String s19081 = "19081";
+  public static final String s19082 = "19082";
+  public static final String s19083 = "19083";
+  public static final String s19084 = "19084";
+  public static final String s19085 = "19085";
+  public static final String s19086 = "19086";
+  public static final String s19087 = "19087";
+  public static final String s19088 = "19088";
+  public static final String s19089 = "19089";
+  public static final String s19090 = "19090";
+  public static final String s19091 = "19091";
+  public static final String s19092 = "19092";
+  public static final String s19093 = "19093";
+  public static final String s19094 = "19094";
+  public static final String s19095 = "19095";
+  public static final String s19096 = "19096";
+  public static final String s19097 = "19097";
+  public static final String s19098 = "19098";
+  public static final String s19099 = "19099";
+  public static final String s19100 = "19100";
+  public static final String s19101 = "19101";
+  public static final String s19102 = "19102";
+  public static final String s19103 = "19103";
+  public static final String s19104 = "19104";
+  public static final String s19105 = "19105";
+  public static final String s19106 = "19106";
+  public static final String s19107 = "19107";
+  public static final String s19108 = "19108";
+  public static final String s19109 = "19109";
+  public static final String s19110 = "19110";
+  public static final String s19111 = "19111";
+  public static final String s19112 = "19112";
+  public static final String s19113 = "19113";
+  public static final String s19114 = "19114";
+  public static final String s19115 = "19115";
+  public static final String s19116 = "19116";
+  public static final String s19117 = "19117";
+  public static final String s19118 = "19118";
+  public static final String s19119 = "19119";
+  public static final String s19120 = "19120";
+  public static final String s19121 = "19121";
+  public static final String s19122 = "19122";
+  public static final String s19123 = "19123";
+  public static final String s19124 = "19124";
+  public static final String s19125 = "19125";
+  public static final String s19126 = "19126";
+  public static final String s19127 = "19127";
+  public static final String s19128 = "19128";
+  public static final String s19129 = "19129";
+  public static final String s19130 = "19130";
+  public static final String s19131 = "19131";
+  public static final String s19132 = "19132";
+  public static final String s19133 = "19133";
+  public static final String s19134 = "19134";
+  public static final String s19135 = "19135";
+  public static final String s19136 = "19136";
+  public static final String s19137 = "19137";
+  public static final String s19138 = "19138";
+  public static final String s19139 = "19139";
+  public static final String s19140 = "19140";
+  public static final String s19141 = "19141";
+  public static final String s19142 = "19142";
+  public static final String s19143 = "19143";
+  public static final String s19144 = "19144";
+  public static final String s19145 = "19145";
+  public static final String s19146 = "19146";
+  public static final String s19147 = "19147";
+  public static final String s19148 = "19148";
+  public static final String s19149 = "19149";
+  public static final String s19150 = "19150";
+  public static final String s19151 = "19151";
+  public static final String s19152 = "19152";
+  public static final String s19153 = "19153";
+  public static final String s19154 = "19154";
+  public static final String s19155 = "19155";
+  public static final String s19156 = "19156";
+  public static final String s19157 = "19157";
+  public static final String s19158 = "19158";
+  public static final String s19159 = "19159";
+  public static final String s19160 = "19160";
+  public static final String s19161 = "19161";
+  public static final String s19162 = "19162";
+  public static final String s19163 = "19163";
+  public static final String s19164 = "19164";
+  public static final String s19165 = "19165";
+  public static final String s19166 = "19166";
+  public static final String s19167 = "19167";
+  public static final String s19168 = "19168";
+  public static final String s19169 = "19169";
+  public static final String s19170 = "19170";
+  public static final String s19171 = "19171";
+  public static final String s19172 = "19172";
+  public static final String s19173 = "19173";
+  public static final String s19174 = "19174";
+  public static final String s19175 = "19175";
+  public static final String s19176 = "19176";
+  public static final String s19177 = "19177";
+  public static final String s19178 = "19178";
+  public static final String s19179 = "19179";
+  public static final String s19180 = "19180";
+  public static final String s19181 = "19181";
+  public static final String s19182 = "19182";
+  public static final String s19183 = "19183";
+  public static final String s19184 = "19184";
+  public static final String s19185 = "19185";
+  public static final String s19186 = "19186";
+  public static final String s19187 = "19187";
+  public static final String s19188 = "19188";
+  public static final String s19189 = "19189";
+  public static final String s19190 = "19190";
+  public static final String s19191 = "19191";
+  public static final String s19192 = "19192";
+  public static final String s19193 = "19193";
+  public static final String s19194 = "19194";
+  public static final String s19195 = "19195";
+  public static final String s19196 = "19196";
+  public static final String s19197 = "19197";
+  public static final String s19198 = "19198";
+  public static final String s19199 = "19199";
+  public static final String s19200 = "19200";
+  public static final String s19201 = "19201";
+  public static final String s19202 = "19202";
+  public static final String s19203 = "19203";
+  public static final String s19204 = "19204";
+  public static final String s19205 = "19205";
+  public static final String s19206 = "19206";
+  public static final String s19207 = "19207";
+  public static final String s19208 = "19208";
+  public static final String s19209 = "19209";
+  public static final String s19210 = "19210";
+  public static final String s19211 = "19211";
+  public static final String s19212 = "19212";
+  public static final String s19213 = "19213";
+  public static final String s19214 = "19214";
+  public static final String s19215 = "19215";
+  public static final String s19216 = "19216";
+  public static final String s19217 = "19217";
+  public static final String s19218 = "19218";
+  public static final String s19219 = "19219";
+  public static final String s19220 = "19220";
+  public static final String s19221 = "19221";
+  public static final String s19222 = "19222";
+  public static final String s19223 = "19223";
+  public static final String s19224 = "19224";
+  public static final String s19225 = "19225";
+  public static final String s19226 = "19226";
+  public static final String s19227 = "19227";
+  public static final String s19228 = "19228";
+  public static final String s19229 = "19229";
+  public static final String s19230 = "19230";
+  public static final String s19231 = "19231";
+  public static final String s19232 = "19232";
+  public static final String s19233 = "19233";
+  public static final String s19234 = "19234";
+  public static final String s19235 = "19235";
+  public static final String s19236 = "19236";
+  public static final String s19237 = "19237";
+  public static final String s19238 = "19238";
+  public static final String s19239 = "19239";
+  public static final String s19240 = "19240";
+  public static final String s19241 = "19241";
+  public static final String s19242 = "19242";
+  public static final String s19243 = "19243";
+  public static final String s19244 = "19244";
+  public static final String s19245 = "19245";
+  public static final String s19246 = "19246";
+  public static final String s19247 = "19247";
+  public static final String s19248 = "19248";
+  public static final String s19249 = "19249";
+  public static final String s19250 = "19250";
+  public static final String s19251 = "19251";
+  public static final String s19252 = "19252";
+  public static final String s19253 = "19253";
+  public static final String s19254 = "19254";
+  public static final String s19255 = "19255";
+  public static final String s19256 = "19256";
+  public static final String s19257 = "19257";
+  public static final String s19258 = "19258";
+  public static final String s19259 = "19259";
+  public static final String s19260 = "19260";
+  public static final String s19261 = "19261";
+  public static final String s19262 = "19262";
+  public static final String s19263 = "19263";
+  public static final String s19264 = "19264";
+  public static final String s19265 = "19265";
+  public static final String s19266 = "19266";
+  public static final String s19267 = "19267";
+  public static final String s19268 = "19268";
+  public static final String s19269 = "19269";
+  public static final String s19270 = "19270";
+  public static final String s19271 = "19271";
+  public static final String s19272 = "19272";
+  public static final String s19273 = "19273";
+  public static final String s19274 = "19274";
+  public static final String s19275 = "19275";
+  public static final String s19276 = "19276";
+  public static final String s19277 = "19277";
+  public static final String s19278 = "19278";
+  public static final String s19279 = "19279";
+  public static final String s19280 = "19280";
+  public static final String s19281 = "19281";
+  public static final String s19282 = "19282";
+  public static final String s19283 = "19283";
+  public static final String s19284 = "19284";
+  public static final String s19285 = "19285";
+  public static final String s19286 = "19286";
+  public static final String s19287 = "19287";
+  public static final String s19288 = "19288";
+  public static final String s19289 = "19289";
+  public static final String s19290 = "19290";
+  public static final String s19291 = "19291";
+  public static final String s19292 = "19292";
+  public static final String s19293 = "19293";
+  public static final String s19294 = "19294";
+  public static final String s19295 = "19295";
+  public static final String s19296 = "19296";
+  public static final String s19297 = "19297";
+  public static final String s19298 = "19298";
+  public static final String s19299 = "19299";
+  public static final String s19300 = "19300";
+  public static final String s19301 = "19301";
+  public static final String s19302 = "19302";
+  public static final String s19303 = "19303";
+  public static final String s19304 = "19304";
+  public static final String s19305 = "19305";
+  public static final String s19306 = "19306";
+  public static final String s19307 = "19307";
+  public static final String s19308 = "19308";
+  public static final String s19309 = "19309";
+  public static final String s19310 = "19310";
+  public static final String s19311 = "19311";
+  public static final String s19312 = "19312";
+  public static final String s19313 = "19313";
+  public static final String s19314 = "19314";
+  public static final String s19315 = "19315";
+  public static final String s19316 = "19316";
+  public static final String s19317 = "19317";
+  public static final String s19318 = "19318";
+  public static final String s19319 = "19319";
+  public static final String s19320 = "19320";
+  public static final String s19321 = "19321";
+  public static final String s19322 = "19322";
+  public static final String s19323 = "19323";
+  public static final String s19324 = "19324";
+  public static final String s19325 = "19325";
+  public static final String s19326 = "19326";
+  public static final String s19327 = "19327";
+  public static final String s19328 = "19328";
+  public static final String s19329 = "19329";
+  public static final String s19330 = "19330";
+  public static final String s19331 = "19331";
+  public static final String s19332 = "19332";
+  public static final String s19333 = "19333";
+  public static final String s19334 = "19334";
+  public static final String s19335 = "19335";
+  public static final String s19336 = "19336";
+  public static final String s19337 = "19337";
+  public static final String s19338 = "19338";
+  public static final String s19339 = "19339";
+  public static final String s19340 = "19340";
+  public static final String s19341 = "19341";
+  public static final String s19342 = "19342";
+  public static final String s19343 = "19343";
+  public static final String s19344 = "19344";
+  public static final String s19345 = "19345";
+  public static final String s19346 = "19346";
+  public static final String s19347 = "19347";
+  public static final String s19348 = "19348";
+  public static final String s19349 = "19349";
+  public static final String s19350 = "19350";
+  public static final String s19351 = "19351";
+  public static final String s19352 = "19352";
+  public static final String s19353 = "19353";
+  public static final String s19354 = "19354";
+  public static final String s19355 = "19355";
+  public static final String s19356 = "19356";
+  public static final String s19357 = "19357";
+  public static final String s19358 = "19358";
+  public static final String s19359 = "19359";
+  public static final String s19360 = "19360";
+  public static final String s19361 = "19361";
+  public static final String s19362 = "19362";
+  public static final String s19363 = "19363";
+  public static final String s19364 = "19364";
+  public static final String s19365 = "19365";
+  public static final String s19366 = "19366";
+  public static final String s19367 = "19367";
+  public static final String s19368 = "19368";
+  public static final String s19369 = "19369";
+  public static final String s19370 = "19370";
+  public static final String s19371 = "19371";
+  public static final String s19372 = "19372";
+  public static final String s19373 = "19373";
+  public static final String s19374 = "19374";
+  public static final String s19375 = "19375";
+  public static final String s19376 = "19376";
+  public static final String s19377 = "19377";
+  public static final String s19378 = "19378";
+  public static final String s19379 = "19379";
+  public static final String s19380 = "19380";
+  public static final String s19381 = "19381";
+  public static final String s19382 = "19382";
+  public static final String s19383 = "19383";
+  public static final String s19384 = "19384";
+  public static final String s19385 = "19385";
+  public static final String s19386 = "19386";
+  public static final String s19387 = "19387";
+  public static final String s19388 = "19388";
+  public static final String s19389 = "19389";
+  public static final String s19390 = "19390";
+  public static final String s19391 = "19391";
+  public static final String s19392 = "19392";
+  public static final String s19393 = "19393";
+  public static final String s19394 = "19394";
+  public static final String s19395 = "19395";
+  public static final String s19396 = "19396";
+  public static final String s19397 = "19397";
+  public static final String s19398 = "19398";
+  public static final String s19399 = "19399";
+  public static final String s19400 = "19400";
+  public static final String s19401 = "19401";
+  public static final String s19402 = "19402";
+  public static final String s19403 = "19403";
+  public static final String s19404 = "19404";
+  public static final String s19405 = "19405";
+  public static final String s19406 = "19406";
+  public static final String s19407 = "19407";
+  public static final String s19408 = "19408";
+  public static final String s19409 = "19409";
+  public static final String s19410 = "19410";
+  public static final String s19411 = "19411";
+  public static final String s19412 = "19412";
+  public static final String s19413 = "19413";
+  public static final String s19414 = "19414";
+  public static final String s19415 = "19415";
+  public static final String s19416 = "19416";
+  public static final String s19417 = "19417";
+  public static final String s19418 = "19418";
+  public static final String s19419 = "19419";
+  public static final String s19420 = "19420";
+  public static final String s19421 = "19421";
+  public static final String s19422 = "19422";
+  public static final String s19423 = "19423";
+  public static final String s19424 = "19424";
+  public static final String s19425 = "19425";
+  public static final String s19426 = "19426";
+  public static final String s19427 = "19427";
+  public static final String s19428 = "19428";
+  public static final String s19429 = "19429";
+  public static final String s19430 = "19430";
+  public static final String s19431 = "19431";
+  public static final String s19432 = "19432";
+  public static final String s19433 = "19433";
+  public static final String s19434 = "19434";
+  public static final String s19435 = "19435";
+  public static final String s19436 = "19436";
+  public static final String s19437 = "19437";
+  public static final String s19438 = "19438";
+  public static final String s19439 = "19439";
+  public static final String s19440 = "19440";
+  public static final String s19441 = "19441";
+  public static final String s19442 = "19442";
+  public static final String s19443 = "19443";
+  public static final String s19444 = "19444";
+  public static final String s19445 = "19445";
+  public static final String s19446 = "19446";
+  public static final String s19447 = "19447";
+  public static final String s19448 = "19448";
+  public static final String s19449 = "19449";
+  public static final String s19450 = "19450";
+  public static final String s19451 = "19451";
+  public static final String s19452 = "19452";
+  public static final String s19453 = "19453";
+  public static final String s19454 = "19454";
+  public static final String s19455 = "19455";
+  public static final String s19456 = "19456";
+  public static final String s19457 = "19457";
+  public static final String s19458 = "19458";
+  public static final String s19459 = "19459";
+  public static final String s19460 = "19460";
+  public static final String s19461 = "19461";
+  public static final String s19462 = "19462";
+  public static final String s19463 = "19463";
+  public static final String s19464 = "19464";
+  public static final String s19465 = "19465";
+  public static final String s19466 = "19466";
+  public static final String s19467 = "19467";
+  public static final String s19468 = "19468";
+  public static final String s19469 = "19469";
+  public static final String s19470 = "19470";
+  public static final String s19471 = "19471";
+  public static final String s19472 = "19472";
+  public static final String s19473 = "19473";
+  public static final String s19474 = "19474";
+  public static final String s19475 = "19475";
+  public static final String s19476 = "19476";
+  public static final String s19477 = "19477";
+  public static final String s19478 = "19478";
+  public static final String s19479 = "19479";
+  public static final String s19480 = "19480";
+  public static final String s19481 = "19481";
+  public static final String s19482 = "19482";
+  public static final String s19483 = "19483";
+  public static final String s19484 = "19484";
+  public static final String s19485 = "19485";
+  public static final String s19486 = "19486";
+  public static final String s19487 = "19487";
+  public static final String s19488 = "19488";
+  public static final String s19489 = "19489";
+  public static final String s19490 = "19490";
+  public static final String s19491 = "19491";
+  public static final String s19492 = "19492";
+  public static final String s19493 = "19493";
+  public static final String s19494 = "19494";
+  public static final String s19495 = "19495";
+  public static final String s19496 = "19496";
+  public static final String s19497 = "19497";
+  public static final String s19498 = "19498";
+  public static final String s19499 = "19499";
+  public static final String s19500 = "19500";
+  public static final String s19501 = "19501";
+  public static final String s19502 = "19502";
+  public static final String s19503 = "19503";
+  public static final String s19504 = "19504";
+  public static final String s19505 = "19505";
+  public static final String s19506 = "19506";
+  public static final String s19507 = "19507";
+  public static final String s19508 = "19508";
+  public static final String s19509 = "19509";
+  public static final String s19510 = "19510";
+  public static final String s19511 = "19511";
+  public static final String s19512 = "19512";
+  public static final String s19513 = "19513";
+  public static final String s19514 = "19514";
+  public static final String s19515 = "19515";
+  public static final String s19516 = "19516";
+  public static final String s19517 = "19517";
+  public static final String s19518 = "19518";
+  public static final String s19519 = "19519";
+  public static final String s19520 = "19520";
+  public static final String s19521 = "19521";
+  public static final String s19522 = "19522";
+  public static final String s19523 = "19523";
+  public static final String s19524 = "19524";
+  public static final String s19525 = "19525";
+  public static final String s19526 = "19526";
+  public static final String s19527 = "19527";
+  public static final String s19528 = "19528";
+  public static final String s19529 = "19529";
+  public static final String s19530 = "19530";
+  public static final String s19531 = "19531";
+  public static final String s19532 = "19532";
+  public static final String s19533 = "19533";
+  public static final String s19534 = "19534";
+  public static final String s19535 = "19535";
+  public static final String s19536 = "19536";
+  public static final String s19537 = "19537";
+  public static final String s19538 = "19538";
+  public static final String s19539 = "19539";
+  public static final String s19540 = "19540";
+  public static final String s19541 = "19541";
+  public static final String s19542 = "19542";
+  public static final String s19543 = "19543";
+  public static final String s19544 = "19544";
+  public static final String s19545 = "19545";
+  public static final String s19546 = "19546";
+  public static final String s19547 = "19547";
+  public static final String s19548 = "19548";
+  public static final String s19549 = "19549";
+  public static final String s19550 = "19550";
+  public static final String s19551 = "19551";
+  public static final String s19552 = "19552";
+  public static final String s19553 = "19553";
+  public static final String s19554 = "19554";
+  public static final String s19555 = "19555";
+  public static final String s19556 = "19556";
+  public static final String s19557 = "19557";
+  public static final String s19558 = "19558";
+  public static final String s19559 = "19559";
+  public static final String s19560 = "19560";
+  public static final String s19561 = "19561";
+  public static final String s19562 = "19562";
+  public static final String s19563 = "19563";
+  public static final String s19564 = "19564";
+  public static final String s19565 = "19565";
+  public static final String s19566 = "19566";
+  public static final String s19567 = "19567";
+  public static final String s19568 = "19568";
+  public static final String s19569 = "19569";
+  public static final String s19570 = "19570";
+  public static final String s19571 = "19571";
+  public static final String s19572 = "19572";
+  public static final String s19573 = "19573";
+  public static final String s19574 = "19574";
+  public static final String s19575 = "19575";
+  public static final String s19576 = "19576";
+  public static final String s19577 = "19577";
+  public static final String s19578 = "19578";
+  public static final String s19579 = "19579";
+  public static final String s19580 = "19580";
+  public static final String s19581 = "19581";
+  public static final String s19582 = "19582";
+  public static final String s19583 = "19583";
+  public static final String s19584 = "19584";
+  public static final String s19585 = "19585";
+  public static final String s19586 = "19586";
+  public static final String s19587 = "19587";
+  public static final String s19588 = "19588";
+  public static final String s19589 = "19589";
+  public static final String s19590 = "19590";
+  public static final String s19591 = "19591";
+  public static final String s19592 = "19592";
+  public static final String s19593 = "19593";
+  public static final String s19594 = "19594";
+  public static final String s19595 = "19595";
+  public static final String s19596 = "19596";
+  public static final String s19597 = "19597";
+  public static final String s19598 = "19598";
+  public static final String s19599 = "19599";
+  public static final String s19600 = "19600";
+  public static final String s19601 = "19601";
+  public static final String s19602 = "19602";
+  public static final String s19603 = "19603";
+  public static final String s19604 = "19604";
+  public static final String s19605 = "19605";
+  public static final String s19606 = "19606";
+  public static final String s19607 = "19607";
+  public static final String s19608 = "19608";
+  public static final String s19609 = "19609";
+  public static final String s19610 = "19610";
+  public static final String s19611 = "19611";
+  public static final String s19612 = "19612";
+  public static final String s19613 = "19613";
+  public static final String s19614 = "19614";
+  public static final String s19615 = "19615";
+  public static final String s19616 = "19616";
+  public static final String s19617 = "19617";
+  public static final String s19618 = "19618";
+  public static final String s19619 = "19619";
+  public static final String s19620 = "19620";
+  public static final String s19621 = "19621";
+  public static final String s19622 = "19622";
+  public static final String s19623 = "19623";
+  public static final String s19624 = "19624";
+  public static final String s19625 = "19625";
+  public static final String s19626 = "19626";
+  public static final String s19627 = "19627";
+  public static final String s19628 = "19628";
+  public static final String s19629 = "19629";
+  public static final String s19630 = "19630";
+  public static final String s19631 = "19631";
+  public static final String s19632 = "19632";
+  public static final String s19633 = "19633";
+  public static final String s19634 = "19634";
+  public static final String s19635 = "19635";
+  public static final String s19636 = "19636";
+  public static final String s19637 = "19637";
+  public static final String s19638 = "19638";
+  public static final String s19639 = "19639";
+  public static final String s19640 = "19640";
+  public static final String s19641 = "19641";
+  public static final String s19642 = "19642";
+  public static final String s19643 = "19643";
+  public static final String s19644 = "19644";
+  public static final String s19645 = "19645";
+  public static final String s19646 = "19646";
+  public static final String s19647 = "19647";
+  public static final String s19648 = "19648";
+  public static final String s19649 = "19649";
+  public static final String s19650 = "19650";
+  public static final String s19651 = "19651";
+  public static final String s19652 = "19652";
+  public static final String s19653 = "19653";
+  public static final String s19654 = "19654";
+  public static final String s19655 = "19655";
+  public static final String s19656 = "19656";
+  public static final String s19657 = "19657";
+  public static final String s19658 = "19658";
+  public static final String s19659 = "19659";
+  public static final String s19660 = "19660";
+  public static final String s19661 = "19661";
+  public static final String s19662 = "19662";
+  public static final String s19663 = "19663";
+  public static final String s19664 = "19664";
+  public static final String s19665 = "19665";
+  public static final String s19666 = "19666";
+  public static final String s19667 = "19667";
+  public static final String s19668 = "19668";
+  public static final String s19669 = "19669";
+  public static final String s19670 = "19670";
+  public static final String s19671 = "19671";
+  public static final String s19672 = "19672";
+  public static final String s19673 = "19673";
+  public static final String s19674 = "19674";
+  public static final String s19675 = "19675";
+  public static final String s19676 = "19676";
+  public static final String s19677 = "19677";
+  public static final String s19678 = "19678";
+  public static final String s19679 = "19679";
+  public static final String s19680 = "19680";
+  public static final String s19681 = "19681";
+  public static final String s19682 = "19682";
+  public static final String s19683 = "19683";
+  public static final String s19684 = "19684";
+  public static final String s19685 = "19685";
+  public static final String s19686 = "19686";
+  public static final String s19687 = "19687";
+  public static final String s19688 = "19688";
+  public static final String s19689 = "19689";
+  public static final String s19690 = "19690";
+  public static final String s19691 = "19691";
+  public static final String s19692 = "19692";
+  public static final String s19693 = "19693";
+  public static final String s19694 = "19694";
+  public static final String s19695 = "19695";
+  public static final String s19696 = "19696";
+  public static final String s19697 = "19697";
+  public static final String s19698 = "19698";
+  public static final String s19699 = "19699";
+  public static final String s19700 = "19700";
+  public static final String s19701 = "19701";
+  public static final String s19702 = "19702";
+  public static final String s19703 = "19703";
+  public static final String s19704 = "19704";
+  public static final String s19705 = "19705";
+  public static final String s19706 = "19706";
+  public static final String s19707 = "19707";
+  public static final String s19708 = "19708";
+  public static final String s19709 = "19709";
+  public static final String s19710 = "19710";
+  public static final String s19711 = "19711";
+  public static final String s19712 = "19712";
+  public static final String s19713 = "19713";
+  public static final String s19714 = "19714";
+  public static final String s19715 = "19715";
+  public static final String s19716 = "19716";
+  public static final String s19717 = "19717";
+  public static final String s19718 = "19718";
+  public static final String s19719 = "19719";
+  public static final String s19720 = "19720";
+  public static final String s19721 = "19721";
+  public static final String s19722 = "19722";
+  public static final String s19723 = "19723";
+  public static final String s19724 = "19724";
+  public static final String s19725 = "19725";
+  public static final String s19726 = "19726";
+  public static final String s19727 = "19727";
+  public static final String s19728 = "19728";
+  public static final String s19729 = "19729";
+  public static final String s19730 = "19730";
+  public static final String s19731 = "19731";
+  public static final String s19732 = "19732";
+  public static final String s19733 = "19733";
+  public static final String s19734 = "19734";
+  public static final String s19735 = "19735";
+  public static final String s19736 = "19736";
+  public static final String s19737 = "19737";
+  public static final String s19738 = "19738";
+  public static final String s19739 = "19739";
+  public static final String s19740 = "19740";
+  public static final String s19741 = "19741";
+  public static final String s19742 = "19742";
+  public static final String s19743 = "19743";
+  public static final String s19744 = "19744";
+  public static final String s19745 = "19745";
+  public static final String s19746 = "19746";
+  public static final String s19747 = "19747";
+  public static final String s19748 = "19748";
+  public static final String s19749 = "19749";
+  public static final String s19750 = "19750";
+  public static final String s19751 = "19751";
+  public static final String s19752 = "19752";
+  public static final String s19753 = "19753";
+  public static final String s19754 = "19754";
+  public static final String s19755 = "19755";
+  public static final String s19756 = "19756";
+  public static final String s19757 = "19757";
+  public static final String s19758 = "19758";
+  public static final String s19759 = "19759";
+  public static final String s19760 = "19760";
+  public static final String s19761 = "19761";
+  public static final String s19762 = "19762";
+  public static final String s19763 = "19763";
+  public static final String s19764 = "19764";
+  public static final String s19765 = "19765";
+  public static final String s19766 = "19766";
+  public static final String s19767 = "19767";
+  public static final String s19768 = "19768";
+  public static final String s19769 = "19769";
+  public static final String s19770 = "19770";
+  public static final String s19771 = "19771";
+  public static final String s19772 = "19772";
+  public static final String s19773 = "19773";
+  public static final String s19774 = "19774";
+  public static final String s19775 = "19775";
+  public static final String s19776 = "19776";
+  public static final String s19777 = "19777";
+  public static final String s19778 = "19778";
+  public static final String s19779 = "19779";
+  public static final String s19780 = "19780";
+  public static final String s19781 = "19781";
+  public static final String s19782 = "19782";
+  public static final String s19783 = "19783";
+  public static final String s19784 = "19784";
+  public static final String s19785 = "19785";
+  public static final String s19786 = "19786";
+  public static final String s19787 = "19787";
+  public static final String s19788 = "19788";
+  public static final String s19789 = "19789";
+  public static final String s19790 = "19790";
+  public static final String s19791 = "19791";
+  public static final String s19792 = "19792";
+  public static final String s19793 = "19793";
+  public static final String s19794 = "19794";
+  public static final String s19795 = "19795";
+  public static final String s19796 = "19796";
+  public static final String s19797 = "19797";
+  public static final String s19798 = "19798";
+  public static final String s19799 = "19799";
+  public static final String s19800 = "19800";
+  public static final String s19801 = "19801";
+  public static final String s19802 = "19802";
+  public static final String s19803 = "19803";
+  public static final String s19804 = "19804";
+  public static final String s19805 = "19805";
+  public static final String s19806 = "19806";
+  public static final String s19807 = "19807";
+  public static final String s19808 = "19808";
+  public static final String s19809 = "19809";
+  public static final String s19810 = "19810";
+  public static final String s19811 = "19811";
+  public static final String s19812 = "19812";
+  public static final String s19813 = "19813";
+  public static final String s19814 = "19814";
+  public static final String s19815 = "19815";
+  public static final String s19816 = "19816";
+  public static final String s19817 = "19817";
+  public static final String s19818 = "19818";
+  public static final String s19819 = "19819";
+  public static final String s19820 = "19820";
+  public static final String s19821 = "19821";
+  public static final String s19822 = "19822";
+  public static final String s19823 = "19823";
+  public static final String s19824 = "19824";
+  public static final String s19825 = "19825";
+  public static final String s19826 = "19826";
+  public static final String s19827 = "19827";
+  public static final String s19828 = "19828";
+  public static final String s19829 = "19829";
+  public static final String s19830 = "19830";
+  public static final String s19831 = "19831";
+  public static final String s19832 = "19832";
+  public static final String s19833 = "19833";
+  public static final String s19834 = "19834";
+  public static final String s19835 = "19835";
+  public static final String s19836 = "19836";
+  public static final String s19837 = "19837";
+  public static final String s19838 = "19838";
+  public static final String s19839 = "19839";
+  public static final String s19840 = "19840";
+  public static final String s19841 = "19841";
+  public static final String s19842 = "19842";
+  public static final String s19843 = "19843";
+  public static final String s19844 = "19844";
+  public static final String s19845 = "19845";
+  public static final String s19846 = "19846";
+  public static final String s19847 = "19847";
+  public static final String s19848 = "19848";
+  public static final String s19849 = "19849";
+  public static final String s19850 = "19850";
+  public static final String s19851 = "19851";
+  public static final String s19852 = "19852";
+  public static final String s19853 = "19853";
+  public static final String s19854 = "19854";
+  public static final String s19855 = "19855";
+  public static final String s19856 = "19856";
+  public static final String s19857 = "19857";
+  public static final String s19858 = "19858";
+  public static final String s19859 = "19859";
+  public static final String s19860 = "19860";
+  public static final String s19861 = "19861";
+  public static final String s19862 = "19862";
+  public static final String s19863 = "19863";
+  public static final String s19864 = "19864";
+  public static final String s19865 = "19865";
+  public static final String s19866 = "19866";
+  public static final String s19867 = "19867";
+  public static final String s19868 = "19868";
+  public static final String s19869 = "19869";
+  public static final String s19870 = "19870";
+  public static final String s19871 = "19871";
+  public static final String s19872 = "19872";
+  public static final String s19873 = "19873";
+  public static final String s19874 = "19874";
+  public static final String s19875 = "19875";
+  public static final String s19876 = "19876";
+  public static final String s19877 = "19877";
+  public static final String s19878 = "19878";
+  public static final String s19879 = "19879";
+  public static final String s19880 = "19880";
+  public static final String s19881 = "19881";
+  public static final String s19882 = "19882";
+  public static final String s19883 = "19883";
+  public static final String s19884 = "19884";
+  public static final String s19885 = "19885";
+  public static final String s19886 = "19886";
+  public static final String s19887 = "19887";
+  public static final String s19888 = "19888";
+  public static final String s19889 = "19889";
+  public static final String s19890 = "19890";
+  public static final String s19891 = "19891";
+  public static final String s19892 = "19892";
+  public static final String s19893 = "19893";
+  public static final String s19894 = "19894";
+  public static final String s19895 = "19895";
+  public static final String s19896 = "19896";
+  public static final String s19897 = "19897";
+  public static final String s19898 = "19898";
+  public static final String s19899 = "19899";
+  public static final String s19900 = "19900";
+  public static final String s19901 = "19901";
+  public static final String s19902 = "19902";
+  public static final String s19903 = "19903";
+  public static final String s19904 = "19904";
+  public static final String s19905 = "19905";
+  public static final String s19906 = "19906";
+  public static final String s19907 = "19907";
+  public static final String s19908 = "19908";
+  public static final String s19909 = "19909";
+  public static final String s19910 = "19910";
+  public static final String s19911 = "19911";
+  public static final String s19912 = "19912";
+  public static final String s19913 = "19913";
+  public static final String s19914 = "19914";
+  public static final String s19915 = "19915";
+  public static final String s19916 = "19916";
+  public static final String s19917 = "19917";
+  public static final String s19918 = "19918";
+  public static final String s19919 = "19919";
+  public static final String s19920 = "19920";
+  public static final String s19921 = "19921";
+  public static final String s19922 = "19922";
+  public static final String s19923 = "19923";
+  public static final String s19924 = "19924";
+  public static final String s19925 = "19925";
+  public static final String s19926 = "19926";
+  public static final String s19927 = "19927";
+  public static final String s19928 = "19928";
+  public static final String s19929 = "19929";
+  public static final String s19930 = "19930";
+  public static final String s19931 = "19931";
+  public static final String s19932 = "19932";
+  public static final String s19933 = "19933";
+  public static final String s19934 = "19934";
+  public static final String s19935 = "19935";
+  public static final String s19936 = "19936";
+  public static final String s19937 = "19937";
+  public static final String s19938 = "19938";
+  public static final String s19939 = "19939";
+  public static final String s19940 = "19940";
+  public static final String s19941 = "19941";
+  public static final String s19942 = "19942";
+  public static final String s19943 = "19943";
+  public static final String s19944 = "19944";
+  public static final String s19945 = "19945";
+  public static final String s19946 = "19946";
+  public static final String s19947 = "19947";
+  public static final String s19948 = "19948";
+  public static final String s19949 = "19949";
+  public static final String s19950 = "19950";
+  public static final String s19951 = "19951";
+  public static final String s19952 = "19952";
+  public static final String s19953 = "19953";
+  public static final String s19954 = "19954";
+  public static final String s19955 = "19955";
+  public static final String s19956 = "19956";
+  public static final String s19957 = "19957";
+  public static final String s19958 = "19958";
+  public static final String s19959 = "19959";
+  public static final String s19960 = "19960";
+  public static final String s19961 = "19961";
+  public static final String s19962 = "19962";
+  public static final String s19963 = "19963";
+  public static final String s19964 = "19964";
+  public static final String s19965 = "19965";
+  public static final String s19966 = "19966";
+  public static final String s19967 = "19967";
+  public static final String s19968 = "19968";
+  public static final String s19969 = "19969";
+  public static final String s19970 = "19970";
+  public static final String s19971 = "19971";
+  public static final String s19972 = "19972";
+  public static final String s19973 = "19973";
+  public static final String s19974 = "19974";
+  public static final String s19975 = "19975";
+  public static final String s19976 = "19976";
+  public static final String s19977 = "19977";
+  public static final String s19978 = "19978";
+  public static final String s19979 = "19979";
+  public static final String s19980 = "19980";
+  public static final String s19981 = "19981";
+  public static final String s19982 = "19982";
+  public static final String s19983 = "19983";
+  public static final String s19984 = "19984";
+  public static final String s19985 = "19985";
+  public static final String s19986 = "19986";
+  public static final String s19987 = "19987";
+  public static final String s19988 = "19988";
+  public static final String s19989 = "19989";
+  public static final String s19990 = "19990";
+  public static final String s19991 = "19991";
+  public static final String s19992 = "19992";
+  public static final String s19993 = "19993";
+  public static final String s19994 = "19994";
+  public static final String s19995 = "19995";
+  public static final String s19996 = "19996";
+  public static final String s19997 = "19997";
+  public static final String s19998 = "19998";
+  public static final String s19999 = "19999";
+  public static final String s20000 = "20000";
+  public static final String s20001 = "20001";
+  public static final String s20002 = "20002";
+  public static final String s20003 = "20003";
+  public static final String s20004 = "20004";
+  public static final String s20005 = "20005";
+  public static final String s20006 = "20006";
+  public static final String s20007 = "20007";
+  public static final String s20008 = "20008";
+  public static final String s20009 = "20009";
+  public static final String s20010 = "20010";
+  public static final String s20011 = "20011";
+  public static final String s20012 = "20012";
+  public static final String s20013 = "20013";
+  public static final String s20014 = "20014";
+  public static final String s20015 = "20015";
+  public static final String s20016 = "20016";
+  public static final String s20017 = "20017";
+  public static final String s20018 = "20018";
+  public static final String s20019 = "20019";
+  public static final String s20020 = "20020";
+  public static final String s20021 = "20021";
+  public static final String s20022 = "20022";
+  public static final String s20023 = "20023";
+  public static final String s20024 = "20024";
+  public static final String s20025 = "20025";
+  public static final String s20026 = "20026";
+  public static final String s20027 = "20027";
+  public static final String s20028 = "20028";
+  public static final String s20029 = "20029";
+  public static final String s20030 = "20030";
+  public static final String s20031 = "20031";
+  public static final String s20032 = "20032";
+  public static final String s20033 = "20033";
+  public static final String s20034 = "20034";
+  public static final String s20035 = "20035";
+  public static final String s20036 = "20036";
+  public static final String s20037 = "20037";
+  public static final String s20038 = "20038";
+  public static final String s20039 = "20039";
+  public static final String s20040 = "20040";
+  public static final String s20041 = "20041";
+  public static final String s20042 = "20042";
+  public static final String s20043 = "20043";
+  public static final String s20044 = "20044";
+  public static final String s20045 = "20045";
+  public static final String s20046 = "20046";
+  public static final String s20047 = "20047";
+  public static final String s20048 = "20048";
+  public static final String s20049 = "20049";
+  public static final String s20050 = "20050";
+  public static final String s20051 = "20051";
+  public static final String s20052 = "20052";
+  public static final String s20053 = "20053";
+  public static final String s20054 = "20054";
+  public static final String s20055 = "20055";
+  public static final String s20056 = "20056";
+  public static final String s20057 = "20057";
+  public static final String s20058 = "20058";
+  public static final String s20059 = "20059";
+  public static final String s20060 = "20060";
+  public static final String s20061 = "20061";
+  public static final String s20062 = "20062";
+  public static final String s20063 = "20063";
+  public static final String s20064 = "20064";
+  public static final String s20065 = "20065";
+  public static final String s20066 = "20066";
+  public static final String s20067 = "20067";
+  public static final String s20068 = "20068";
+  public static final String s20069 = "20069";
+  public static final String s20070 = "20070";
+  public static final String s20071 = "20071";
+  public static final String s20072 = "20072";
+  public static final String s20073 = "20073";
+  public static final String s20074 = "20074";
+  public static final String s20075 = "20075";
+  public static final String s20076 = "20076";
+  public static final String s20077 = "20077";
+  public static final String s20078 = "20078";
+  public static final String s20079 = "20079";
+  public static final String s20080 = "20080";
+  public static final String s20081 = "20081";
+  public static final String s20082 = "20082";
+  public static final String s20083 = "20083";
+  public static final String s20084 = "20084";
+  public static final String s20085 = "20085";
+  public static final String s20086 = "20086";
+  public static final String s20087 = "20087";
+  public static final String s20088 = "20088";
+  public static final String s20089 = "20089";
+  public static final String s20090 = "20090";
+  public static final String s20091 = "20091";
+  public static final String s20092 = "20092";
+  public static final String s20093 = "20093";
+  public static final String s20094 = "20094";
+  public static final String s20095 = "20095";
+  public static final String s20096 = "20096";
+  public static final String s20097 = "20097";
+  public static final String s20098 = "20098";
+  public static final String s20099 = "20099";
+  public static final String s20100 = "20100";
+  public static final String s20101 = "20101";
+  public static final String s20102 = "20102";
+  public static final String s20103 = "20103";
+  public static final String s20104 = "20104";
+  public static final String s20105 = "20105";
+  public static final String s20106 = "20106";
+  public static final String s20107 = "20107";
+  public static final String s20108 = "20108";
+  public static final String s20109 = "20109";
+  public static final String s20110 = "20110";
+  public static final String s20111 = "20111";
+  public static final String s20112 = "20112";
+  public static final String s20113 = "20113";
+  public static final String s20114 = "20114";
+  public static final String s20115 = "20115";
+  public static final String s20116 = "20116";
+  public static final String s20117 = "20117";
+  public static final String s20118 = "20118";
+  public static final String s20119 = "20119";
+  public static final String s20120 = "20120";
+  public static final String s20121 = "20121";
+  public static final String s20122 = "20122";
+  public static final String s20123 = "20123";
+  public static final String s20124 = "20124";
+  public static final String s20125 = "20125";
+  public static final String s20126 = "20126";
+  public static final String s20127 = "20127";
+  public static final String s20128 = "20128";
+  public static final String s20129 = "20129";
+  public static final String s20130 = "20130";
+  public static final String s20131 = "20131";
+  public static final String s20132 = "20132";
+  public static final String s20133 = "20133";
+  public static final String s20134 = "20134";
+  public static final String s20135 = "20135";
+  public static final String s20136 = "20136";
+  public static final String s20137 = "20137";
+  public static final String s20138 = "20138";
+  public static final String s20139 = "20139";
+  public static final String s20140 = "20140";
+  public static final String s20141 = "20141";
+  public static final String s20142 = "20142";
+  public static final String s20143 = "20143";
+  public static final String s20144 = "20144";
+  public static final String s20145 = "20145";
+  public static final String s20146 = "20146";
+  public static final String s20147 = "20147";
+  public static final String s20148 = "20148";
+  public static final String s20149 = "20149";
+  public static final String s20150 = "20150";
+  public static final String s20151 = "20151";
+  public static final String s20152 = "20152";
+  public static final String s20153 = "20153";
+  public static final String s20154 = "20154";
+  public static final String s20155 = "20155";
+  public static final String s20156 = "20156";
+  public static final String s20157 = "20157";
+  public static final String s20158 = "20158";
+  public static final String s20159 = "20159";
+  public static final String s20160 = "20160";
+  public static final String s20161 = "20161";
+  public static final String s20162 = "20162";
+  public static final String s20163 = "20163";
+  public static final String s20164 = "20164";
+  public static final String s20165 = "20165";
+  public static final String s20166 = "20166";
+  public static final String s20167 = "20167";
+  public static final String s20168 = "20168";
+  public static final String s20169 = "20169";
+  public static final String s20170 = "20170";
+  public static final String s20171 = "20171";
+  public static final String s20172 = "20172";
+  public static final String s20173 = "20173";
+  public static final String s20174 = "20174";
+  public static final String s20175 = "20175";
+  public static final String s20176 = "20176";
+  public static final String s20177 = "20177";
+  public static final String s20178 = "20178";
+  public static final String s20179 = "20179";
+  public static final String s20180 = "20180";
+  public static final String s20181 = "20181";
+  public static final String s20182 = "20182";
+  public static final String s20183 = "20183";
+  public static final String s20184 = "20184";
+  public static final String s20185 = "20185";
+  public static final String s20186 = "20186";
+  public static final String s20187 = "20187";
+  public static final String s20188 = "20188";
+  public static final String s20189 = "20189";
+  public static final String s20190 = "20190";
+  public static final String s20191 = "20191";
+  public static final String s20192 = "20192";
+  public static final String s20193 = "20193";
+  public static final String s20194 = "20194";
+  public static final String s20195 = "20195";
+  public static final String s20196 = "20196";
+  public static final String s20197 = "20197";
+  public static final String s20198 = "20198";
+  public static final String s20199 = "20199";
+  public static final String s20200 = "20200";
+  public static final String s20201 = "20201";
+  public static final String s20202 = "20202";
+  public static final String s20203 = "20203";
+  public static final String s20204 = "20204";
+  public static final String s20205 = "20205";
+  public static final String s20206 = "20206";
+  public static final String s20207 = "20207";
+  public static final String s20208 = "20208";
+  public static final String s20209 = "20209";
+  public static final String s20210 = "20210";
+  public static final String s20211 = "20211";
+  public static final String s20212 = "20212";
+  public static final String s20213 = "20213";
+  public static final String s20214 = "20214";
+  public static final String s20215 = "20215";
+  public static final String s20216 = "20216";
+  public static final String s20217 = "20217";
+  public static final String s20218 = "20218";
+  public static final String s20219 = "20219";
+  public static final String s20220 = "20220";
+  public static final String s20221 = "20221";
+  public static final String s20222 = "20222";
+  public static final String s20223 = "20223";
+  public static final String s20224 = "20224";
+  public static final String s20225 = "20225";
+  public static final String s20226 = "20226";
+  public static final String s20227 = "20227";
+  public static final String s20228 = "20228";
+  public static final String s20229 = "20229";
+  public static final String s20230 = "20230";
+  public static final String s20231 = "20231";
+  public static final String s20232 = "20232";
+  public static final String s20233 = "20233";
+  public static final String s20234 = "20234";
+  public static final String s20235 = "20235";
+  public static final String s20236 = "20236";
+  public static final String s20237 = "20237";
+  public static final String s20238 = "20238";
+  public static final String s20239 = "20239";
+  public static final String s20240 = "20240";
+  public static final String s20241 = "20241";
+  public static final String s20242 = "20242";
+  public static final String s20243 = "20243";
+  public static final String s20244 = "20244";
+  public static final String s20245 = "20245";
+  public static final String s20246 = "20246";
+  public static final String s20247 = "20247";
+  public static final String s20248 = "20248";
+  public static final String s20249 = "20249";
+  public static final String s20250 = "20250";
+  public static final String s20251 = "20251";
+  public static final String s20252 = "20252";
+  public static final String s20253 = "20253";
+  public static final String s20254 = "20254";
+  public static final String s20255 = "20255";
+  public static final String s20256 = "20256";
+  public static final String s20257 = "20257";
+  public static final String s20258 = "20258";
+  public static final String s20259 = "20259";
+  public static final String s20260 = "20260";
+  public static final String s20261 = "20261";
+  public static final String s20262 = "20262";
+  public static final String s20263 = "20263";
+  public static final String s20264 = "20264";
+  public static final String s20265 = "20265";
+  public static final String s20266 = "20266";
+  public static final String s20267 = "20267";
+  public static final String s20268 = "20268";
+  public static final String s20269 = "20269";
+  public static final String s20270 = "20270";
+  public static final String s20271 = "20271";
+  public static final String s20272 = "20272";
+  public static final String s20273 = "20273";
+  public static final String s20274 = "20274";
+  public static final String s20275 = "20275";
+  public static final String s20276 = "20276";
+  public static final String s20277 = "20277";
+  public static final String s20278 = "20278";
+  public static final String s20279 = "20279";
+  public static final String s20280 = "20280";
+  public static final String s20281 = "20281";
+  public static final String s20282 = "20282";
+  public static final String s20283 = "20283";
+  public static final String s20284 = "20284";
+  public static final String s20285 = "20285";
+  public static final String s20286 = "20286";
+  public static final String s20287 = "20287";
+  public static final String s20288 = "20288";
+  public static final String s20289 = "20289";
+  public static final String s20290 = "20290";
+  public static final String s20291 = "20291";
+  public static final String s20292 = "20292";
+  public static final String s20293 = "20293";
+  public static final String s20294 = "20294";
+  public static final String s20295 = "20295";
+  public static final String s20296 = "20296";
+  public static final String s20297 = "20297";
+  public static final String s20298 = "20298";
+  public static final String s20299 = "20299";
+  public static final String s20300 = "20300";
+  public static final String s20301 = "20301";
+  public static final String s20302 = "20302";
+  public static final String s20303 = "20303";
+  public static final String s20304 = "20304";
+  public static final String s20305 = "20305";
+  public static final String s20306 = "20306";
+  public static final String s20307 = "20307";
+  public static final String s20308 = "20308";
+  public static final String s20309 = "20309";
+  public static final String s20310 = "20310";
+  public static final String s20311 = "20311";
+  public static final String s20312 = "20312";
+  public static final String s20313 = "20313";
+  public static final String s20314 = "20314";
+  public static final String s20315 = "20315";
+  public static final String s20316 = "20316";
+  public static final String s20317 = "20317";
+  public static final String s20318 = "20318";
+  public static final String s20319 = "20319";
+  public static final String s20320 = "20320";
+  public static final String s20321 = "20321";
+  public static final String s20322 = "20322";
+  public static final String s20323 = "20323";
+  public static final String s20324 = "20324";
+  public static final String s20325 = "20325";
+  public static final String s20326 = "20326";
+  public static final String s20327 = "20327";
+  public static final String s20328 = "20328";
+  public static final String s20329 = "20329";
+  public static final String s20330 = "20330";
+  public static final String s20331 = "20331";
+  public static final String s20332 = "20332";
+  public static final String s20333 = "20333";
+  public static final String s20334 = "20334";
+  public static final String s20335 = "20335";
+  public static final String s20336 = "20336";
+  public static final String s20337 = "20337";
+  public static final String s20338 = "20338";
+  public static final String s20339 = "20339";
+  public static final String s20340 = "20340";
+  public static final String s20341 = "20341";
+  public static final String s20342 = "20342";
+  public static final String s20343 = "20343";
+  public static final String s20344 = "20344";
+  public static final String s20345 = "20345";
+  public static final String s20346 = "20346";
+  public static final String s20347 = "20347";
+  public static final String s20348 = "20348";
+  public static final String s20349 = "20349";
+  public static final String s20350 = "20350";
+  public static final String s20351 = "20351";
+  public static final String s20352 = "20352";
+  public static final String s20353 = "20353";
+  public static final String s20354 = "20354";
+  public static final String s20355 = "20355";
+  public static final String s20356 = "20356";
+  public static final String s20357 = "20357";
+  public static final String s20358 = "20358";
+  public static final String s20359 = "20359";
+  public static final String s20360 = "20360";
+  public static final String s20361 = "20361";
+  public static final String s20362 = "20362";
+  public static final String s20363 = "20363";
+  public static final String s20364 = "20364";
+  public static final String s20365 = "20365";
+  public static final String s20366 = "20366";
+  public static final String s20367 = "20367";
+  public static final String s20368 = "20368";
+  public static final String s20369 = "20369";
+  public static final String s20370 = "20370";
+  public static final String s20371 = "20371";
+  public static final String s20372 = "20372";
+  public static final String s20373 = "20373";
+  public static final String s20374 = "20374";
+  public static final String s20375 = "20375";
+  public static final String s20376 = "20376";
+  public static final String s20377 = "20377";
+  public static final String s20378 = "20378";
+  public static final String s20379 = "20379";
+  public static final String s20380 = "20380";
+  public static final String s20381 = "20381";
+  public static final String s20382 = "20382";
+  public static final String s20383 = "20383";
+  public static final String s20384 = "20384";
+  public static final String s20385 = "20385";
+  public static final String s20386 = "20386";
+  public static final String s20387 = "20387";
+  public static final String s20388 = "20388";
+  public static final String s20389 = "20389";
+  public static final String s20390 = "20390";
+  public static final String s20391 = "20391";
+  public static final String s20392 = "20392";
+  public static final String s20393 = "20393";
+  public static final String s20394 = "20394";
+  public static final String s20395 = "20395";
+  public static final String s20396 = "20396";
+  public static final String s20397 = "20397";
+  public static final String s20398 = "20398";
+  public static final String s20399 = "20399";
+  public static final String s20400 = "20400";
+  public static final String s20401 = "20401";
+  public static final String s20402 = "20402";
+  public static final String s20403 = "20403";
+  public static final String s20404 = "20404";
+  public static final String s20405 = "20405";
+  public static final String s20406 = "20406";
+  public static final String s20407 = "20407";
+  public static final String s20408 = "20408";
+  public static final String s20409 = "20409";
+  public static final String s20410 = "20410";
+  public static final String s20411 = "20411";
+  public static final String s20412 = "20412";
+  public static final String s20413 = "20413";
+  public static final String s20414 = "20414";
+  public static final String s20415 = "20415";
+  public static final String s20416 = "20416";
+  public static final String s20417 = "20417";
+  public static final String s20418 = "20418";
+  public static final String s20419 = "20419";
+  public static final String s20420 = "20420";
+  public static final String s20421 = "20421";
+  public static final String s20422 = "20422";
+  public static final String s20423 = "20423";
+  public static final String s20424 = "20424";
+  public static final String s20425 = "20425";
+  public static final String s20426 = "20426";
+  public static final String s20427 = "20427";
+  public static final String s20428 = "20428";
+  public static final String s20429 = "20429";
+  public static final String s20430 = "20430";
+  public static final String s20431 = "20431";
+  public static final String s20432 = "20432";
+  public static final String s20433 = "20433";
+  public static final String s20434 = "20434";
+  public static final String s20435 = "20435";
+  public static final String s20436 = "20436";
+  public static final String s20437 = "20437";
+  public static final String s20438 = "20438";
+  public static final String s20439 = "20439";
+  public static final String s20440 = "20440";
+  public static final String s20441 = "20441";
+  public static final String s20442 = "20442";
+  public static final String s20443 = "20443";
+  public static final String s20444 = "20444";
+  public static final String s20445 = "20445";
+  public static final String s20446 = "20446";
+  public static final String s20447 = "20447";
+  public static final String s20448 = "20448";
+  public static final String s20449 = "20449";
+  public static final String s20450 = "20450";
+  public static final String s20451 = "20451";
+  public static final String s20452 = "20452";
+  public static final String s20453 = "20453";
+  public static final String s20454 = "20454";
+  public static final String s20455 = "20455";
+  public static final String s20456 = "20456";
+  public static final String s20457 = "20457";
+  public static final String s20458 = "20458";
+  public static final String s20459 = "20459";
+  public static final String s20460 = "20460";
+  public static final String s20461 = "20461";
+  public static final String s20462 = "20462";
+  public static final String s20463 = "20463";
+  public static final String s20464 = "20464";
+  public static final String s20465 = "20465";
+  public static final String s20466 = "20466";
+  public static final String s20467 = "20467";
+  public static final String s20468 = "20468";
+  public static final String s20469 = "20469";
+  public static final String s20470 = "20470";
+  public static final String s20471 = "20471";
+  public static final String s20472 = "20472";
+  public static final String s20473 = "20473";
+  public static final String s20474 = "20474";
+  public static final String s20475 = "20475";
+  public static final String s20476 = "20476";
+  public static final String s20477 = "20477";
+  public static final String s20478 = "20478";
+  public static final String s20479 = "20479";
+  public static final String s20480 = "20480";
+  public static final String s20481 = "20481";
+  public static final String s20482 = "20482";
+  public static final String s20483 = "20483";
+  public static final String s20484 = "20484";
+  public static final String s20485 = "20485";
+  public static final String s20486 = "20486";
+  public static final String s20487 = "20487";
+  public static final String s20488 = "20488";
+  public static final String s20489 = "20489";
+  public static final String s20490 = "20490";
+  public static final String s20491 = "20491";
+  public static final String s20492 = "20492";
+  public static final String s20493 = "20493";
+  public static final String s20494 = "20494";
+  public static final String s20495 = "20495";
+  public static final String s20496 = "20496";
+  public static final String s20497 = "20497";
+  public static final String s20498 = "20498";
+  public static final String s20499 = "20499";
+  public static final String s20500 = "20500";
+  public static final String s20501 = "20501";
+  public static final String s20502 = "20502";
+  public static final String s20503 = "20503";
+  public static final String s20504 = "20504";
+  public static final String s20505 = "20505";
+  public static final String s20506 = "20506";
+  public static final String s20507 = "20507";
+  public static final String s20508 = "20508";
+  public static final String s20509 = "20509";
+  public static final String s20510 = "20510";
+  public static final String s20511 = "20511";
+  public static final String s20512 = "20512";
+  public static final String s20513 = "20513";
+  public static final String s20514 = "20514";
+  public static final String s20515 = "20515";
+  public static final String s20516 = "20516";
+  public static final String s20517 = "20517";
+  public static final String s20518 = "20518";
+  public static final String s20519 = "20519";
+  public static final String s20520 = "20520";
+  public static final String s20521 = "20521";
+  public static final String s20522 = "20522";
+  public static final String s20523 = "20523";
+  public static final String s20524 = "20524";
+  public static final String s20525 = "20525";
+  public static final String s20526 = "20526";
+  public static final String s20527 = "20527";
+  public static final String s20528 = "20528";
+  public static final String s20529 = "20529";
+  public static final String s20530 = "20530";
+  public static final String s20531 = "20531";
+  public static final String s20532 = "20532";
+  public static final String s20533 = "20533";
+  public static final String s20534 = "20534";
+  public static final String s20535 = "20535";
+  public static final String s20536 = "20536";
+  public static final String s20537 = "20537";
+  public static final String s20538 = "20538";
+  public static final String s20539 = "20539";
+  public static final String s20540 = "20540";
+  public static final String s20541 = "20541";
+  public static final String s20542 = "20542";
+  public static final String s20543 = "20543";
+  public static final String s20544 = "20544";
+  public static final String s20545 = "20545";
+  public static final String s20546 = "20546";
+  public static final String s20547 = "20547";
+  public static final String s20548 = "20548";
+  public static final String s20549 = "20549";
+  public static final String s20550 = "20550";
+  public static final String s20551 = "20551";
+  public static final String s20552 = "20552";
+  public static final String s20553 = "20553";
+  public static final String s20554 = "20554";
+  public static final String s20555 = "20555";
+  public static final String s20556 = "20556";
+  public static final String s20557 = "20557";
+  public static final String s20558 = "20558";
+  public static final String s20559 = "20559";
+  public static final String s20560 = "20560";
+  public static final String s20561 = "20561";
+  public static final String s20562 = "20562";
+  public static final String s20563 = "20563";
+  public static final String s20564 = "20564";
+  public static final String s20565 = "20565";
+  public static final String s20566 = "20566";
+  public static final String s20567 = "20567";
+  public static final String s20568 = "20568";
+  public static final String s20569 = "20569";
+  public static final String s20570 = "20570";
+  public static final String s20571 = "20571";
+  public static final String s20572 = "20572";
+  public static final String s20573 = "20573";
+  public static final String s20574 = "20574";
+  public static final String s20575 = "20575";
+  public static final String s20576 = "20576";
+  public static final String s20577 = "20577";
+  public static final String s20578 = "20578";
+  public static final String s20579 = "20579";
+  public static final String s20580 = "20580";
+  public static final String s20581 = "20581";
+  public static final String s20582 = "20582";
+  public static final String s20583 = "20583";
+  public static final String s20584 = "20584";
+  public static final String s20585 = "20585";
+  public static final String s20586 = "20586";
+  public static final String s20587 = "20587";
+  public static final String s20588 = "20588";
+  public static final String s20589 = "20589";
+  public static final String s20590 = "20590";
+  public static final String s20591 = "20591";
+  public static final String s20592 = "20592";
+  public static final String s20593 = "20593";
+  public static final String s20594 = "20594";
+  public static final String s20595 = "20595";
+  public static final String s20596 = "20596";
+  public static final String s20597 = "20597";
+  public static final String s20598 = "20598";
+  public static final String s20599 = "20599";
+  public static final String s20600 = "20600";
+  public static final String s20601 = "20601";
+  public static final String s20602 = "20602";
+  public static final String s20603 = "20603";
+  public static final String s20604 = "20604";
+  public static final String s20605 = "20605";
+  public static final String s20606 = "20606";
+  public static final String s20607 = "20607";
+  public static final String s20608 = "20608";
+  public static final String s20609 = "20609";
+  public static final String s20610 = "20610";
+  public static final String s20611 = "20611";
+  public static final String s20612 = "20612";
+  public static final String s20613 = "20613";
+  public static final String s20614 = "20614";
+  public static final String s20615 = "20615";
+  public static final String s20616 = "20616";
+  public static final String s20617 = "20617";
+  public static final String s20618 = "20618";
+  public static final String s20619 = "20619";
+  public static final String s20620 = "20620";
+  public static final String s20621 = "20621";
+  public static final String s20622 = "20622";
+  public static final String s20623 = "20623";
+  public static final String s20624 = "20624";
+  public static final String s20625 = "20625";
+  public static final String s20626 = "20626";
+  public static final String s20627 = "20627";
+  public static final String s20628 = "20628";
+  public static final String s20629 = "20629";
+  public static final String s20630 = "20630";
+  public static final String s20631 = "20631";
+  public static final String s20632 = "20632";
+  public static final String s20633 = "20633";
+  public static final String s20634 = "20634";
+  public static final String s20635 = "20635";
+  public static final String s20636 = "20636";
+  public static final String s20637 = "20637";
+  public static final String s20638 = "20638";
+  public static final String s20639 = "20639";
+  public static final String s20640 = "20640";
+  public static final String s20641 = "20641";
+  public static final String s20642 = "20642";
+  public static final String s20643 = "20643";
+  public static final String s20644 = "20644";
+  public static final String s20645 = "20645";
+  public static final String s20646 = "20646";
+  public static final String s20647 = "20647";
+  public static final String s20648 = "20648";
+  public static final String s20649 = "20649";
+  public static final String s20650 = "20650";
+  public static final String s20651 = "20651";
+  public static final String s20652 = "20652";
+  public static final String s20653 = "20653";
+  public static final String s20654 = "20654";
+  public static final String s20655 = "20655";
+  public static final String s20656 = "20656";
+  public static final String s20657 = "20657";
+  public static final String s20658 = "20658";
+  public static final String s20659 = "20659";
+  public static final String s20660 = "20660";
+  public static final String s20661 = "20661";
+  public static final String s20662 = "20662";
+  public static final String s20663 = "20663";
+  public static final String s20664 = "20664";
+  public static final String s20665 = "20665";
+  public static final String s20666 = "20666";
+  public static final String s20667 = "20667";
+  public static final String s20668 = "20668";
+  public static final String s20669 = "20669";
+  public static final String s20670 = "20670";
+  public static final String s20671 = "20671";
+  public static final String s20672 = "20672";
+  public static final String s20673 = "20673";
+  public static final String s20674 = "20674";
+  public static final String s20675 = "20675";
+  public static final String s20676 = "20676";
+  public static final String s20677 = "20677";
+  public static final String s20678 = "20678";
+  public static final String s20679 = "20679";
+  public static final String s20680 = "20680";
+  public static final String s20681 = "20681";
+  public static final String s20682 = "20682";
+  public static final String s20683 = "20683";
+  public static final String s20684 = "20684";
+  public static final String s20685 = "20685";
+  public static final String s20686 = "20686";
+  public static final String s20687 = "20687";
+  public static final String s20688 = "20688";
+  public static final String s20689 = "20689";
+  public static final String s20690 = "20690";
+  public static final String s20691 = "20691";
+  public static final String s20692 = "20692";
+  public static final String s20693 = "20693";
+  public static final String s20694 = "20694";
+  public static final String s20695 = "20695";
+  public static final String s20696 = "20696";
+  public static final String s20697 = "20697";
+  public static final String s20698 = "20698";
+  public static final String s20699 = "20699";
+  public static final String s20700 = "20700";
+  public static final String s20701 = "20701";
+  public static final String s20702 = "20702";
+  public static final String s20703 = "20703";
+  public static final String s20704 = "20704";
+  public static final String s20705 = "20705";
+  public static final String s20706 = "20706";
+  public static final String s20707 = "20707";
+  public static final String s20708 = "20708";
+  public static final String s20709 = "20709";
+  public static final String s20710 = "20710";
+  public static final String s20711 = "20711";
+  public static final String s20712 = "20712";
+  public static final String s20713 = "20713";
+  public static final String s20714 = "20714";
+  public static final String s20715 = "20715";
+  public static final String s20716 = "20716";
+  public static final String s20717 = "20717";
+  public static final String s20718 = "20718";
+  public static final String s20719 = "20719";
+  public static final String s20720 = "20720";
+  public static final String s20721 = "20721";
+  public static final String s20722 = "20722";
+  public static final String s20723 = "20723";
+  public static final String s20724 = "20724";
+  public static final String s20725 = "20725";
+  public static final String s20726 = "20726";
+  public static final String s20727 = "20727";
+  public static final String s20728 = "20728";
+  public static final String s20729 = "20729";
+  public static final String s20730 = "20730";
+  public static final String s20731 = "20731";
+  public static final String s20732 = "20732";
+  public static final String s20733 = "20733";
+  public static final String s20734 = "20734";
+  public static final String s20735 = "20735";
+  public static final String s20736 = "20736";
+  public static final String s20737 = "20737";
+  public static final String s20738 = "20738";
+  public static final String s20739 = "20739";
+  public static final String s20740 = "20740";
+  public static final String s20741 = "20741";
+  public static final String s20742 = "20742";
+  public static final String s20743 = "20743";
+  public static final String s20744 = "20744";
+  public static final String s20745 = "20745";
+  public static final String s20746 = "20746";
+  public static final String s20747 = "20747";
+  public static final String s20748 = "20748";
+  public static final String s20749 = "20749";
+  public static final String s20750 = "20750";
+  public static final String s20751 = "20751";
+  public static final String s20752 = "20752";
+  public static final String s20753 = "20753";
+  public static final String s20754 = "20754";
+  public static final String s20755 = "20755";
+  public static final String s20756 = "20756";
+  public static final String s20757 = "20757";
+  public static final String s20758 = "20758";
+  public static final String s20759 = "20759";
+  public static final String s20760 = "20760";
+  public static final String s20761 = "20761";
+  public static final String s20762 = "20762";
+  public static final String s20763 = "20763";
+  public static final String s20764 = "20764";
+  public static final String s20765 = "20765";
+  public static final String s20766 = "20766";
+  public static final String s20767 = "20767";
+  public static final String s20768 = "20768";
+  public static final String s20769 = "20769";
+  public static final String s20770 = "20770";
+  public static final String s20771 = "20771";
+  public static final String s20772 = "20772";
+  public static final String s20773 = "20773";
+  public static final String s20774 = "20774";
+  public static final String s20775 = "20775";
+  public static final String s20776 = "20776";
+  public static final String s20777 = "20777";
+  public static final String s20778 = "20778";
+  public static final String s20779 = "20779";
+  public static final String s20780 = "20780";
+  public static final String s20781 = "20781";
+  public static final String s20782 = "20782";
+  public static final String s20783 = "20783";
+  public static final String s20784 = "20784";
+  public static final String s20785 = "20785";
+  public static final String s20786 = "20786";
+  public static final String s20787 = "20787";
+  public static final String s20788 = "20788";
+  public static final String s20789 = "20789";
+  public static final String s20790 = "20790";
+  public static final String s20791 = "20791";
+  public static final String s20792 = "20792";
+  public static final String s20793 = "20793";
+  public static final String s20794 = "20794";
+  public static final String s20795 = "20795";
+  public static final String s20796 = "20796";
+  public static final String s20797 = "20797";
+  public static final String s20798 = "20798";
+  public static final String s20799 = "20799";
+  public static final String s20800 = "20800";
+  public static final String s20801 = "20801";
+  public static final String s20802 = "20802";
+  public static final String s20803 = "20803";
+  public static final String s20804 = "20804";
+  public static final String s20805 = "20805";
+  public static final String s20806 = "20806";
+  public static final String s20807 = "20807";
+  public static final String s20808 = "20808";
+  public static final String s20809 = "20809";
+  public static final String s20810 = "20810";
+  public static final String s20811 = "20811";
+  public static final String s20812 = "20812";
+  public static final String s20813 = "20813";
+  public static final String s20814 = "20814";
+  public static final String s20815 = "20815";
+  public static final String s20816 = "20816";
+  public static final String s20817 = "20817";
+  public static final String s20818 = "20818";
+  public static final String s20819 = "20819";
+  public static final String s20820 = "20820";
+  public static final String s20821 = "20821";
+  public static final String s20822 = "20822";
+  public static final String s20823 = "20823";
+  public static final String s20824 = "20824";
+  public static final String s20825 = "20825";
+  public static final String s20826 = "20826";
+  public static final String s20827 = "20827";
+  public static final String s20828 = "20828";
+  public static final String s20829 = "20829";
+  public static final String s20830 = "20830";
+  public static final String s20831 = "20831";
+  public static final String s20832 = "20832";
+  public static final String s20833 = "20833";
+  public static final String s20834 = "20834";
+  public static final String s20835 = "20835";
+  public static final String s20836 = "20836";
+  public static final String s20837 = "20837";
+  public static final String s20838 = "20838";
+  public static final String s20839 = "20839";
+  public static final String s20840 = "20840";
+  public static final String s20841 = "20841";
+  public static final String s20842 = "20842";
+  public static final String s20843 = "20843";
+  public static final String s20844 = "20844";
+  public static final String s20845 = "20845";
+  public static final String s20846 = "20846";
+  public static final String s20847 = "20847";
+  public static final String s20848 = "20848";
+  public static final String s20849 = "20849";
+  public static final String s20850 = "20850";
+  public static final String s20851 = "20851";
+  public static final String s20852 = "20852";
+  public static final String s20853 = "20853";
+  public static final String s20854 = "20854";
+  public static final String s20855 = "20855";
+  public static final String s20856 = "20856";
+  public static final String s20857 = "20857";
+  public static final String s20858 = "20858";
+  public static final String s20859 = "20859";
+  public static final String s20860 = "20860";
+  public static final String s20861 = "20861";
+  public static final String s20862 = "20862";
+  public static final String s20863 = "20863";
+  public static final String s20864 = "20864";
+  public static final String s20865 = "20865";
+  public static final String s20866 = "20866";
+  public static final String s20867 = "20867";
+  public static final String s20868 = "20868";
+  public static final String s20869 = "20869";
+  public static final String s20870 = "20870";
+  public static final String s20871 = "20871";
+  public static final String s20872 = "20872";
+  public static final String s20873 = "20873";
+  public static final String s20874 = "20874";
+  public static final String s20875 = "20875";
+  public static final String s20876 = "20876";
+  public static final String s20877 = "20877";
+  public static final String s20878 = "20878";
+  public static final String s20879 = "20879";
+  public static final String s20880 = "20880";
+  public static final String s20881 = "20881";
+  public static final String s20882 = "20882";
+  public static final String s20883 = "20883";
+  public static final String s20884 = "20884";
+  public static final String s20885 = "20885";
+  public static final String s20886 = "20886";
+  public static final String s20887 = "20887";
+  public static final String s20888 = "20888";
+  public static final String s20889 = "20889";
+  public static final String s20890 = "20890";
+  public static final String s20891 = "20891";
+  public static final String s20892 = "20892";
+  public static final String s20893 = "20893";
+  public static final String s20894 = "20894";
+  public static final String s20895 = "20895";
+  public static final String s20896 = "20896";
+  public static final String s20897 = "20897";
+  public static final String s20898 = "20898";
+  public static final String s20899 = "20899";
+  public static final String s20900 = "20900";
+  public static final String s20901 = "20901";
+  public static final String s20902 = "20902";
+  public static final String s20903 = "20903";
+  public static final String s20904 = "20904";
+  public static final String s20905 = "20905";
+  public static final String s20906 = "20906";
+  public static final String s20907 = "20907";
+  public static final String s20908 = "20908";
+  public static final String s20909 = "20909";
+  public static final String s20910 = "20910";
+  public static final String s20911 = "20911";
+  public static final String s20912 = "20912";
+  public static final String s20913 = "20913";
+  public static final String s20914 = "20914";
+  public static final String s20915 = "20915";
+  public static final String s20916 = "20916";
+  public static final String s20917 = "20917";
+  public static final String s20918 = "20918";
+  public static final String s20919 = "20919";
+  public static final String s20920 = "20920";
+  public static final String s20921 = "20921";
+  public static final String s20922 = "20922";
+  public static final String s20923 = "20923";
+  public static final String s20924 = "20924";
+  public static final String s20925 = "20925";
+  public static final String s20926 = "20926";
+  public static final String s20927 = "20927";
+  public static final String s20928 = "20928";
+  public static final String s20929 = "20929";
+  public static final String s20930 = "20930";
+  public static final String s20931 = "20931";
+  public static final String s20932 = "20932";
+  public static final String s20933 = "20933";
+  public static final String s20934 = "20934";
+  public static final String s20935 = "20935";
+  public static final String s20936 = "20936";
+  public static final String s20937 = "20937";
+  public static final String s20938 = "20938";
+  public static final String s20939 = "20939";
+  public static final String s20940 = "20940";
+  public static final String s20941 = "20941";
+  public static final String s20942 = "20942";
+  public static final String s20943 = "20943";
+  public static final String s20944 = "20944";
+  public static final String s20945 = "20945";
+  public static final String s20946 = "20946";
+  public static final String s20947 = "20947";
+  public static final String s20948 = "20948";
+  public static final String s20949 = "20949";
+  public static final String s20950 = "20950";
+  public static final String s20951 = "20951";
+  public static final String s20952 = "20952";
+  public static final String s20953 = "20953";
+  public static final String s20954 = "20954";
+  public static final String s20955 = "20955";
+  public static final String s20956 = "20956";
+  public static final String s20957 = "20957";
+  public static final String s20958 = "20958";
+  public static final String s20959 = "20959";
+  public static final String s20960 = "20960";
+  public static final String s20961 = "20961";
+  public static final String s20962 = "20962";
+  public static final String s20963 = "20963";
+  public static final String s20964 = "20964";
+  public static final String s20965 = "20965";
+  public static final String s20966 = "20966";
+  public static final String s20967 = "20967";
+  public static final String s20968 = "20968";
+  public static final String s20969 = "20969";
+  public static final String s20970 = "20970";
+  public static final String s20971 = "20971";
+  public static final String s20972 = "20972";
+  public static final String s20973 = "20973";
+  public static final String s20974 = "20974";
+  public static final String s20975 = "20975";
+  public static final String s20976 = "20976";
+  public static final String s20977 = "20977";
+  public static final String s20978 = "20978";
+  public static final String s20979 = "20979";
+  public static final String s20980 = "20980";
+  public static final String s20981 = "20981";
+  public static final String s20982 = "20982";
+  public static final String s20983 = "20983";
+  public static final String s20984 = "20984";
+  public static final String s20985 = "20985";
+  public static final String s20986 = "20986";
+  public static final String s20987 = "20987";
+  public static final String s20988 = "20988";
+  public static final String s20989 = "20989";
+  public static final String s20990 = "20990";
+  public static final String s20991 = "20991";
+  public static final String s20992 = "20992";
+  public static final String s20993 = "20993";
+  public static final String s20994 = "20994";
+  public static final String s20995 = "20995";
+  public static final String s20996 = "20996";
+  public static final String s20997 = "20997";
+  public static final String s20998 = "20998";
+  public static final String s20999 = "20999";
+  public static final String s21000 = "21000";
+  public static final String s21001 = "21001";
+  public static final String s21002 = "21002";
+  public static final String s21003 = "21003";
+  public static final String s21004 = "21004";
+  public static final String s21005 = "21005";
+  public static final String s21006 = "21006";
+  public static final String s21007 = "21007";
+  public static final String s21008 = "21008";
+  public static final String s21009 = "21009";
+  public static final String s21010 = "21010";
+  public static final String s21011 = "21011";
+  public static final String s21012 = "21012";
+  public static final String s21013 = "21013";
+  public static final String s21014 = "21014";
+  public static final String s21015 = "21015";
+  public static final String s21016 = "21016";
+  public static final String s21017 = "21017";
+  public static final String s21018 = "21018";
+  public static final String s21019 = "21019";
+  public static final String s21020 = "21020";
+  public static final String s21021 = "21021";
+  public static final String s21022 = "21022";
+  public static final String s21023 = "21023";
+  public static final String s21024 = "21024";
+  public static final String s21025 = "21025";
+  public static final String s21026 = "21026";
+  public static final String s21027 = "21027";
+  public static final String s21028 = "21028";
+  public static final String s21029 = "21029";
+  public static final String s21030 = "21030";
+  public static final String s21031 = "21031";
+  public static final String s21032 = "21032";
+  public static final String s21033 = "21033";
+  public static final String s21034 = "21034";
+  public static final String s21035 = "21035";
+  public static final String s21036 = "21036";
+  public static final String s21037 = "21037";
+  public static final String s21038 = "21038";
+  public static final String s21039 = "21039";
+  public static final String s21040 = "21040";
+  public static final String s21041 = "21041";
+  public static final String s21042 = "21042";
+  public static final String s21043 = "21043";
+  public static final String s21044 = "21044";
+  public static final String s21045 = "21045";
+  public static final String s21046 = "21046";
+  public static final String s21047 = "21047";
+  public static final String s21048 = "21048";
+  public static final String s21049 = "21049";
+  public static final String s21050 = "21050";
+  public static final String s21051 = "21051";
+  public static final String s21052 = "21052";
+  public static final String s21053 = "21053";
+  public static final String s21054 = "21054";
+  public static final String s21055 = "21055";
+  public static final String s21056 = "21056";
+  public static final String s21057 = "21057";
+  public static final String s21058 = "21058";
+  public static final String s21059 = "21059";
+  public static final String s21060 = "21060";
+  public static final String s21061 = "21061";
+  public static final String s21062 = "21062";
+  public static final String s21063 = "21063";
+  public static final String s21064 = "21064";
+  public static final String s21065 = "21065";
+  public static final String s21066 = "21066";
+  public static final String s21067 = "21067";
+  public static final String s21068 = "21068";
+  public static final String s21069 = "21069";
+  public static final String s21070 = "21070";
+  public static final String s21071 = "21071";
+  public static final String s21072 = "21072";
+  public static final String s21073 = "21073";
+  public static final String s21074 = "21074";
+  public static final String s21075 = "21075";
+  public static final String s21076 = "21076";
+  public static final String s21077 = "21077";
+  public static final String s21078 = "21078";
+  public static final String s21079 = "21079";
+  public static final String s21080 = "21080";
+  public static final String s21081 = "21081";
+  public static final String s21082 = "21082";
+  public static final String s21083 = "21083";
+  public static final String s21084 = "21084";
+  public static final String s21085 = "21085";
+  public static final String s21086 = "21086";
+  public static final String s21087 = "21087";
+  public static final String s21088 = "21088";
+  public static final String s21089 = "21089";
+  public static final String s21090 = "21090";
+  public static final String s21091 = "21091";
+  public static final String s21092 = "21092";
+  public static final String s21093 = "21093";
+  public static final String s21094 = "21094";
+  public static final String s21095 = "21095";
+  public static final String s21096 = "21096";
+  public static final String s21097 = "21097";
+  public static final String s21098 = "21098";
+  public static final String s21099 = "21099";
+  public static final String s21100 = "21100";
+  public static final String s21101 = "21101";
+  public static final String s21102 = "21102";
+  public static final String s21103 = "21103";
+  public static final String s21104 = "21104";
+  public static final String s21105 = "21105";
+  public static final String s21106 = "21106";
+  public static final String s21107 = "21107";
+  public static final String s21108 = "21108";
+  public static final String s21109 = "21109";
+  public static final String s21110 = "21110";
+  public static final String s21111 = "21111";
+  public static final String s21112 = "21112";
+  public static final String s21113 = "21113";
+  public static final String s21114 = "21114";
+  public static final String s21115 = "21115";
+  public static final String s21116 = "21116";
+  public static final String s21117 = "21117";
+  public static final String s21118 = "21118";
+  public static final String s21119 = "21119";
+  public static final String s21120 = "21120";
+  public static final String s21121 = "21121";
+  public static final String s21122 = "21122";
+  public static final String s21123 = "21123";
+  public static final String s21124 = "21124";
+  public static final String s21125 = "21125";
+  public static final String s21126 = "21126";
+  public static final String s21127 = "21127";
+  public static final String s21128 = "21128";
+  public static final String s21129 = "21129";
+  public static final String s21130 = "21130";
+  public static final String s21131 = "21131";
+  public static final String s21132 = "21132";
+  public static final String s21133 = "21133";
+  public static final String s21134 = "21134";
+  public static final String s21135 = "21135";
+  public static final String s21136 = "21136";
+  public static final String s21137 = "21137";
+  public static final String s21138 = "21138";
+  public static final String s21139 = "21139";
+  public static final String s21140 = "21140";
+  public static final String s21141 = "21141";
+  public static final String s21142 = "21142";
+  public static final String s21143 = "21143";
+  public static final String s21144 = "21144";
+  public static final String s21145 = "21145";
+  public static final String s21146 = "21146";
+  public static final String s21147 = "21147";
+  public static final String s21148 = "21148";
+  public static final String s21149 = "21149";
+  public static final String s21150 = "21150";
+  public static final String s21151 = "21151";
+  public static final String s21152 = "21152";
+  public static final String s21153 = "21153";
+  public static final String s21154 = "21154";
+  public static final String s21155 = "21155";
+  public static final String s21156 = "21156";
+  public static final String s21157 = "21157";
+  public static final String s21158 = "21158";
+  public static final String s21159 = "21159";
+  public static final String s21160 = "21160";
+  public static final String s21161 = "21161";
+  public static final String s21162 = "21162";
+  public static final String s21163 = "21163";
+  public static final String s21164 = "21164";
+  public static final String s21165 = "21165";
+  public static final String s21166 = "21166";
+  public static final String s21167 = "21167";
+  public static final String s21168 = "21168";
+  public static final String s21169 = "21169";
+  public static final String s21170 = "21170";
+  public static final String s21171 = "21171";
+  public static final String s21172 = "21172";
+  public static final String s21173 = "21173";
+  public static final String s21174 = "21174";
+  public static final String s21175 = "21175";
+  public static final String s21176 = "21176";
+  public static final String s21177 = "21177";
+  public static final String s21178 = "21178";
+  public static final String s21179 = "21179";
+  public static final String s21180 = "21180";
+  public static final String s21181 = "21181";
+  public static final String s21182 = "21182";
+  public static final String s21183 = "21183";
+  public static final String s21184 = "21184";
+  public static final String s21185 = "21185";
+  public static final String s21186 = "21186";
+  public static final String s21187 = "21187";
+  public static final String s21188 = "21188";
+  public static final String s21189 = "21189";
+  public static final String s21190 = "21190";
+  public static final String s21191 = "21191";
+  public static final String s21192 = "21192";
+  public static final String s21193 = "21193";
+  public static final String s21194 = "21194";
+  public static final String s21195 = "21195";
+  public static final String s21196 = "21196";
+  public static final String s21197 = "21197";
+  public static final String s21198 = "21198";
+  public static final String s21199 = "21199";
+  public static final String s21200 = "21200";
+  public static final String s21201 = "21201";
+  public static final String s21202 = "21202";
+  public static final String s21203 = "21203";
+  public static final String s21204 = "21204";
+  public static final String s21205 = "21205";
+  public static final String s21206 = "21206";
+  public static final String s21207 = "21207";
+  public static final String s21208 = "21208";
+  public static final String s21209 = "21209";
+  public static final String s21210 = "21210";
+  public static final String s21211 = "21211";
+  public static final String s21212 = "21212";
+  public static final String s21213 = "21213";
+  public static final String s21214 = "21214";
+  public static final String s21215 = "21215";
+  public static final String s21216 = "21216";
+  public static final String s21217 = "21217";
+  public static final String s21218 = "21218";
+  public static final String s21219 = "21219";
+  public static final String s21220 = "21220";
+  public static final String s21221 = "21221";
+  public static final String s21222 = "21222";
+  public static final String s21223 = "21223";
+  public static final String s21224 = "21224";
+  public static final String s21225 = "21225";
+  public static final String s21226 = "21226";
+  public static final String s21227 = "21227";
+  public static final String s21228 = "21228";
+  public static final String s21229 = "21229";
+  public static final String s21230 = "21230";
+  public static final String s21231 = "21231";
+  public static final String s21232 = "21232";
+  public static final String s21233 = "21233";
+  public static final String s21234 = "21234";
+  public static final String s21235 = "21235";
+  public static final String s21236 = "21236";
+  public static final String s21237 = "21237";
+  public static final String s21238 = "21238";
+  public static final String s21239 = "21239";
+  public static final String s21240 = "21240";
+  public static final String s21241 = "21241";
+  public static final String s21242 = "21242";
+  public static final String s21243 = "21243";
+  public static final String s21244 = "21244";
+  public static final String s21245 = "21245";
+  public static final String s21246 = "21246";
+  public static final String s21247 = "21247";
+  public static final String s21248 = "21248";
+  public static final String s21249 = "21249";
+  public static final String s21250 = "21250";
+  public static final String s21251 = "21251";
+  public static final String s21252 = "21252";
+  public static final String s21253 = "21253";
+  public static final String s21254 = "21254";
+  public static final String s21255 = "21255";
+  public static final String s21256 = "21256";
+  public static final String s21257 = "21257";
+  public static final String s21258 = "21258";
+  public static final String s21259 = "21259";
+  public static final String s21260 = "21260";
+  public static final String s21261 = "21261";
+  public static final String s21262 = "21262";
+  public static final String s21263 = "21263";
+  public static final String s21264 = "21264";
+  public static final String s21265 = "21265";
+  public static final String s21266 = "21266";
+  public static final String s21267 = "21267";
+  public static final String s21268 = "21268";
+  public static final String s21269 = "21269";
+  public static final String s21270 = "21270";
+  public static final String s21271 = "21271";
+  public static final String s21272 = "21272";
+  public static final String s21273 = "21273";
+  public static final String s21274 = "21274";
+  public static final String s21275 = "21275";
+  public static final String s21276 = "21276";
+  public static final String s21277 = "21277";
+  public static final String s21278 = "21278";
+  public static final String s21279 = "21279";
+  public static final String s21280 = "21280";
+  public static final String s21281 = "21281";
+  public static final String s21282 = "21282";
+  public static final String s21283 = "21283";
+  public static final String s21284 = "21284";
+  public static final String s21285 = "21285";
+  public static final String s21286 = "21286";
+  public static final String s21287 = "21287";
+  public static final String s21288 = "21288";
+  public static final String s21289 = "21289";
+  public static final String s21290 = "21290";
+  public static final String s21291 = "21291";
+  public static final String s21292 = "21292";
+  public static final String s21293 = "21293";
+  public static final String s21294 = "21294";
+  public static final String s21295 = "21295";
+  public static final String s21296 = "21296";
+  public static final String s21297 = "21297";
+  public static final String s21298 = "21298";
+  public static final String s21299 = "21299";
+  public static final String s21300 = "21300";
+  public static final String s21301 = "21301";
+  public static final String s21302 = "21302";
+  public static final String s21303 = "21303";
+  public static final String s21304 = "21304";
+  public static final String s21305 = "21305";
+  public static final String s21306 = "21306";
+  public static final String s21307 = "21307";
+  public static final String s21308 = "21308";
+  public static final String s21309 = "21309";
+  public static final String s21310 = "21310";
+  public static final String s21311 = "21311";
+  public static final String s21312 = "21312";
+  public static final String s21313 = "21313";
+  public static final String s21314 = "21314";
+  public static final String s21315 = "21315";
+  public static final String s21316 = "21316";
+  public static final String s21317 = "21317";
+  public static final String s21318 = "21318";
+  public static final String s21319 = "21319";
+  public static final String s21320 = "21320";
+  public static final String s21321 = "21321";
+  public static final String s21322 = "21322";
+  public static final String s21323 = "21323";
+  public static final String s21324 = "21324";
+  public static final String s21325 = "21325";
+  public static final String s21326 = "21326";
+  public static final String s21327 = "21327";
+  public static final String s21328 = "21328";
+  public static final String s21329 = "21329";
+  public static final String s21330 = "21330";
+  public static final String s21331 = "21331";
+  public static final String s21332 = "21332";
+  public static final String s21333 = "21333";
+  public static final String s21334 = "21334";
+  public static final String s21335 = "21335";
+  public static final String s21336 = "21336";
+  public static final String s21337 = "21337";
+  public static final String s21338 = "21338";
+  public static final String s21339 = "21339";
+  public static final String s21340 = "21340";
+  public static final String s21341 = "21341";
+  public static final String s21342 = "21342";
+  public static final String s21343 = "21343";
+  public static final String s21344 = "21344";
+  public static final String s21345 = "21345";
+  public static final String s21346 = "21346";
+  public static final String s21347 = "21347";
+  public static final String s21348 = "21348";
+  public static final String s21349 = "21349";
+  public static final String s21350 = "21350";
+  public static final String s21351 = "21351";
+  public static final String s21352 = "21352";
+  public static final String s21353 = "21353";
+  public static final String s21354 = "21354";
+  public static final String s21355 = "21355";
+  public static final String s21356 = "21356";
+  public static final String s21357 = "21357";
+  public static final String s21358 = "21358";
+  public static final String s21359 = "21359";
+  public static final String s21360 = "21360";
+  public static final String s21361 = "21361";
+  public static final String s21362 = "21362";
+  public static final String s21363 = "21363";
+  public static final String s21364 = "21364";
+  public static final String s21365 = "21365";
+  public static final String s21366 = "21366";
+  public static final String s21367 = "21367";
+  public static final String s21368 = "21368";
+  public static final String s21369 = "21369";
+  public static final String s21370 = "21370";
+  public static final String s21371 = "21371";
+  public static final String s21372 = "21372";
+  public static final String s21373 = "21373";
+  public static final String s21374 = "21374";
+  public static final String s21375 = "21375";
+  public static final String s21376 = "21376";
+  public static final String s21377 = "21377";
+  public static final String s21378 = "21378";
+  public static final String s21379 = "21379";
+  public static final String s21380 = "21380";
+  public static final String s21381 = "21381";
+  public static final String s21382 = "21382";
+  public static final String s21383 = "21383";
+  public static final String s21384 = "21384";
+  public static final String s21385 = "21385";
+  public static final String s21386 = "21386";
+  public static final String s21387 = "21387";
+  public static final String s21388 = "21388";
+  public static final String s21389 = "21389";
+  public static final String s21390 = "21390";
+  public static final String s21391 = "21391";
+  public static final String s21392 = "21392";
+  public static final String s21393 = "21393";
+  public static final String s21394 = "21394";
+  public static final String s21395 = "21395";
+  public static final String s21396 = "21396";
+  public static final String s21397 = "21397";
+  public static final String s21398 = "21398";
+  public static final String s21399 = "21399";
+  public static final String s21400 = "21400";
+  public static final String s21401 = "21401";
+  public static final String s21402 = "21402";
+  public static final String s21403 = "21403";
+  public static final String s21404 = "21404";
+  public static final String s21405 = "21405";
+  public static final String s21406 = "21406";
+  public static final String s21407 = "21407";
+  public static final String s21408 = "21408";
+  public static final String s21409 = "21409";
+  public static final String s21410 = "21410";
+  public static final String s21411 = "21411";
+  public static final String s21412 = "21412";
+  public static final String s21413 = "21413";
+  public static final String s21414 = "21414";
+  public static final String s21415 = "21415";
+  public static final String s21416 = "21416";
+  public static final String s21417 = "21417";
+  public static final String s21418 = "21418";
+  public static final String s21419 = "21419";
+  public static final String s21420 = "21420";
+  public static final String s21421 = "21421";
+  public static final String s21422 = "21422";
+  public static final String s21423 = "21423";
+  public static final String s21424 = "21424";
+  public static final String s21425 = "21425";
+  public static final String s21426 = "21426";
+  public static final String s21427 = "21427";
+  public static final String s21428 = "21428";
+  public static final String s21429 = "21429";
+  public static final String s21430 = "21430";
+  public static final String s21431 = "21431";
+  public static final String s21432 = "21432";
+  public static final String s21433 = "21433";
+  public static final String s21434 = "21434";
+  public static final String s21435 = "21435";
+  public static final String s21436 = "21436";
+  public static final String s21437 = "21437";
+  public static final String s21438 = "21438";
+  public static final String s21439 = "21439";
+  public static final String s21440 = "21440";
+  public static final String s21441 = "21441";
+  public static final String s21442 = "21442";
+  public static final String s21443 = "21443";
+  public static final String s21444 = "21444";
+  public static final String s21445 = "21445";
+  public static final String s21446 = "21446";
+  public static final String s21447 = "21447";
+  public static final String s21448 = "21448";
+  public static final String s21449 = "21449";
+  public static final String s21450 = "21450";
+  public static final String s21451 = "21451";
+  public static final String s21452 = "21452";
+  public static final String s21453 = "21453";
+  public static final String s21454 = "21454";
+  public static final String s21455 = "21455";
+  public static final String s21456 = "21456";
+  public static final String s21457 = "21457";
+  public static final String s21458 = "21458";
+  public static final String s21459 = "21459";
+  public static final String s21460 = "21460";
+  public static final String s21461 = "21461";
+  public static final String s21462 = "21462";
+  public static final String s21463 = "21463";
+  public static final String s21464 = "21464";
+  public static final String s21465 = "21465";
+  public static final String s21466 = "21466";
+  public static final String s21467 = "21467";
+  public static final String s21468 = "21468";
+  public static final String s21469 = "21469";
+  public static final String s21470 = "21470";
+  public static final String s21471 = "21471";
+  public static final String s21472 = "21472";
+  public static final String s21473 = "21473";
+  public static final String s21474 = "21474";
+  public static final String s21475 = "21475";
+  public static final String s21476 = "21476";
+  public static final String s21477 = "21477";
+  public static final String s21478 = "21478";
+  public static final String s21479 = "21479";
+  public static final String s21480 = "21480";
+  public static final String s21481 = "21481";
+  public static final String s21482 = "21482";
+  public static final String s21483 = "21483";
+  public static final String s21484 = "21484";
+  public static final String s21485 = "21485";
+  public static final String s21486 = "21486";
+  public static final String s21487 = "21487";
+  public static final String s21488 = "21488";
+  public static final String s21489 = "21489";
+  public static final String s21490 = "21490";
+  public static final String s21491 = "21491";
+  public static final String s21492 = "21492";
+  public static final String s21493 = "21493";
+  public static final String s21494 = "21494";
+  public static final String s21495 = "21495";
+  public static final String s21496 = "21496";
+  public static final String s21497 = "21497";
+  public static final String s21498 = "21498";
+  public static final String s21499 = "21499";
+  public static final String s21500 = "21500";
+  public static final String s21501 = "21501";
+  public static final String s21502 = "21502";
+  public static final String s21503 = "21503";
+  public static final String s21504 = "21504";
+  public static final String s21505 = "21505";
+  public static final String s21506 = "21506";
+  public static final String s21507 = "21507";
+  public static final String s21508 = "21508";
+  public static final String s21509 = "21509";
+  public static final String s21510 = "21510";
+  public static final String s21511 = "21511";
+  public static final String s21512 = "21512";
+  public static final String s21513 = "21513";
+  public static final String s21514 = "21514";
+  public static final String s21515 = "21515";
+  public static final String s21516 = "21516";
+  public static final String s21517 = "21517";
+  public static final String s21518 = "21518";
+  public static final String s21519 = "21519";
+  public static final String s21520 = "21520";
+  public static final String s21521 = "21521";
+  public static final String s21522 = "21522";
+  public static final String s21523 = "21523";
+  public static final String s21524 = "21524";
+  public static final String s21525 = "21525";
+  public static final String s21526 = "21526";
+  public static final String s21527 = "21527";
+  public static final String s21528 = "21528";
+  public static final String s21529 = "21529";
+  public static final String s21530 = "21530";
+  public static final String s21531 = "21531";
+  public static final String s21532 = "21532";
+  public static final String s21533 = "21533";
+  public static final String s21534 = "21534";
+  public static final String s21535 = "21535";
+  public static final String s21536 = "21536";
+  public static final String s21537 = "21537";
+  public static final String s21538 = "21538";
+  public static final String s21539 = "21539";
+  public static final String s21540 = "21540";
+  public static final String s21541 = "21541";
+  public static final String s21542 = "21542";
+  public static final String s21543 = "21543";
+  public static final String s21544 = "21544";
+  public static final String s21545 = "21545";
+  public static final String s21546 = "21546";
+  public static final String s21547 = "21547";
+  public static final String s21548 = "21548";
+  public static final String s21549 = "21549";
+  public static final String s21550 = "21550";
+  public static final String s21551 = "21551";
+  public static final String s21552 = "21552";
+  public static final String s21553 = "21553";
+  public static final String s21554 = "21554";
+  public static final String s21555 = "21555";
+  public static final String s21556 = "21556";
+  public static final String s21557 = "21557";
+  public static final String s21558 = "21558";
+  public static final String s21559 = "21559";
+  public static final String s21560 = "21560";
+  public static final String s21561 = "21561";
+  public static final String s21562 = "21562";
+  public static final String s21563 = "21563";
+  public static final String s21564 = "21564";
+  public static final String s21565 = "21565";
+  public static final String s21566 = "21566";
+  public static final String s21567 = "21567";
+  public static final String s21568 = "21568";
+  public static final String s21569 = "21569";
+  public static final String s21570 = "21570";
+  public static final String s21571 = "21571";
+  public static final String s21572 = "21572";
+  public static final String s21573 = "21573";
+  public static final String s21574 = "21574";
+  public static final String s21575 = "21575";
+  public static final String s21576 = "21576";
+  public static final String s21577 = "21577";
+  public static final String s21578 = "21578";
+  public static final String s21579 = "21579";
+  public static final String s21580 = "21580";
+  public static final String s21581 = "21581";
+  public static final String s21582 = "21582";
+  public static final String s21583 = "21583";
+  public static final String s21584 = "21584";
+  public static final String s21585 = "21585";
+  public static final String s21586 = "21586";
+  public static final String s21587 = "21587";
+  public static final String s21588 = "21588";
+  public static final String s21589 = "21589";
+  public static final String s21590 = "21590";
+  public static final String s21591 = "21591";
+  public static final String s21592 = "21592";
+  public static final String s21593 = "21593";
+  public static final String s21594 = "21594";
+  public static final String s21595 = "21595";
+  public static final String s21596 = "21596";
+  public static final String s21597 = "21597";
+  public static final String s21598 = "21598";
+  public static final String s21599 = "21599";
+  public static final String s21600 = "21600";
+  public static final String s21601 = "21601";
+  public static final String s21602 = "21602";
+  public static final String s21603 = "21603";
+  public static final String s21604 = "21604";
+  public static final String s21605 = "21605";
+  public static final String s21606 = "21606";
+  public static final String s21607 = "21607";
+  public static final String s21608 = "21608";
+  public static final String s21609 = "21609";
+  public static final String s21610 = "21610";
+  public static final String s21611 = "21611";
+  public static final String s21612 = "21612";
+  public static final String s21613 = "21613";
+  public static final String s21614 = "21614";
+  public static final String s21615 = "21615";
+  public static final String s21616 = "21616";
+  public static final String s21617 = "21617";
+  public static final String s21618 = "21618";
+  public static final String s21619 = "21619";
+  public static final String s21620 = "21620";
+  public static final String s21621 = "21621";
+  public static final String s21622 = "21622";
+  public static final String s21623 = "21623";
+  public static final String s21624 = "21624";
+  public static final String s21625 = "21625";
+  public static final String s21626 = "21626";
+  public static final String s21627 = "21627";
+  public static final String s21628 = "21628";
+  public static final String s21629 = "21629";
+  public static final String s21630 = "21630";
+  public static final String s21631 = "21631";
+  public static final String s21632 = "21632";
+  public static final String s21633 = "21633";
+  public static final String s21634 = "21634";
+  public static final String s21635 = "21635";
+  public static final String s21636 = "21636";
+  public static final String s21637 = "21637";
+  public static final String s21638 = "21638";
+  public static final String s21639 = "21639";
+  public static final String s21640 = "21640";
+  public static final String s21641 = "21641";
+  public static final String s21642 = "21642";
+  public static final String s21643 = "21643";
+  public static final String s21644 = "21644";
+  public static final String s21645 = "21645";
+  public static final String s21646 = "21646";
+  public static final String s21647 = "21647";
+  public static final String s21648 = "21648";
+  public static final String s21649 = "21649";
+  public static final String s21650 = "21650";
+  public static final String s21651 = "21651";
+  public static final String s21652 = "21652";
+  public static final String s21653 = "21653";
+  public static final String s21654 = "21654";
+  public static final String s21655 = "21655";
+  public static final String s21656 = "21656";
+  public static final String s21657 = "21657";
+  public static final String s21658 = "21658";
+  public static final String s21659 = "21659";
+  public static final String s21660 = "21660";
+  public static final String s21661 = "21661";
+  public static final String s21662 = "21662";
+  public static final String s21663 = "21663";
+  public static final String s21664 = "21664";
+  public static final String s21665 = "21665";
+  public static final String s21666 = "21666";
+  public static final String s21667 = "21667";
+  public static final String s21668 = "21668";
+  public static final String s21669 = "21669";
+  public static final String s21670 = "21670";
+  public static final String s21671 = "21671";
+  public static final String s21672 = "21672";
+  public static final String s21673 = "21673";
+  public static final String s21674 = "21674";
+  public static final String s21675 = "21675";
+  public static final String s21676 = "21676";
+  public static final String s21677 = "21677";
+  public static final String s21678 = "21678";
+  public static final String s21679 = "21679";
+  public static final String s21680 = "21680";
+  public static final String s21681 = "21681";
+  public static final String s21682 = "21682";
+  public static final String s21683 = "21683";
+  public static final String s21684 = "21684";
+  public static final String s21685 = "21685";
+  public static final String s21686 = "21686";
+  public static final String s21687 = "21687";
+  public static final String s21688 = "21688";
+  public static final String s21689 = "21689";
+  public static final String s21690 = "21690";
+  public static final String s21691 = "21691";
+  public static final String s21692 = "21692";
+  public static final String s21693 = "21693";
+  public static final String s21694 = "21694";
+  public static final String s21695 = "21695";
+  public static final String s21696 = "21696";
+  public static final String s21697 = "21697";
+  public static final String s21698 = "21698";
+  public static final String s21699 = "21699";
+  public static final String s21700 = "21700";
+  public static final String s21701 = "21701";
+  public static final String s21702 = "21702";
+  public static final String s21703 = "21703";
+  public static final String s21704 = "21704";
+  public static final String s21705 = "21705";
+  public static final String s21706 = "21706";
+  public static final String s21707 = "21707";
+  public static final String s21708 = "21708";
+  public static final String s21709 = "21709";
+  public static final String s21710 = "21710";
+  public static final String s21711 = "21711";
+  public static final String s21712 = "21712";
+  public static final String s21713 = "21713";
+  public static final String s21714 = "21714";
+  public static final String s21715 = "21715";
+  public static final String s21716 = "21716";
+  public static final String s21717 = "21717";
+  public static final String s21718 = "21718";
+  public static final String s21719 = "21719";
+  public static final String s21720 = "21720";
+  public static final String s21721 = "21721";
+  public static final String s21722 = "21722";
+  public static final String s21723 = "21723";
+  public static final String s21724 = "21724";
+  public static final String s21725 = "21725";
+  public static final String s21726 = "21726";
+  public static final String s21727 = "21727";
+  public static final String s21728 = "21728";
+  public static final String s21729 = "21729";
+  public static final String s21730 = "21730";
+  public static final String s21731 = "21731";
+  public static final String s21732 = "21732";
+  public static final String s21733 = "21733";
+  public static final String s21734 = "21734";
+  public static final String s21735 = "21735";
+  public static final String s21736 = "21736";
+  public static final String s21737 = "21737";
+  public static final String s21738 = "21738";
+  public static final String s21739 = "21739";
+  public static final String s21740 = "21740";
+  public static final String s21741 = "21741";
+  public static final String s21742 = "21742";
+  public static final String s21743 = "21743";
+  public static final String s21744 = "21744";
+  public static final String s21745 = "21745";
+  public static final String s21746 = "21746";
+  public static final String s21747 = "21747";
+  public static final String s21748 = "21748";
+  public static final String s21749 = "21749";
+  public static final String s21750 = "21750";
+  public static final String s21751 = "21751";
+  public static final String s21752 = "21752";
+  public static final String s21753 = "21753";
+  public static final String s21754 = "21754";
+  public static final String s21755 = "21755";
+  public static final String s21756 = "21756";
+  public static final String s21757 = "21757";
+  public static final String s21758 = "21758";
+  public static final String s21759 = "21759";
+  public static final String s21760 = "21760";
+  public static final String s21761 = "21761";
+  public static final String s21762 = "21762";
+  public static final String s21763 = "21763";
+  public static final String s21764 = "21764";
+  public static final String s21765 = "21765";
+  public static final String s21766 = "21766";
+  public static final String s21767 = "21767";
+  public static final String s21768 = "21768";
+  public static final String s21769 = "21769";
+  public static final String s21770 = "21770";
+  public static final String s21771 = "21771";
+  public static final String s21772 = "21772";
+  public static final String s21773 = "21773";
+  public static final String s21774 = "21774";
+  public static final String s21775 = "21775";
+  public static final String s21776 = "21776";
+  public static final String s21777 = "21777";
+  public static final String s21778 = "21778";
+  public static final String s21779 = "21779";
+  public static final String s21780 = "21780";
+  public static final String s21781 = "21781";
+  public static final String s21782 = "21782";
+  public static final String s21783 = "21783";
+  public static final String s21784 = "21784";
+  public static final String s21785 = "21785";
+  public static final String s21786 = "21786";
+  public static final String s21787 = "21787";
+  public static final String s21788 = "21788";
+  public static final String s21789 = "21789";
+  public static final String s21790 = "21790";
+  public static final String s21791 = "21791";
+  public static final String s21792 = "21792";
+  public static final String s21793 = "21793";
+  public static final String s21794 = "21794";
+  public static final String s21795 = "21795";
+  public static final String s21796 = "21796";
+  public static final String s21797 = "21797";
+  public static final String s21798 = "21798";
+  public static final String s21799 = "21799";
+  public static final String s21800 = "21800";
+  public static final String s21801 = "21801";
+  public static final String s21802 = "21802";
+  public static final String s21803 = "21803";
+  public static final String s21804 = "21804";
+  public static final String s21805 = "21805";
+  public static final String s21806 = "21806";
+  public static final String s21807 = "21807";
+  public static final String s21808 = "21808";
+  public static final String s21809 = "21809";
+  public static final String s21810 = "21810";
+  public static final String s21811 = "21811";
+  public static final String s21812 = "21812";
+  public static final String s21813 = "21813";
+  public static final String s21814 = "21814";
+  public static final String s21815 = "21815";
+  public static final String s21816 = "21816";
+  public static final String s21817 = "21817";
+  public static final String s21818 = "21818";
+  public static final String s21819 = "21819";
+  public static final String s21820 = "21820";
+  public static final String s21821 = "21821";
+  public static final String s21822 = "21822";
+  public static final String s21823 = "21823";
+  public static final String s21824 = "21824";
+  public static final String s21825 = "21825";
+  public static final String s21826 = "21826";
+  public static final String s21827 = "21827";
+  public static final String s21828 = "21828";
+  public static final String s21829 = "21829";
+  public static final String s21830 = "21830";
+  public static final String s21831 = "21831";
+  public static final String s21832 = "21832";
+  public static final String s21833 = "21833";
+  public static final String s21834 = "21834";
+  public static final String s21835 = "21835";
+  public static final String s21836 = "21836";
+  public static final String s21837 = "21837";
+  public static final String s21838 = "21838";
+  public static final String s21839 = "21839";
+  public static final String s21840 = "21840";
+  public static final String s21841 = "21841";
+  public static final String s21842 = "21842";
+  public static final String s21843 = "21843";
+  public static final String s21844 = "21844";
+  public static final String s21845 = "21845";
+  public static final String s21846 = "21846";
+  public static final String s21847 = "21847";
+  public static final String s21848 = "21848";
+  public static final String s21849 = "21849";
+  public static final String s21850 = "21850";
+  public static final String s21851 = "21851";
+  public static final String s21852 = "21852";
+  public static final String s21853 = "21853";
+  public static final String s21854 = "21854";
+  public static final String s21855 = "21855";
+  public static final String s21856 = "21856";
+  public static final String s21857 = "21857";
+  public static final String s21858 = "21858";
+  public static final String s21859 = "21859";
+  public static final String s21860 = "21860";
+  public static final String s21861 = "21861";
+  public static final String s21862 = "21862";
+  public static final String s21863 = "21863";
+  public static final String s21864 = "21864";
+  public static final String s21865 = "21865";
+  public static final String s21866 = "21866";
+  public static final String s21867 = "21867";
+  public static final String s21868 = "21868";
+  public static final String s21869 = "21869";
+  public static final String s21870 = "21870";
+  public static final String s21871 = "21871";
+  public static final String s21872 = "21872";
+  public static final String s21873 = "21873";
+  public static final String s21874 = "21874";
+  public static final String s21875 = "21875";
+  public static final String s21876 = "21876";
+  public static final String s21877 = "21877";
+  public static final String s21878 = "21878";
+  public static final String s21879 = "21879";
+  public static final String s21880 = "21880";
+  public static final String s21881 = "21881";
+  public static final String s21882 = "21882";
+  public static final String s21883 = "21883";
+  public static final String s21884 = "21884";
+  public static final String s21885 = "21885";
+  public static final String s21886 = "21886";
+  public static final String s21887 = "21887";
+  public static final String s21888 = "21888";
+  public static final String s21889 = "21889";
+  public static final String s21890 = "21890";
+  public static final String s21891 = "21891";
+  public static final String s21892 = "21892";
+  public static final String s21893 = "21893";
+  public static final String s21894 = "21894";
+  public static final String s21895 = "21895";
+  public static final String s21896 = "21896";
+  public static final String s21897 = "21897";
+  public static final String s21898 = "21898";
+  public static final String s21899 = "21899";
+  public static final String s21900 = "21900";
+  public static final String s21901 = "21901";
+  public static final String s21902 = "21902";
+  public static final String s21903 = "21903";
+  public static final String s21904 = "21904";
+  public static final String s21905 = "21905";
+  public static final String s21906 = "21906";
+  public static final String s21907 = "21907";
+  public static final String s21908 = "21908";
+  public static final String s21909 = "21909";
+  public static final String s21910 = "21910";
+  public static final String s21911 = "21911";
+  public static final String s21912 = "21912";
+  public static final String s21913 = "21913";
+  public static final String s21914 = "21914";
+  public static final String s21915 = "21915";
+  public static final String s21916 = "21916";
+  public static final String s21917 = "21917";
+  public static final String s21918 = "21918";
+  public static final String s21919 = "21919";
+  public static final String s21920 = "21920";
+  public static final String s21921 = "21921";
+  public static final String s21922 = "21922";
+  public static final String s21923 = "21923";
+  public static final String s21924 = "21924";
+  public static final String s21925 = "21925";
+  public static final String s21926 = "21926";
+  public static final String s21927 = "21927";
+  public static final String s21928 = "21928";
+  public static final String s21929 = "21929";
+  public static final String s21930 = "21930";
+  public static final String s21931 = "21931";
+  public static final String s21932 = "21932";
+  public static final String s21933 = "21933";
+  public static final String s21934 = "21934";
+  public static final String s21935 = "21935";
+  public static final String s21936 = "21936";
+  public static final String s21937 = "21937";
+  public static final String s21938 = "21938";
+  public static final String s21939 = "21939";
+  public static final String s21940 = "21940";
+  public static final String s21941 = "21941";
+  public static final String s21942 = "21942";
+  public static final String s21943 = "21943";
+  public static final String s21944 = "21944";
+  public static final String s21945 = "21945";
+  public static final String s21946 = "21946";
+  public static final String s21947 = "21947";
+  public static final String s21948 = "21948";
+  public static final String s21949 = "21949";
+  public static final String s21950 = "21950";
+  public static final String s21951 = "21951";
+  public static final String s21952 = "21952";
+  public static final String s21953 = "21953";
+  public static final String s21954 = "21954";
+  public static final String s21955 = "21955";
+  public static final String s21956 = "21956";
+  public static final String s21957 = "21957";
+  public static final String s21958 = "21958";
+  public static final String s21959 = "21959";
+  public static final String s21960 = "21960";
+  public static final String s21961 = "21961";
+  public static final String s21962 = "21962";
+  public static final String s21963 = "21963";
+  public static final String s21964 = "21964";
+  public static final String s21965 = "21965";
+  public static final String s21966 = "21966";
+  public static final String s21967 = "21967";
+  public static final String s21968 = "21968";
+  public static final String s21969 = "21969";
+  public static final String s21970 = "21970";
+  public static final String s21971 = "21971";
+  public static final String s21972 = "21972";
+  public static final String s21973 = "21973";
+  public static final String s21974 = "21974";
+  public static final String s21975 = "21975";
+  public static final String s21976 = "21976";
+  public static final String s21977 = "21977";
+  public static final String s21978 = "21978";
+  public static final String s21979 = "21979";
+  public static final String s21980 = "21980";
+  public static final String s21981 = "21981";
+  public static final String s21982 = "21982";
+  public static final String s21983 = "21983";
+  public static final String s21984 = "21984";
+  public static final String s21985 = "21985";
+  public static final String s21986 = "21986";
+  public static final String s21987 = "21987";
+  public static final String s21988 = "21988";
+  public static final String s21989 = "21989";
+  public static final String s21990 = "21990";
+  public static final String s21991 = "21991";
+  public static final String s21992 = "21992";
+  public static final String s21993 = "21993";
+  public static final String s21994 = "21994";
+  public static final String s21995 = "21995";
+  public static final String s21996 = "21996";
+  public static final String s21997 = "21997";
+  public static final String s21998 = "21998";
+  public static final String s21999 = "21999";
+  public static final String s22000 = "22000";
+  public static final String s22001 = "22001";
+  public static final String s22002 = "22002";
+  public static final String s22003 = "22003";
+  public static final String s22004 = "22004";
+  public static final String s22005 = "22005";
+  public static final String s22006 = "22006";
+  public static final String s22007 = "22007";
+  public static final String s22008 = "22008";
+  public static final String s22009 = "22009";
+  public static final String s22010 = "22010";
+  public static final String s22011 = "22011";
+  public static final String s22012 = "22012";
+  public static final String s22013 = "22013";
+  public static final String s22014 = "22014";
+  public static final String s22015 = "22015";
+  public static final String s22016 = "22016";
+  public static final String s22017 = "22017";
+  public static final String s22018 = "22018";
+  public static final String s22019 = "22019";
+  public static final String s22020 = "22020";
+  public static final String s22021 = "22021";
+  public static final String s22022 = "22022";
+  public static final String s22023 = "22023";
+  public static final String s22024 = "22024";
+  public static final String s22025 = "22025";
+  public static final String s22026 = "22026";
+  public static final String s22027 = "22027";
+  public static final String s22028 = "22028";
+  public static final String s22029 = "22029";
+  public static final String s22030 = "22030";
+  public static final String s22031 = "22031";
+  public static final String s22032 = "22032";
+  public static final String s22033 = "22033";
+  public static final String s22034 = "22034";
+  public static final String s22035 = "22035";
+  public static final String s22036 = "22036";
+  public static final String s22037 = "22037";
+  public static final String s22038 = "22038";
+  public static final String s22039 = "22039";
+  public static final String s22040 = "22040";
+  public static final String s22041 = "22041";
+  public static final String s22042 = "22042";
+  public static final String s22043 = "22043";
+  public static final String s22044 = "22044";
+  public static final String s22045 = "22045";
+  public static final String s22046 = "22046";
+  public static final String s22047 = "22047";
+  public static final String s22048 = "22048";
+  public static final String s22049 = "22049";
+  public static final String s22050 = "22050";
+  public static final String s22051 = "22051";
+  public static final String s22052 = "22052";
+  public static final String s22053 = "22053";
+  public static final String s22054 = "22054";
+  public static final String s22055 = "22055";
+  public static final String s22056 = "22056";
+  public static final String s22057 = "22057";
+  public static final String s22058 = "22058";
+  public static final String s22059 = "22059";
+  public static final String s22060 = "22060";
+  public static final String s22061 = "22061";
+  public static final String s22062 = "22062";
+  public static final String s22063 = "22063";
+  public static final String s22064 = "22064";
+  public static final String s22065 = "22065";
+  public static final String s22066 = "22066";
+  public static final String s22067 = "22067";
+  public static final String s22068 = "22068";
+  public static final String s22069 = "22069";
+  public static final String s22070 = "22070";
+  public static final String s22071 = "22071";
+  public static final String s22072 = "22072";
+  public static final String s22073 = "22073";
+  public static final String s22074 = "22074";
+  public static final String s22075 = "22075";
+  public static final String s22076 = "22076";
+  public static final String s22077 = "22077";
+  public static final String s22078 = "22078";
+  public static final String s22079 = "22079";
+  public static final String s22080 = "22080";
+  public static final String s22081 = "22081";
+  public static final String s22082 = "22082";
+  public static final String s22083 = "22083";
+  public static final String s22084 = "22084";
+  public static final String s22085 = "22085";
+  public static final String s22086 = "22086";
+  public static final String s22087 = "22087";
+  public static final String s22088 = "22088";
+  public static final String s22089 = "22089";
+  public static final String s22090 = "22090";
+  public static final String s22091 = "22091";
+  public static final String s22092 = "22092";
+  public static final String s22093 = "22093";
+  public static final String s22094 = "22094";
+  public static final String s22095 = "22095";
+  public static final String s22096 = "22096";
+  public static final String s22097 = "22097";
+  public static final String s22098 = "22098";
+  public static final String s22099 = "22099";
+  public static final String s22100 = "22100";
+  public static final String s22101 = "22101";
+  public static final String s22102 = "22102";
+  public static final String s22103 = "22103";
+  public static final String s22104 = "22104";
+  public static final String s22105 = "22105";
+  public static final String s22106 = "22106";
+  public static final String s22107 = "22107";
+  public static final String s22108 = "22108";
+  public static final String s22109 = "22109";
+  public static final String s22110 = "22110";
+  public static final String s22111 = "22111";
+  public static final String s22112 = "22112";
+  public static final String s22113 = "22113";
+  public static final String s22114 = "22114";
+  public static final String s22115 = "22115";
+  public static final String s22116 = "22116";
+  public static final String s22117 = "22117";
+  public static final String s22118 = "22118";
+  public static final String s22119 = "22119";
+  public static final String s22120 = "22120";
+  public static final String s22121 = "22121";
+  public static final String s22122 = "22122";
+  public static final String s22123 = "22123";
+  public static final String s22124 = "22124";
+  public static final String s22125 = "22125";
+  public static final String s22126 = "22126";
+  public static final String s22127 = "22127";
+  public static final String s22128 = "22128";
+  public static final String s22129 = "22129";
+  public static final String s22130 = "22130";
+  public static final String s22131 = "22131";
+  public static final String s22132 = "22132";
+  public static final String s22133 = "22133";
+  public static final String s22134 = "22134";
+  public static final String s22135 = "22135";
+  public static final String s22136 = "22136";
+  public static final String s22137 = "22137";
+  public static final String s22138 = "22138";
+  public static final String s22139 = "22139";
+  public static final String s22140 = "22140";
+  public static final String s22141 = "22141";
+  public static final String s22142 = "22142";
+  public static final String s22143 = "22143";
+  public static final String s22144 = "22144";
+  public static final String s22145 = "22145";
+  public static final String s22146 = "22146";
+  public static final String s22147 = "22147";
+  public static final String s22148 = "22148";
+  public static final String s22149 = "22149";
+  public static final String s22150 = "22150";
+  public static final String s22151 = "22151";
+  public static final String s22152 = "22152";
+  public static final String s22153 = "22153";
+  public static final String s22154 = "22154";
+  public static final String s22155 = "22155";
+  public static final String s22156 = "22156";
+  public static final String s22157 = "22157";
+  public static final String s22158 = "22158";
+  public static final String s22159 = "22159";
+  public static final String s22160 = "22160";
+  public static final String s22161 = "22161";
+  public static final String s22162 = "22162";
+  public static final String s22163 = "22163";
+  public static final String s22164 = "22164";
+  public static final String s22165 = "22165";
+  public static final String s22166 = "22166";
+  public static final String s22167 = "22167";
+  public static final String s22168 = "22168";
+  public static final String s22169 = "22169";
+  public static final String s22170 = "22170";
+  public static final String s22171 = "22171";
+  public static final String s22172 = "22172";
+  public static final String s22173 = "22173";
+  public static final String s22174 = "22174";
+  public static final String s22175 = "22175";
+  public static final String s22176 = "22176";
+  public static final String s22177 = "22177";
+  public static final String s22178 = "22178";
+  public static final String s22179 = "22179";
+  public static final String s22180 = "22180";
+  public static final String s22181 = "22181";
+  public static final String s22182 = "22182";
+  public static final String s22183 = "22183";
+  public static final String s22184 = "22184";
+  public static final String s22185 = "22185";
+  public static final String s22186 = "22186";
+  public static final String s22187 = "22187";
+  public static final String s22188 = "22188";
+  public static final String s22189 = "22189";
+  public static final String s22190 = "22190";
+  public static final String s22191 = "22191";
+  public static final String s22192 = "22192";
+  public static final String s22193 = "22193";
+  public static final String s22194 = "22194";
+  public static final String s22195 = "22195";
+  public static final String s22196 = "22196";
+  public static final String s22197 = "22197";
+  public static final String s22198 = "22198";
+  public static final String s22199 = "22199";
+  public static final String s22200 = "22200";
+  public static final String s22201 = "22201";
+  public static final String s22202 = "22202";
+  public static final String s22203 = "22203";
+  public static final String s22204 = "22204";
+  public static final String s22205 = "22205";
+  public static final String s22206 = "22206";
+  public static final String s22207 = "22207";
+  public static final String s22208 = "22208";
+  public static final String s22209 = "22209";
+  public static final String s22210 = "22210";
+  public static final String s22211 = "22211";
+  public static final String s22212 = "22212";
+  public static final String s22213 = "22213";
+  public static final String s22214 = "22214";
+  public static final String s22215 = "22215";
+  public static final String s22216 = "22216";
+  public static final String s22217 = "22217";
+  public static final String s22218 = "22218";
+  public static final String s22219 = "22219";
+  public static final String s22220 = "22220";
+  public static final String s22221 = "22221";
+  public static final String s22222 = "22222";
+  public static final String s22223 = "22223";
+  public static final String s22224 = "22224";
+  public static final String s22225 = "22225";
+  public static final String s22226 = "22226";
+  public static final String s22227 = "22227";
+  public static final String s22228 = "22228";
+  public static final String s22229 = "22229";
+  public static final String s22230 = "22230";
+  public static final String s22231 = "22231";
+  public static final String s22232 = "22232";
+  public static final String s22233 = "22233";
+  public static final String s22234 = "22234";
+  public static final String s22235 = "22235";
+  public static final String s22236 = "22236";
+  public static final String s22237 = "22237";
+  public static final String s22238 = "22238";
+  public static final String s22239 = "22239";
+  public static final String s22240 = "22240";
+  public static final String s22241 = "22241";
+  public static final String s22242 = "22242";
+  public static final String s22243 = "22243";
+  public static final String s22244 = "22244";
+  public static final String s22245 = "22245";
+  public static final String s22246 = "22246";
+  public static final String s22247 = "22247";
+  public static final String s22248 = "22248";
+  public static final String s22249 = "22249";
+  public static final String s22250 = "22250";
+  public static final String s22251 = "22251";
+  public static final String s22252 = "22252";
+  public static final String s22253 = "22253";
+  public static final String s22254 = "22254";
+  public static final String s22255 = "22255";
+  public static final String s22256 = "22256";
+  public static final String s22257 = "22257";
+  public static final String s22258 = "22258";
+  public static final String s22259 = "22259";
+  public static final String s22260 = "22260";
+  public static final String s22261 = "22261";
+  public static final String s22262 = "22262";
+  public static final String s22263 = "22263";
+  public static final String s22264 = "22264";
+  public static final String s22265 = "22265";
+  public static final String s22266 = "22266";
+  public static final String s22267 = "22267";
+  public static final String s22268 = "22268";
+  public static final String s22269 = "22269";
+  public static final String s22270 = "22270";
+  public static final String s22271 = "22271";
+  public static final String s22272 = "22272";
+  public static final String s22273 = "22273";
+  public static final String s22274 = "22274";
+  public static final String s22275 = "22275";
+  public static final String s22276 = "22276";
+  public static final String s22277 = "22277";
+  public static final String s22278 = "22278";
+  public static final String s22279 = "22279";
+  public static final String s22280 = "22280";
+  public static final String s22281 = "22281";
+  public static final String s22282 = "22282";
+  public static final String s22283 = "22283";
+  public static final String s22284 = "22284";
+  public static final String s22285 = "22285";
+  public static final String s22286 = "22286";
+  public static final String s22287 = "22287";
+  public static final String s22288 = "22288";
+  public static final String s22289 = "22289";
+  public static final String s22290 = "22290";
+  public static final String s22291 = "22291";
+  public static final String s22292 = "22292";
+  public static final String s22293 = "22293";
+  public static final String s22294 = "22294";
+  public static final String s22295 = "22295";
+  public static final String s22296 = "22296";
+  public static final String s22297 = "22297";
+  public static final String s22298 = "22298";
+  public static final String s22299 = "22299";
+  public static final String s22300 = "22300";
+  public static final String s22301 = "22301";
+  public static final String s22302 = "22302";
+  public static final String s22303 = "22303";
+  public static final String s22304 = "22304";
+  public static final String s22305 = "22305";
+  public static final String s22306 = "22306";
+  public static final String s22307 = "22307";
+  public static final String s22308 = "22308";
+  public static final String s22309 = "22309";
+  public static final String s22310 = "22310";
+  public static final String s22311 = "22311";
+  public static final String s22312 = "22312";
+  public static final String s22313 = "22313";
+  public static final String s22314 = "22314";
+  public static final String s22315 = "22315";
+  public static final String s22316 = "22316";
+  public static final String s22317 = "22317";
+  public static final String s22318 = "22318";
+  public static final String s22319 = "22319";
+  public static final String s22320 = "22320";
+  public static final String s22321 = "22321";
+  public static final String s22322 = "22322";
+  public static final String s22323 = "22323";
+  public static final String s22324 = "22324";
+  public static final String s22325 = "22325";
+  public static final String s22326 = "22326";
+  public static final String s22327 = "22327";
+  public static final String s22328 = "22328";
+  public static final String s22329 = "22329";
+  public static final String s22330 = "22330";
+  public static final String s22331 = "22331";
+  public static final String s22332 = "22332";
+  public static final String s22333 = "22333";
+  public static final String s22334 = "22334";
+  public static final String s22335 = "22335";
+  public static final String s22336 = "22336";
+  public static final String s22337 = "22337";
+  public static final String s22338 = "22338";
+  public static final String s22339 = "22339";
+  public static final String s22340 = "22340";
+  public static final String s22341 = "22341";
+  public static final String s22342 = "22342";
+  public static final String s22343 = "22343";
+  public static final String s22344 = "22344";
+  public static final String s22345 = "22345";
+  public static final String s22346 = "22346";
+  public static final String s22347 = "22347";
+  public static final String s22348 = "22348";
+  public static final String s22349 = "22349";
+  public static final String s22350 = "22350";
+  public static final String s22351 = "22351";
+  public static final String s22352 = "22352";
+  public static final String s22353 = "22353";
+  public static final String s22354 = "22354";
+  public static final String s22355 = "22355";
+  public static final String s22356 = "22356";
+  public static final String s22357 = "22357";
+  public static final String s22358 = "22358";
+  public static final String s22359 = "22359";
+  public static final String s22360 = "22360";
+  public static final String s22361 = "22361";
+  public static final String s22362 = "22362";
+  public static final String s22363 = "22363";
+  public static final String s22364 = "22364";
+  public static final String s22365 = "22365";
+  public static final String s22366 = "22366";
+  public static final String s22367 = "22367";
+  public static final String s22368 = "22368";
+  public static final String s22369 = "22369";
+  public static final String s22370 = "22370";
+  public static final String s22371 = "22371";
+  public static final String s22372 = "22372";
+  public static final String s22373 = "22373";
+  public static final String s22374 = "22374";
+  public static final String s22375 = "22375";
+  public static final String s22376 = "22376";
+  public static final String s22377 = "22377";
+  public static final String s22378 = "22378";
+  public static final String s22379 = "22379";
+  public static final String s22380 = "22380";
+  public static final String s22381 = "22381";
+  public static final String s22382 = "22382";
+  public static final String s22383 = "22383";
+  public static final String s22384 = "22384";
+  public static final String s22385 = "22385";
+  public static final String s22386 = "22386";
+  public static final String s22387 = "22387";
+  public static final String s22388 = "22388";
+  public static final String s22389 = "22389";
+  public static final String s22390 = "22390";
+  public static final String s22391 = "22391";
+  public static final String s22392 = "22392";
+  public static final String s22393 = "22393";
+  public static final String s22394 = "22394";
+  public static final String s22395 = "22395";
+  public static final String s22396 = "22396";
+  public static final String s22397 = "22397";
+  public static final String s22398 = "22398";
+  public static final String s22399 = "22399";
+  public static final String s22400 = "22400";
+  public static final String s22401 = "22401";
+  public static final String s22402 = "22402";
+  public static final String s22403 = "22403";
+  public static final String s22404 = "22404";
+  public static final String s22405 = "22405";
+  public static final String s22406 = "22406";
+  public static final String s22407 = "22407";
+  public static final String s22408 = "22408";
+  public static final String s22409 = "22409";
+  public static final String s22410 = "22410";
+  public static final String s22411 = "22411";
+  public static final String s22412 = "22412";
+  public static final String s22413 = "22413";
+  public static final String s22414 = "22414";
+  public static final String s22415 = "22415";
+  public static final String s22416 = "22416";
+  public static final String s22417 = "22417";
+  public static final String s22418 = "22418";
+  public static final String s22419 = "22419";
+  public static final String s22420 = "22420";
+  public static final String s22421 = "22421";
+  public static final String s22422 = "22422";
+  public static final String s22423 = "22423";
+  public static final String s22424 = "22424";
+  public static final String s22425 = "22425";
+  public static final String s22426 = "22426";
+  public static final String s22427 = "22427";
+  public static final String s22428 = "22428";
+  public static final String s22429 = "22429";
+  public static final String s22430 = "22430";
+  public static final String s22431 = "22431";
+  public static final String s22432 = "22432";
+  public static final String s22433 = "22433";
+  public static final String s22434 = "22434";
+  public static final String s22435 = "22435";
+  public static final String s22436 = "22436";
+  public static final String s22437 = "22437";
+  public static final String s22438 = "22438";
+  public static final String s22439 = "22439";
+  public static final String s22440 = "22440";
+  public static final String s22441 = "22441";
+  public static final String s22442 = "22442";
+  public static final String s22443 = "22443";
+  public static final String s22444 = "22444";
+  public static final String s22445 = "22445";
+  public static final String s22446 = "22446";
+  public static final String s22447 = "22447";
+  public static final String s22448 = "22448";
+  public static final String s22449 = "22449";
+  public static final String s22450 = "22450";
+  public static final String s22451 = "22451";
+  public static final String s22452 = "22452";
+  public static final String s22453 = "22453";
+  public static final String s22454 = "22454";
+  public static final String s22455 = "22455";
+  public static final String s22456 = "22456";
+  public static final String s22457 = "22457";
+  public static final String s22458 = "22458";
+  public static final String s22459 = "22459";
+  public static final String s22460 = "22460";
+  public static final String s22461 = "22461";
+  public static final String s22462 = "22462";
+  public static final String s22463 = "22463";
+  public static final String s22464 = "22464";
+  public static final String s22465 = "22465";
+  public static final String s22466 = "22466";
+  public static final String s22467 = "22467";
+  public static final String s22468 = "22468";
+  public static final String s22469 = "22469";
+  public static final String s22470 = "22470";
+  public static final String s22471 = "22471";
+  public static final String s22472 = "22472";
+  public static final String s22473 = "22473";
+  public static final String s22474 = "22474";
+  public static final String s22475 = "22475";
+  public static final String s22476 = "22476";
+  public static final String s22477 = "22477";
+  public static final String s22478 = "22478";
+  public static final String s22479 = "22479";
+  public static final String s22480 = "22480";
+  public static final String s22481 = "22481";
+  public static final String s22482 = "22482";
+  public static final String s22483 = "22483";
+  public static final String s22484 = "22484";
+  public static final String s22485 = "22485";
+  public static final String s22486 = "22486";
+  public static final String s22487 = "22487";
+  public static final String s22488 = "22488";
+  public static final String s22489 = "22489";
+  public static final String s22490 = "22490";
+  public static final String s22491 = "22491";
+  public static final String s22492 = "22492";
+  public static final String s22493 = "22493";
+  public static final String s22494 = "22494";
+  public static final String s22495 = "22495";
+  public static final String s22496 = "22496";
+  public static final String s22497 = "22497";
+  public static final String s22498 = "22498";
+  public static final String s22499 = "22499";
+  public static final String s22500 = "22500";
+  public static final String s22501 = "22501";
+  public static final String s22502 = "22502";
+  public static final String s22503 = "22503";
+  public static final String s22504 = "22504";
+  public static final String s22505 = "22505";
+  public static final String s22506 = "22506";
+  public static final String s22507 = "22507";
+  public static final String s22508 = "22508";
+  public static final String s22509 = "22509";
+  public static final String s22510 = "22510";
+  public static final String s22511 = "22511";
+  public static final String s22512 = "22512";
+  public static final String s22513 = "22513";
+  public static final String s22514 = "22514";
+  public static final String s22515 = "22515";
+  public static final String s22516 = "22516";
+  public static final String s22517 = "22517";
+  public static final String s22518 = "22518";
+  public static final String s22519 = "22519";
+  public static final String s22520 = "22520";
+  public static final String s22521 = "22521";
+  public static final String s22522 = "22522";
+  public static final String s22523 = "22523";
+  public static final String s22524 = "22524";
+  public static final String s22525 = "22525";
+  public static final String s22526 = "22526";
+  public static final String s22527 = "22527";
+  public static final String s22528 = "22528";
+  public static final String s22529 = "22529";
+  public static final String s22530 = "22530";
+  public static final String s22531 = "22531";
+  public static final String s22532 = "22532";
+  public static final String s22533 = "22533";
+  public static final String s22534 = "22534";
+  public static final String s22535 = "22535";
+  public static final String s22536 = "22536";
+  public static final String s22537 = "22537";
+  public static final String s22538 = "22538";
+  public static final String s22539 = "22539";
+  public static final String s22540 = "22540";
+  public static final String s22541 = "22541";
+  public static final String s22542 = "22542";
+  public static final String s22543 = "22543";
+  public static final String s22544 = "22544";
+  public static final String s22545 = "22545";
+  public static final String s22546 = "22546";
+  public static final String s22547 = "22547";
+  public static final String s22548 = "22548";
+  public static final String s22549 = "22549";
+  public static final String s22550 = "22550";
+  public static final String s22551 = "22551";
+  public static final String s22552 = "22552";
+  public static final String s22553 = "22553";
+  public static final String s22554 = "22554";
+  public static final String s22555 = "22555";
+  public static final String s22556 = "22556";
+  public static final String s22557 = "22557";
+  public static final String s22558 = "22558";
+  public static final String s22559 = "22559";
+  public static final String s22560 = "22560";
+  public static final String s22561 = "22561";
+  public static final String s22562 = "22562";
+  public static final String s22563 = "22563";
+  public static final String s22564 = "22564";
+  public static final String s22565 = "22565";
+  public static final String s22566 = "22566";
+  public static final String s22567 = "22567";
+  public static final String s22568 = "22568";
+  public static final String s22569 = "22569";
+  public static final String s22570 = "22570";
+  public static final String s22571 = "22571";
+  public static final String s22572 = "22572";
+  public static final String s22573 = "22573";
+  public static final String s22574 = "22574";
+  public static final String s22575 = "22575";
+  public static final String s22576 = "22576";
+  public static final String s22577 = "22577";
+  public static final String s22578 = "22578";
+  public static final String s22579 = "22579";
+  public static final String s22580 = "22580";
+  public static final String s22581 = "22581";
+  public static final String s22582 = "22582";
+  public static final String s22583 = "22583";
+  public static final String s22584 = "22584";
+  public static final String s22585 = "22585";
+  public static final String s22586 = "22586";
+  public static final String s22587 = "22587";
+  public static final String s22588 = "22588";
+  public static final String s22589 = "22589";
+  public static final String s22590 = "22590";
+  public static final String s22591 = "22591";
+  public static final String s22592 = "22592";
+  public static final String s22593 = "22593";
+  public static final String s22594 = "22594";
+  public static final String s22595 = "22595";
+  public static final String s22596 = "22596";
+  public static final String s22597 = "22597";
+  public static final String s22598 = "22598";
+  public static final String s22599 = "22599";
+  public static final String s22600 = "22600";
+  public static final String s22601 = "22601";
+  public static final String s22602 = "22602";
+  public static final String s22603 = "22603";
+  public static final String s22604 = "22604";
+  public static final String s22605 = "22605";
+  public static final String s22606 = "22606";
+  public static final String s22607 = "22607";
+  public static final String s22608 = "22608";
+  public static final String s22609 = "22609";
+  public static final String s22610 = "22610";
+  public static final String s22611 = "22611";
+  public static final String s22612 = "22612";
+  public static final String s22613 = "22613";
+  public static final String s22614 = "22614";
+  public static final String s22615 = "22615";
+  public static final String s22616 = "22616";
+  public static final String s22617 = "22617";
+  public static final String s22618 = "22618";
+  public static final String s22619 = "22619";
+  public static final String s22620 = "22620";
+  public static final String s22621 = "22621";
+  public static final String s22622 = "22622";
+  public static final String s22623 = "22623";
+  public static final String s22624 = "22624";
+  public static final String s22625 = "22625";
+  public static final String s22626 = "22626";
+  public static final String s22627 = "22627";
+  public static final String s22628 = "22628";
+  public static final String s22629 = "22629";
+  public static final String s22630 = "22630";
+  public static final String s22631 = "22631";
+  public static final String s22632 = "22632";
+  public static final String s22633 = "22633";
+  public static final String s22634 = "22634";
+  public static final String s22635 = "22635";
+  public static final String s22636 = "22636";
+  public static final String s22637 = "22637";
+  public static final String s22638 = "22638";
+  public static final String s22639 = "22639";
+  public static final String s22640 = "22640";
+  public static final String s22641 = "22641";
+  public static final String s22642 = "22642";
+  public static final String s22643 = "22643";
+  public static final String s22644 = "22644";
+  public static final String s22645 = "22645";
+  public static final String s22646 = "22646";
+  public static final String s22647 = "22647";
+  public static final String s22648 = "22648";
+  public static final String s22649 = "22649";
+  public static final String s22650 = "22650";
+  public static final String s22651 = "22651";
+  public static final String s22652 = "22652";
+  public static final String s22653 = "22653";
+  public static final String s22654 = "22654";
+  public static final String s22655 = "22655";
+  public static final String s22656 = "22656";
+  public static final String s22657 = "22657";
+  public static final String s22658 = "22658";
+  public static final String s22659 = "22659";
+  public static final String s22660 = "22660";
+  public static final String s22661 = "22661";
+  public static final String s22662 = "22662";
+  public static final String s22663 = "22663";
+  public static final String s22664 = "22664";
+  public static final String s22665 = "22665";
+  public static final String s22666 = "22666";
+  public static final String s22667 = "22667";
+  public static final String s22668 = "22668";
+  public static final String s22669 = "22669";
+  public static final String s22670 = "22670";
+  public static final String s22671 = "22671";
+  public static final String s22672 = "22672";
+  public static final String s22673 = "22673";
+  public static final String s22674 = "22674";
+  public static final String s22675 = "22675";
+  public static final String s22676 = "22676";
+  public static final String s22677 = "22677";
+  public static final String s22678 = "22678";
+  public static final String s22679 = "22679";
+  public static final String s22680 = "22680";
+  public static final String s22681 = "22681";
+  public static final String s22682 = "22682";
+  public static final String s22683 = "22683";
+  public static final String s22684 = "22684";
+  public static final String s22685 = "22685";
+  public static final String s22686 = "22686";
+  public static final String s22687 = "22687";
+  public static final String s22688 = "22688";
+  public static final String s22689 = "22689";
+  public static final String s22690 = "22690";
+  public static final String s22691 = "22691";
+  public static final String s22692 = "22692";
+  public static final String s22693 = "22693";
+  public static final String s22694 = "22694";
+  public static final String s22695 = "22695";
+  public static final String s22696 = "22696";
+  public static final String s22697 = "22697";
+  public static final String s22698 = "22698";
+  public static final String s22699 = "22699";
+  public static final String s22700 = "22700";
+  public static final String s22701 = "22701";
+  public static final String s22702 = "22702";
+  public static final String s22703 = "22703";
+  public static final String s22704 = "22704";
+  public static final String s22705 = "22705";
+  public static final String s22706 = "22706";
+  public static final String s22707 = "22707";
+  public static final String s22708 = "22708";
+  public static final String s22709 = "22709";
+  public static final String s22710 = "22710";
+  public static final String s22711 = "22711";
+  public static final String s22712 = "22712";
+  public static final String s22713 = "22713";
+  public static final String s22714 = "22714";
+  public static final String s22715 = "22715";
+  public static final String s22716 = "22716";
+  public static final String s22717 = "22717";
+  public static final String s22718 = "22718";
+  public static final String s22719 = "22719";
+  public static final String s22720 = "22720";
+  public static final String s22721 = "22721";
+  public static final String s22722 = "22722";
+  public static final String s22723 = "22723";
+  public static final String s22724 = "22724";
+  public static final String s22725 = "22725";
+  public static final String s22726 = "22726";
+  public static final String s22727 = "22727";
+  public static final String s22728 = "22728";
+  public static final String s22729 = "22729";
+  public static final String s22730 = "22730";
+  public static final String s22731 = "22731";
+  public static final String s22732 = "22732";
+  public static final String s22733 = "22733";
+  public static final String s22734 = "22734";
+  public static final String s22735 = "22735";
+  public static final String s22736 = "22736";
+  public static final String s22737 = "22737";
+  public static final String s22738 = "22738";
+  public static final String s22739 = "22739";
+  public static final String s22740 = "22740";
+  public static final String s22741 = "22741";
+  public static final String s22742 = "22742";
+  public static final String s22743 = "22743";
+  public static final String s22744 = "22744";
+  public static final String s22745 = "22745";
+  public static final String s22746 = "22746";
+  public static final String s22747 = "22747";
+  public static final String s22748 = "22748";
+  public static final String s22749 = "22749";
+  public static final String s22750 = "22750";
+  public static final String s22751 = "22751";
+  public static final String s22752 = "22752";
+  public static final String s22753 = "22753";
+  public static final String s22754 = "22754";
+  public static final String s22755 = "22755";
+  public static final String s22756 = "22756";
+  public static final String s22757 = "22757";
+  public static final String s22758 = "22758";
+  public static final String s22759 = "22759";
+  public static final String s22760 = "22760";
+  public static final String s22761 = "22761";
+  public static final String s22762 = "22762";
+  public static final String s22763 = "22763";
+  public static final String s22764 = "22764";
+  public static final String s22765 = "22765";
+  public static final String s22766 = "22766";
+  public static final String s22767 = "22767";
+  public static final String s22768 = "22768";
+  public static final String s22769 = "22769";
+  public static final String s22770 = "22770";
+  public static final String s22771 = "22771";
+  public static final String s22772 = "22772";
+  public static final String s22773 = "22773";
+  public static final String s22774 = "22774";
+  public static final String s22775 = "22775";
+  public static final String s22776 = "22776";
+  public static final String s22777 = "22777";
+  public static final String s22778 = "22778";
+  public static final String s22779 = "22779";
+  public static final String s22780 = "22780";
+  public static final String s22781 = "22781";
+  public static final String s22782 = "22782";
+  public static final String s22783 = "22783";
+  public static final String s22784 = "22784";
+  public static final String s22785 = "22785";
+  public static final String s22786 = "22786";
+  public static final String s22787 = "22787";
+  public static final String s22788 = "22788";
+  public static final String s22789 = "22789";
+  public static final String s22790 = "22790";
+  public static final String s22791 = "22791";
+  public static final String s22792 = "22792";
+  public static final String s22793 = "22793";
+  public static final String s22794 = "22794";
+  public static final String s22795 = "22795";
+  public static final String s22796 = "22796";
+  public static final String s22797 = "22797";
+  public static final String s22798 = "22798";
+  public static final String s22799 = "22799";
+  public static final String s22800 = "22800";
+  public static final String s22801 = "22801";
+  public static final String s22802 = "22802";
+  public static final String s22803 = "22803";
+  public static final String s22804 = "22804";
+  public static final String s22805 = "22805";
+  public static final String s22806 = "22806";
+  public static final String s22807 = "22807";
+  public static final String s22808 = "22808";
+  public static final String s22809 = "22809";
+  public static final String s22810 = "22810";
+  public static final String s22811 = "22811";
+  public static final String s22812 = "22812";
+  public static final String s22813 = "22813";
+  public static final String s22814 = "22814";
+  public static final String s22815 = "22815";
+  public static final String s22816 = "22816";
+  public static final String s22817 = "22817";
+  public static final String s22818 = "22818";
+  public static final String s22819 = "22819";
+  public static final String s22820 = "22820";
+  public static final String s22821 = "22821";
+  public static final String s22822 = "22822";
+  public static final String s22823 = "22823";
+  public static final String s22824 = "22824";
+  public static final String s22825 = "22825";
+  public static final String s22826 = "22826";
+  public static final String s22827 = "22827";
+  public static final String s22828 = "22828";
+  public static final String s22829 = "22829";
+  public static final String s22830 = "22830";
+  public static final String s22831 = "22831";
+  public static final String s22832 = "22832";
+  public static final String s22833 = "22833";
+  public static final String s22834 = "22834";
+  public static final String s22835 = "22835";
+  public static final String s22836 = "22836";
+  public static final String s22837 = "22837";
+  public static final String s22838 = "22838";
+  public static final String s22839 = "22839";
+  public static final String s22840 = "22840";
+  public static final String s22841 = "22841";
+  public static final String s22842 = "22842";
+  public static final String s22843 = "22843";
+  public static final String s22844 = "22844";
+  public static final String s22845 = "22845";
+  public static final String s22846 = "22846";
+  public static final String s22847 = "22847";
+  public static final String s22848 = "22848";
+  public static final String s22849 = "22849";
+  public static final String s22850 = "22850";
+  public static final String s22851 = "22851";
+  public static final String s22852 = "22852";
+  public static final String s22853 = "22853";
+  public static final String s22854 = "22854";
+  public static final String s22855 = "22855";
+  public static final String s22856 = "22856";
+  public static final String s22857 = "22857";
+  public static final String s22858 = "22858";
+  public static final String s22859 = "22859";
+  public static final String s22860 = "22860";
+  public static final String s22861 = "22861";
+  public static final String s22862 = "22862";
+  public static final String s22863 = "22863";
+  public static final String s22864 = "22864";
+  public static final String s22865 = "22865";
+  public static final String s22866 = "22866";
+  public static final String s22867 = "22867";
+  public static final String s22868 = "22868";
+  public static final String s22869 = "22869";
+  public static final String s22870 = "22870";
+  public static final String s22871 = "22871";
+  public static final String s22872 = "22872";
+  public static final String s22873 = "22873";
+  public static final String s22874 = "22874";
+  public static final String s22875 = "22875";
+  public static final String s22876 = "22876";
+  public static final String s22877 = "22877";
+  public static final String s22878 = "22878";
+  public static final String s22879 = "22879";
+  public static final String s22880 = "22880";
+  public static final String s22881 = "22881";
+  public static final String s22882 = "22882";
+  public static final String s22883 = "22883";
+  public static final String s22884 = "22884";
+  public static final String s22885 = "22885";
+  public static final String s22886 = "22886";
+  public static final String s22887 = "22887";
+  public static final String s22888 = "22888";
+  public static final String s22889 = "22889";
+  public static final String s22890 = "22890";
+  public static final String s22891 = "22891";
+  public static final String s22892 = "22892";
+  public static final String s22893 = "22893";
+  public static final String s22894 = "22894";
+  public static final String s22895 = "22895";
+  public static final String s22896 = "22896";
+  public static final String s22897 = "22897";
+  public static final String s22898 = "22898";
+  public static final String s22899 = "22899";
+  public static final String s22900 = "22900";
+  public static final String s22901 = "22901";
+  public static final String s22902 = "22902";
+  public static final String s22903 = "22903";
+  public static final String s22904 = "22904";
+  public static final String s22905 = "22905";
+  public static final String s22906 = "22906";
+  public static final String s22907 = "22907";
+  public static final String s22908 = "22908";
+  public static final String s22909 = "22909";
+  public static final String s22910 = "22910";
+  public static final String s22911 = "22911";
+  public static final String s22912 = "22912";
+  public static final String s22913 = "22913";
+  public static final String s22914 = "22914";
+  public static final String s22915 = "22915";
+  public static final String s22916 = "22916";
+  public static final String s22917 = "22917";
+  public static final String s22918 = "22918";
+  public static final String s22919 = "22919";
+  public static final String s22920 = "22920";
+  public static final String s22921 = "22921";
+  public static final String s22922 = "22922";
+  public static final String s22923 = "22923";
+  public static final String s22924 = "22924";
+  public static final String s22925 = "22925";
+  public static final String s22926 = "22926";
+  public static final String s22927 = "22927";
+  public static final String s22928 = "22928";
+  public static final String s22929 = "22929";
+  public static final String s22930 = "22930";
+  public static final String s22931 = "22931";
+  public static final String s22932 = "22932";
+  public static final String s22933 = "22933";
+  public static final String s22934 = "22934";
+  public static final String s22935 = "22935";
+  public static final String s22936 = "22936";
+  public static final String s22937 = "22937";
+  public static final String s22938 = "22938";
+  public static final String s22939 = "22939";
+  public static final String s22940 = "22940";
+  public static final String s22941 = "22941";
+  public static final String s22942 = "22942";
+  public static final String s22943 = "22943";
+  public static final String s22944 = "22944";
+  public static final String s22945 = "22945";
+  public static final String s22946 = "22946";
+  public static final String s22947 = "22947";
+  public static final String s22948 = "22948";
+  public static final String s22949 = "22949";
+  public static final String s22950 = "22950";
+  public static final String s22951 = "22951";
+  public static final String s22952 = "22952";
+  public static final String s22953 = "22953";
+  public static final String s22954 = "22954";
+  public static final String s22955 = "22955";
+  public static final String s22956 = "22956";
+  public static final String s22957 = "22957";
+  public static final String s22958 = "22958";
+  public static final String s22959 = "22959";
+  public static final String s22960 = "22960";
+  public static final String s22961 = "22961";
+  public static final String s22962 = "22962";
+  public static final String s22963 = "22963";
+  public static final String s22964 = "22964";
+  public static final String s22965 = "22965";
+  public static final String s22966 = "22966";
+  public static final String s22967 = "22967";
+  public static final String s22968 = "22968";
+  public static final String s22969 = "22969";
+  public static final String s22970 = "22970";
+  public static final String s22971 = "22971";
+  public static final String s22972 = "22972";
+  public static final String s22973 = "22973";
+  public static final String s22974 = "22974";
+  public static final String s22975 = "22975";
+  public static final String s22976 = "22976";
+  public static final String s22977 = "22977";
+  public static final String s22978 = "22978";
+  public static final String s22979 = "22979";
+  public static final String s22980 = "22980";
+  public static final String s22981 = "22981";
+  public static final String s22982 = "22982";
+  public static final String s22983 = "22983";
+  public static final String s22984 = "22984";
+  public static final String s22985 = "22985";
+  public static final String s22986 = "22986";
+  public static final String s22987 = "22987";
+  public static final String s22988 = "22988";
+  public static final String s22989 = "22989";
+  public static final String s22990 = "22990";
+  public static final String s22991 = "22991";
+  public static final String s22992 = "22992";
+  public static final String s22993 = "22993";
+  public static final String s22994 = "22994";
+  public static final String s22995 = "22995";
+  public static final String s22996 = "22996";
+  public static final String s22997 = "22997";
+  public static final String s22998 = "22998";
+  public static final String s22999 = "22999";
+  public static final String s23000 = "23000";
+  public static final String s23001 = "23001";
+  public static final String s23002 = "23002";
+  public static final String s23003 = "23003";
+  public static final String s23004 = "23004";
+  public static final String s23005 = "23005";
+  public static final String s23006 = "23006";
+  public static final String s23007 = "23007";
+  public static final String s23008 = "23008";
+  public static final String s23009 = "23009";
+  public static final String s23010 = "23010";
+  public static final String s23011 = "23011";
+  public static final String s23012 = "23012";
+  public static final String s23013 = "23013";
+  public static final String s23014 = "23014";
+  public static final String s23015 = "23015";
+  public static final String s23016 = "23016";
+  public static final String s23017 = "23017";
+  public static final String s23018 = "23018";
+  public static final String s23019 = "23019";
+  public static final String s23020 = "23020";
+  public static final String s23021 = "23021";
+  public static final String s23022 = "23022";
+  public static final String s23023 = "23023";
+  public static final String s23024 = "23024";
+  public static final String s23025 = "23025";
+  public static final String s23026 = "23026";
+  public static final String s23027 = "23027";
+  public static final String s23028 = "23028";
+  public static final String s23029 = "23029";
+  public static final String s23030 = "23030";
+  public static final String s23031 = "23031";
+  public static final String s23032 = "23032";
+  public static final String s23033 = "23033";
+  public static final String s23034 = "23034";
+  public static final String s23035 = "23035";
+  public static final String s23036 = "23036";
+  public static final String s23037 = "23037";
+  public static final String s23038 = "23038";
+  public static final String s23039 = "23039";
+  public static final String s23040 = "23040";
+  public static final String s23041 = "23041";
+  public static final String s23042 = "23042";
+  public static final String s23043 = "23043";
+  public static final String s23044 = "23044";
+  public static final String s23045 = "23045";
+  public static final String s23046 = "23046";
+  public static final String s23047 = "23047";
+  public static final String s23048 = "23048";
+  public static final String s23049 = "23049";
+  public static final String s23050 = "23050";
+  public static final String s23051 = "23051";
+  public static final String s23052 = "23052";
+  public static final String s23053 = "23053";
+  public static final String s23054 = "23054";
+  public static final String s23055 = "23055";
+  public static final String s23056 = "23056";
+  public static final String s23057 = "23057";
+  public static final String s23058 = "23058";
+  public static final String s23059 = "23059";
+  public static final String s23060 = "23060";
+  public static final String s23061 = "23061";
+  public static final String s23062 = "23062";
+  public static final String s23063 = "23063";
+  public static final String s23064 = "23064";
+  public static final String s23065 = "23065";
+  public static final String s23066 = "23066";
+  public static final String s23067 = "23067";
+  public static final String s23068 = "23068";
+  public static final String s23069 = "23069";
+  public static final String s23070 = "23070";
+  public static final String s23071 = "23071";
+  public static final String s23072 = "23072";
+  public static final String s23073 = "23073";
+  public static final String s23074 = "23074";
+  public static final String s23075 = "23075";
+  public static final String s23076 = "23076";
+  public static final String s23077 = "23077";
+  public static final String s23078 = "23078";
+  public static final String s23079 = "23079";
+  public static final String s23080 = "23080";
+  public static final String s23081 = "23081";
+  public static final String s23082 = "23082";
+  public static final String s23083 = "23083";
+  public static final String s23084 = "23084";
+  public static final String s23085 = "23085";
+  public static final String s23086 = "23086";
+  public static final String s23087 = "23087";
+  public static final String s23088 = "23088";
+  public static final String s23089 = "23089";
+  public static final String s23090 = "23090";
+  public static final String s23091 = "23091";
+  public static final String s23092 = "23092";
+  public static final String s23093 = "23093";
+  public static final String s23094 = "23094";
+  public static final String s23095 = "23095";
+  public static final String s23096 = "23096";
+  public static final String s23097 = "23097";
+  public static final String s23098 = "23098";
+  public static final String s23099 = "23099";
+  public static final String s23100 = "23100";
+  public static final String s23101 = "23101";
+  public static final String s23102 = "23102";
+  public static final String s23103 = "23103";
+  public static final String s23104 = "23104";
+  public static final String s23105 = "23105";
+  public static final String s23106 = "23106";
+  public static final String s23107 = "23107";
+  public static final String s23108 = "23108";
+  public static final String s23109 = "23109";
+  public static final String s23110 = "23110";
+  public static final String s23111 = "23111";
+  public static final String s23112 = "23112";
+  public static final String s23113 = "23113";
+  public static final String s23114 = "23114";
+  public static final String s23115 = "23115";
+  public static final String s23116 = "23116";
+  public static final String s23117 = "23117";
+  public static final String s23118 = "23118";
+  public static final String s23119 = "23119";
+  public static final String s23120 = "23120";
+  public static final String s23121 = "23121";
+  public static final String s23122 = "23122";
+  public static final String s23123 = "23123";
+  public static final String s23124 = "23124";
+  public static final String s23125 = "23125";
+  public static final String s23126 = "23126";
+  public static final String s23127 = "23127";
+  public static final String s23128 = "23128";
+  public static final String s23129 = "23129";
+  public static final String s23130 = "23130";
+  public static final String s23131 = "23131";
+  public static final String s23132 = "23132";
+  public static final String s23133 = "23133";
+  public static final String s23134 = "23134";
+  public static final String s23135 = "23135";
+  public static final String s23136 = "23136";
+  public static final String s23137 = "23137";
+  public static final String s23138 = "23138";
+  public static final String s23139 = "23139";
+  public static final String s23140 = "23140";
+  public static final String s23141 = "23141";
+  public static final String s23142 = "23142";
+  public static final String s23143 = "23143";
+  public static final String s23144 = "23144";
+  public static final String s23145 = "23145";
+  public static final String s23146 = "23146";
+  public static final String s23147 = "23147";
+  public static final String s23148 = "23148";
+  public static final String s23149 = "23149";
+  public static final String s23150 = "23150";
+  public static final String s23151 = "23151";
+  public static final String s23152 = "23152";
+  public static final String s23153 = "23153";
+  public static final String s23154 = "23154";
+  public static final String s23155 = "23155";
+  public static final String s23156 = "23156";
+  public static final String s23157 = "23157";
+  public static final String s23158 = "23158";
+  public static final String s23159 = "23159";
+  public static final String s23160 = "23160";
+  public static final String s23161 = "23161";
+  public static final String s23162 = "23162";
+  public static final String s23163 = "23163";
+  public static final String s23164 = "23164";
+  public static final String s23165 = "23165";
+  public static final String s23166 = "23166";
+  public static final String s23167 = "23167";
+  public static final String s23168 = "23168";
+  public static final String s23169 = "23169";
+  public static final String s23170 = "23170";
+  public static final String s23171 = "23171";
+  public static final String s23172 = "23172";
+  public static final String s23173 = "23173";
+  public static final String s23174 = "23174";
+  public static final String s23175 = "23175";
+  public static final String s23176 = "23176";
+  public static final String s23177 = "23177";
+  public static final String s23178 = "23178";
+  public static final String s23179 = "23179";
+  public static final String s23180 = "23180";
+  public static final String s23181 = "23181";
+  public static final String s23182 = "23182";
+  public static final String s23183 = "23183";
+  public static final String s23184 = "23184";
+  public static final String s23185 = "23185";
+  public static final String s23186 = "23186";
+  public static final String s23187 = "23187";
+  public static final String s23188 = "23188";
+  public static final String s23189 = "23189";
+  public static final String s23190 = "23190";
+  public static final String s23191 = "23191";
+  public static final String s23192 = "23192";
+  public static final String s23193 = "23193";
+  public static final String s23194 = "23194";
+  public static final String s23195 = "23195";
+  public static final String s23196 = "23196";
+  public static final String s23197 = "23197";
+  public static final String s23198 = "23198";
+  public static final String s23199 = "23199";
+  public static final String s23200 = "23200";
+  public static final String s23201 = "23201";
+  public static final String s23202 = "23202";
+  public static final String s23203 = "23203";
+  public static final String s23204 = "23204";
+  public static final String s23205 = "23205";
+  public static final String s23206 = "23206";
+  public static final String s23207 = "23207";
+  public static final String s23208 = "23208";
+  public static final String s23209 = "23209";
+  public static final String s23210 = "23210";
+  public static final String s23211 = "23211";
+  public static final String s23212 = "23212";
+  public static final String s23213 = "23213";
+  public static final String s23214 = "23214";
+  public static final String s23215 = "23215";
+  public static final String s23216 = "23216";
+  public static final String s23217 = "23217";
+  public static final String s23218 = "23218";
+  public static final String s23219 = "23219";
+  public static final String s23220 = "23220";
+  public static final String s23221 = "23221";
+  public static final String s23222 = "23222";
+  public static final String s23223 = "23223";
+  public static final String s23224 = "23224";
+  public static final String s23225 = "23225";
+  public static final String s23226 = "23226";
+  public static final String s23227 = "23227";
+  public static final String s23228 = "23228";
+  public static final String s23229 = "23229";
+  public static final String s23230 = "23230";
+  public static final String s23231 = "23231";
+  public static final String s23232 = "23232";
+  public static final String s23233 = "23233";
+  public static final String s23234 = "23234";
+  public static final String s23235 = "23235";
+  public static final String s23236 = "23236";
+  public static final String s23237 = "23237";
+  public static final String s23238 = "23238";
+  public static final String s23239 = "23239";
+  public static final String s23240 = "23240";
+  public static final String s23241 = "23241";
+  public static final String s23242 = "23242";
+  public static final String s23243 = "23243";
+  public static final String s23244 = "23244";
+  public static final String s23245 = "23245";
+  public static final String s23246 = "23246";
+  public static final String s23247 = "23247";
+  public static final String s23248 = "23248";
+  public static final String s23249 = "23249";
+  public static final String s23250 = "23250";
+  public static final String s23251 = "23251";
+  public static final String s23252 = "23252";
+  public static final String s23253 = "23253";
+  public static final String s23254 = "23254";
+  public static final String s23255 = "23255";
+  public static final String s23256 = "23256";
+  public static final String s23257 = "23257";
+  public static final String s23258 = "23258";
+  public static final String s23259 = "23259";
+  public static final String s23260 = "23260";
+  public static final String s23261 = "23261";
+  public static final String s23262 = "23262";
+  public static final String s23263 = "23263";
+  public static final String s23264 = "23264";
+  public static final String s23265 = "23265";
+  public static final String s23266 = "23266";
+  public static final String s23267 = "23267";
+  public static final String s23268 = "23268";
+  public static final String s23269 = "23269";
+  public static final String s23270 = "23270";
+  public static final String s23271 = "23271";
+  public static final String s23272 = "23272";
+  public static final String s23273 = "23273";
+  public static final String s23274 = "23274";
+  public static final String s23275 = "23275";
+  public static final String s23276 = "23276";
+  public static final String s23277 = "23277";
+  public static final String s23278 = "23278";
+  public static final String s23279 = "23279";
+  public static final String s23280 = "23280";
+  public static final String s23281 = "23281";
+  public static final String s23282 = "23282";
+  public static final String s23283 = "23283";
+  public static final String s23284 = "23284";
+  public static final String s23285 = "23285";
+  public static final String s23286 = "23286";
+  public static final String s23287 = "23287";
+  public static final String s23288 = "23288";
+  public static final String s23289 = "23289";
+  public static final String s23290 = "23290";
+  public static final String s23291 = "23291";
+  public static final String s23292 = "23292";
+  public static final String s23293 = "23293";
+  public static final String s23294 = "23294";
+  public static final String s23295 = "23295";
+  public static final String s23296 = "23296";
+  public static final String s23297 = "23297";
+  public static final String s23298 = "23298";
+  public static final String s23299 = "23299";
+  public static final String s23300 = "23300";
+  public static final String s23301 = "23301";
+  public static final String s23302 = "23302";
+  public static final String s23303 = "23303";
+  public static final String s23304 = "23304";
+  public static final String s23305 = "23305";
+  public static final String s23306 = "23306";
+  public static final String s23307 = "23307";
+  public static final String s23308 = "23308";
+  public static final String s23309 = "23309";
+  public static final String s23310 = "23310";
+  public static final String s23311 = "23311";
+  public static final String s23312 = "23312";
+  public static final String s23313 = "23313";
+  public static final String s23314 = "23314";
+  public static final String s23315 = "23315";
+  public static final String s23316 = "23316";
+  public static final String s23317 = "23317";
+  public static final String s23318 = "23318";
+  public static final String s23319 = "23319";
+  public static final String s23320 = "23320";
+  public static final String s23321 = "23321";
+  public static final String s23322 = "23322";
+  public static final String s23323 = "23323";
+  public static final String s23324 = "23324";
+  public static final String s23325 = "23325";
+  public static final String s23326 = "23326";
+  public static final String s23327 = "23327";
+  public static final String s23328 = "23328";
+  public static final String s23329 = "23329";
+  public static final String s23330 = "23330";
+  public static final String s23331 = "23331";
+  public static final String s23332 = "23332";
+  public static final String s23333 = "23333";
+  public static final String s23334 = "23334";
+  public static final String s23335 = "23335";
+  public static final String s23336 = "23336";
+  public static final String s23337 = "23337";
+  public static final String s23338 = "23338";
+  public static final String s23339 = "23339";
+  public static final String s23340 = "23340";
+  public static final String s23341 = "23341";
+  public static final String s23342 = "23342";
+  public static final String s23343 = "23343";
+  public static final String s23344 = "23344";
+  public static final String s23345 = "23345";
+  public static final String s23346 = "23346";
+  public static final String s23347 = "23347";
+  public static final String s23348 = "23348";
+  public static final String s23349 = "23349";
+  public static final String s23350 = "23350";
+  public static final String s23351 = "23351";
+  public static final String s23352 = "23352";
+  public static final String s23353 = "23353";
+  public static final String s23354 = "23354";
+  public static final String s23355 = "23355";
+  public static final String s23356 = "23356";
+  public static final String s23357 = "23357";
+  public static final String s23358 = "23358";
+  public static final String s23359 = "23359";
+  public static final String s23360 = "23360";
+  public static final String s23361 = "23361";
+  public static final String s23362 = "23362";
+  public static final String s23363 = "23363";
+  public static final String s23364 = "23364";
+  public static final String s23365 = "23365";
+  public static final String s23366 = "23366";
+  public static final String s23367 = "23367";
+  public static final String s23368 = "23368";
+  public static final String s23369 = "23369";
+  public static final String s23370 = "23370";
+  public static final String s23371 = "23371";
+  public static final String s23372 = "23372";
+  public static final String s23373 = "23373";
+  public static final String s23374 = "23374";
+  public static final String s23375 = "23375";
+  public static final String s23376 = "23376";
+  public static final String s23377 = "23377";
+  public static final String s23378 = "23378";
+  public static final String s23379 = "23379";
+  public static final String s23380 = "23380";
+  public static final String s23381 = "23381";
+  public static final String s23382 = "23382";
+  public static final String s23383 = "23383";
+  public static final String s23384 = "23384";
+  public static final String s23385 = "23385";
+  public static final String s23386 = "23386";
+  public static final String s23387 = "23387";
+  public static final String s23388 = "23388";
+  public static final String s23389 = "23389";
+  public static final String s23390 = "23390";
+  public static final String s23391 = "23391";
+  public static final String s23392 = "23392";
+  public static final String s23393 = "23393";
+  public static final String s23394 = "23394";
+  public static final String s23395 = "23395";
+  public static final String s23396 = "23396";
+  public static final String s23397 = "23397";
+  public static final String s23398 = "23398";
+  public static final String s23399 = "23399";
+  public static final String s23400 = "23400";
+  public static final String s23401 = "23401";
+  public static final String s23402 = "23402";
+  public static final String s23403 = "23403";
+  public static final String s23404 = "23404";
+  public static final String s23405 = "23405";
+  public static final String s23406 = "23406";
+  public static final String s23407 = "23407";
+  public static final String s23408 = "23408";
+  public static final String s23409 = "23409";
+  public static final String s23410 = "23410";
+  public static final String s23411 = "23411";
+  public static final String s23412 = "23412";
+  public static final String s23413 = "23413";
+  public static final String s23414 = "23414";
+  public static final String s23415 = "23415";
+  public static final String s23416 = "23416";
+  public static final String s23417 = "23417";
+  public static final String s23418 = "23418";
+  public static final String s23419 = "23419";
+  public static final String s23420 = "23420";
+  public static final String s23421 = "23421";
+  public static final String s23422 = "23422";
+  public static final String s23423 = "23423";
+  public static final String s23424 = "23424";
+  public static final String s23425 = "23425";
+  public static final String s23426 = "23426";
+  public static final String s23427 = "23427";
+  public static final String s23428 = "23428";
+  public static final String s23429 = "23429";
+  public static final String s23430 = "23430";
+  public static final String s23431 = "23431";
+  public static final String s23432 = "23432";
+  public static final String s23433 = "23433";
+  public static final String s23434 = "23434";
+  public static final String s23435 = "23435";
+  public static final String s23436 = "23436";
+  public static final String s23437 = "23437";
+  public static final String s23438 = "23438";
+  public static final String s23439 = "23439";
+  public static final String s23440 = "23440";
+  public static final String s23441 = "23441";
+  public static final String s23442 = "23442";
+  public static final String s23443 = "23443";
+  public static final String s23444 = "23444";
+  public static final String s23445 = "23445";
+  public static final String s23446 = "23446";
+  public static final String s23447 = "23447";
+  public static final String s23448 = "23448";
+  public static final String s23449 = "23449";
+  public static final String s23450 = "23450";
+  public static final String s23451 = "23451";
+  public static final String s23452 = "23452";
+  public static final String s23453 = "23453";
+  public static final String s23454 = "23454";
+  public static final String s23455 = "23455";
+  public static final String s23456 = "23456";
+  public static final String s23457 = "23457";
+  public static final String s23458 = "23458";
+  public static final String s23459 = "23459";
+  public static final String s23460 = "23460";
+  public static final String s23461 = "23461";
+  public static final String s23462 = "23462";
+  public static final String s23463 = "23463";
+  public static final String s23464 = "23464";
+  public static final String s23465 = "23465";
+  public static final String s23466 = "23466";
+  public static final String s23467 = "23467";
+  public static final String s23468 = "23468";
+  public static final String s23469 = "23469";
+  public static final String s23470 = "23470";
+  public static final String s23471 = "23471";
+  public static final String s23472 = "23472";
+  public static final String s23473 = "23473";
+  public static final String s23474 = "23474";
+  public static final String s23475 = "23475";
+  public static final String s23476 = "23476";
+  public static final String s23477 = "23477";
+  public static final String s23478 = "23478";
+  public static final String s23479 = "23479";
+  public static final String s23480 = "23480";
+  public static final String s23481 = "23481";
+  public static final String s23482 = "23482";
+  public static final String s23483 = "23483";
+  public static final String s23484 = "23484";
+  public static final String s23485 = "23485";
+  public static final String s23486 = "23486";
+  public static final String s23487 = "23487";
+  public static final String s23488 = "23488";
+  public static final String s23489 = "23489";
+  public static final String s23490 = "23490";
+  public static final String s23491 = "23491";
+  public static final String s23492 = "23492";
+  public static final String s23493 = "23493";
+  public static final String s23494 = "23494";
+  public static final String s23495 = "23495";
+  public static final String s23496 = "23496";
+  public static final String s23497 = "23497";
+  public static final String s23498 = "23498";
+  public static final String s23499 = "23499";
+  public static final String s23500 = "23500";
+  public static final String s23501 = "23501";
+  public static final String s23502 = "23502";
+  public static final String s23503 = "23503";
+  public static final String s23504 = "23504";
+  public static final String s23505 = "23505";
+  public static final String s23506 = "23506";
+  public static final String s23507 = "23507";
+  public static final String s23508 = "23508";
+  public static final String s23509 = "23509";
+  public static final String s23510 = "23510";
+  public static final String s23511 = "23511";
+  public static final String s23512 = "23512";
+  public static final String s23513 = "23513";
+  public static final String s23514 = "23514";
+  public static final String s23515 = "23515";
+  public static final String s23516 = "23516";
+  public static final String s23517 = "23517";
+  public static final String s23518 = "23518";
+  public static final String s23519 = "23519";
+  public static final String s23520 = "23520";
+  public static final String s23521 = "23521";
+  public static final String s23522 = "23522";
+  public static final String s23523 = "23523";
+  public static final String s23524 = "23524";
+  public static final String s23525 = "23525";
+  public static final String s23526 = "23526";
+  public static final String s23527 = "23527";
+  public static final String s23528 = "23528";
+  public static final String s23529 = "23529";
+  public static final String s23530 = "23530";
+  public static final String s23531 = "23531";
+  public static final String s23532 = "23532";
+  public static final String s23533 = "23533";
+  public static final String s23534 = "23534";
+  public static final String s23535 = "23535";
+  public static final String s23536 = "23536";
+  public static final String s23537 = "23537";
+  public static final String s23538 = "23538";
+  public static final String s23539 = "23539";
+  public static final String s23540 = "23540";
+  public static final String s23541 = "23541";
+  public static final String s23542 = "23542";
+  public static final String s23543 = "23543";
+  public static final String s23544 = "23544";
+  public static final String s23545 = "23545";
+  public static final String s23546 = "23546";
+  public static final String s23547 = "23547";
+  public static final String s23548 = "23548";
+  public static final String s23549 = "23549";
+  public static final String s23550 = "23550";
+  public static final String s23551 = "23551";
+  public static final String s23552 = "23552";
+  public static final String s23553 = "23553";
+  public static final String s23554 = "23554";
+  public static final String s23555 = "23555";
+  public static final String s23556 = "23556";
+  public static final String s23557 = "23557";
+  public static final String s23558 = "23558";
+  public static final String s23559 = "23559";
+  public static final String s23560 = "23560";
+  public static final String s23561 = "23561";
+  public static final String s23562 = "23562";
+  public static final String s23563 = "23563";
+  public static final String s23564 = "23564";
+  public static final String s23565 = "23565";
+  public static final String s23566 = "23566";
+  public static final String s23567 = "23567";
+  public static final String s23568 = "23568";
+  public static final String s23569 = "23569";
+  public static final String s23570 = "23570";
+  public static final String s23571 = "23571";
+  public static final String s23572 = "23572";
+  public static final String s23573 = "23573";
+  public static final String s23574 = "23574";
+  public static final String s23575 = "23575";
+  public static final String s23576 = "23576";
+  public static final String s23577 = "23577";
+  public static final String s23578 = "23578";
+  public static final String s23579 = "23579";
+  public static final String s23580 = "23580";
+  public static final String s23581 = "23581";
+  public static final String s23582 = "23582";
+  public static final String s23583 = "23583";
+  public static final String s23584 = "23584";
+  public static final String s23585 = "23585";
+  public static final String s23586 = "23586";
+  public static final String s23587 = "23587";
+  public static final String s23588 = "23588";
+  public static final String s23589 = "23589";
+  public static final String s23590 = "23590";
+  public static final String s23591 = "23591";
+  public static final String s23592 = "23592";
+  public static final String s23593 = "23593";
+  public static final String s23594 = "23594";
+  public static final String s23595 = "23595";
+  public static final String s23596 = "23596";
+  public static final String s23597 = "23597";
+  public static final String s23598 = "23598";
+  public static final String s23599 = "23599";
+  public static final String s23600 = "23600";
+  public static final String s23601 = "23601";
+  public static final String s23602 = "23602";
+  public static final String s23603 = "23603";
+  public static final String s23604 = "23604";
+  public static final String s23605 = "23605";
+  public static final String s23606 = "23606";
+  public static final String s23607 = "23607";
+  public static final String s23608 = "23608";
+  public static final String s23609 = "23609";
+  public static final String s23610 = "23610";
+  public static final String s23611 = "23611";
+  public static final String s23612 = "23612";
+  public static final String s23613 = "23613";
+  public static final String s23614 = "23614";
+  public static final String s23615 = "23615";
+  public static final String s23616 = "23616";
+  public static final String s23617 = "23617";
+  public static final String s23618 = "23618";
+  public static final String s23619 = "23619";
+  public static final String s23620 = "23620";
+  public static final String s23621 = "23621";
+  public static final String s23622 = "23622";
+  public static final String s23623 = "23623";
+  public static final String s23624 = "23624";
+  public static final String s23625 = "23625";
+  public static final String s23626 = "23626";
+  public static final String s23627 = "23627";
+  public static final String s23628 = "23628";
+  public static final String s23629 = "23629";
+  public static final String s23630 = "23630";
+  public static final String s23631 = "23631";
+  public static final String s23632 = "23632";
+  public static final String s23633 = "23633";
+  public static final String s23634 = "23634";
+  public static final String s23635 = "23635";
+  public static final String s23636 = "23636";
+  public static final String s23637 = "23637";
+  public static final String s23638 = "23638";
+  public static final String s23639 = "23639";
+  public static final String s23640 = "23640";
+  public static final String s23641 = "23641";
+  public static final String s23642 = "23642";
+  public static final String s23643 = "23643";
+  public static final String s23644 = "23644";
+  public static final String s23645 = "23645";
+  public static final String s23646 = "23646";
+  public static final String s23647 = "23647";
+  public static final String s23648 = "23648";
+  public static final String s23649 = "23649";
+  public static final String s23650 = "23650";
+  public static final String s23651 = "23651";
+  public static final String s23652 = "23652";
+  public static final String s23653 = "23653";
+  public static final String s23654 = "23654";
+  public static final String s23655 = "23655";
+  public static final String s23656 = "23656";
+  public static final String s23657 = "23657";
+  public static final String s23658 = "23658";
+  public static final String s23659 = "23659";
+  public static final String s23660 = "23660";
+  public static final String s23661 = "23661";
+  public static final String s23662 = "23662";
+  public static final String s23663 = "23663";
+  public static final String s23664 = "23664";
+  public static final String s23665 = "23665";
+  public static final String s23666 = "23666";
+  public static final String s23667 = "23667";
+  public static final String s23668 = "23668";
+  public static final String s23669 = "23669";
+  public static final String s23670 = "23670";
+  public static final String s23671 = "23671";
+  public static final String s23672 = "23672";
+  public static final String s23673 = "23673";
+  public static final String s23674 = "23674";
+  public static final String s23675 = "23675";
+  public static final String s23676 = "23676";
+  public static final String s23677 = "23677";
+  public static final String s23678 = "23678";
+  public static final String s23679 = "23679";
+  public static final String s23680 = "23680";
+  public static final String s23681 = "23681";
+  public static final String s23682 = "23682";
+  public static final String s23683 = "23683";
+  public static final String s23684 = "23684";
+  public static final String s23685 = "23685";
+  public static final String s23686 = "23686";
+  public static final String s23687 = "23687";
+  public static final String s23688 = "23688";
+  public static final String s23689 = "23689";
+  public static final String s23690 = "23690";
+  public static final String s23691 = "23691";
+  public static final String s23692 = "23692";
+  public static final String s23693 = "23693";
+  public static final String s23694 = "23694";
+  public static final String s23695 = "23695";
+  public static final String s23696 = "23696";
+  public static final String s23697 = "23697";
+  public static final String s23698 = "23698";
+  public static final String s23699 = "23699";
+  public static final String s23700 = "23700";
+  public static final String s23701 = "23701";
+  public static final String s23702 = "23702";
+  public static final String s23703 = "23703";
+  public static final String s23704 = "23704";
+  public static final String s23705 = "23705";
+  public static final String s23706 = "23706";
+  public static final String s23707 = "23707";
+  public static final String s23708 = "23708";
+  public static final String s23709 = "23709";
+  public static final String s23710 = "23710";
+  public static final String s23711 = "23711";
+  public static final String s23712 = "23712";
+  public static final String s23713 = "23713";
+  public static final String s23714 = "23714";
+  public static final String s23715 = "23715";
+  public static final String s23716 = "23716";
+  public static final String s23717 = "23717";
+  public static final String s23718 = "23718";
+  public static final String s23719 = "23719";
+  public static final String s23720 = "23720";
+  public static final String s23721 = "23721";
+  public static final String s23722 = "23722";
+  public static final String s23723 = "23723";
+  public static final String s23724 = "23724";
+  public static final String s23725 = "23725";
+  public static final String s23726 = "23726";
+  public static final String s23727 = "23727";
+  public static final String s23728 = "23728";
+  public static final String s23729 = "23729";
+  public static final String s23730 = "23730";
+  public static final String s23731 = "23731";
+  public static final String s23732 = "23732";
+  public static final String s23733 = "23733";
+  public static final String s23734 = "23734";
+  public static final String s23735 = "23735";
+  public static final String s23736 = "23736";
+  public static final String s23737 = "23737";
+  public static final String s23738 = "23738";
+  public static final String s23739 = "23739";
+  public static final String s23740 = "23740";
+  public static final String s23741 = "23741";
+  public static final String s23742 = "23742";
+  public static final String s23743 = "23743";
+  public static final String s23744 = "23744";
+  public static final String s23745 = "23745";
+  public static final String s23746 = "23746";
+  public static final String s23747 = "23747";
+  public static final String s23748 = "23748";
+  public static final String s23749 = "23749";
+  public static final String s23750 = "23750";
+  public static final String s23751 = "23751";
+  public static final String s23752 = "23752";
+  public static final String s23753 = "23753";
+  public static final String s23754 = "23754";
+  public static final String s23755 = "23755";
+  public static final String s23756 = "23756";
+  public static final String s23757 = "23757";
+  public static final String s23758 = "23758";
+  public static final String s23759 = "23759";
+  public static final String s23760 = "23760";
+  public static final String s23761 = "23761";
+  public static final String s23762 = "23762";
+  public static final String s23763 = "23763";
+  public static final String s23764 = "23764";
+  public static final String s23765 = "23765";
+  public static final String s23766 = "23766";
+  public static final String s23767 = "23767";
+  public static final String s23768 = "23768";
+  public static final String s23769 = "23769";
+  public static final String s23770 = "23770";
+  public static final String s23771 = "23771";
+  public static final String s23772 = "23772";
+  public static final String s23773 = "23773";
+  public static final String s23774 = "23774";
+  public static final String s23775 = "23775";
+  public static final String s23776 = "23776";
+  public static final String s23777 = "23777";
+  public static final String s23778 = "23778";
+  public static final String s23779 = "23779";
+  public static final String s23780 = "23780";
+  public static final String s23781 = "23781";
+  public static final String s23782 = "23782";
+  public static final String s23783 = "23783";
+  public static final String s23784 = "23784";
+  public static final String s23785 = "23785";
+  public static final String s23786 = "23786";
+  public static final String s23787 = "23787";
+  public static final String s23788 = "23788";
+  public static final String s23789 = "23789";
+  public static final String s23790 = "23790";
+  public static final String s23791 = "23791";
+  public static final String s23792 = "23792";
+  public static final String s23793 = "23793";
+  public static final String s23794 = "23794";
+  public static final String s23795 = "23795";
+  public static final String s23796 = "23796";
+  public static final String s23797 = "23797";
+  public static final String s23798 = "23798";
+  public static final String s23799 = "23799";
+  public static final String s23800 = "23800";
+  public static final String s23801 = "23801";
+  public static final String s23802 = "23802";
+  public static final String s23803 = "23803";
+  public static final String s23804 = "23804";
+  public static final String s23805 = "23805";
+  public static final String s23806 = "23806";
+  public static final String s23807 = "23807";
+  public static final String s23808 = "23808";
+  public static final String s23809 = "23809";
+  public static final String s23810 = "23810";
+  public static final String s23811 = "23811";
+  public static final String s23812 = "23812";
+  public static final String s23813 = "23813";
+  public static final String s23814 = "23814";
+  public static final String s23815 = "23815";
+  public static final String s23816 = "23816";
+  public static final String s23817 = "23817";
+  public static final String s23818 = "23818";
+  public static final String s23819 = "23819";
+  public static final String s23820 = "23820";
+  public static final String s23821 = "23821";
+  public static final String s23822 = "23822";
+  public static final String s23823 = "23823";
+  public static final String s23824 = "23824";
+  public static final String s23825 = "23825";
+  public static final String s23826 = "23826";
+  public static final String s23827 = "23827";
+  public static final String s23828 = "23828";
+  public static final String s23829 = "23829";
+  public static final String s23830 = "23830";
+  public static final String s23831 = "23831";
+  public static final String s23832 = "23832";
+  public static final String s23833 = "23833";
+  public static final String s23834 = "23834";
+  public static final String s23835 = "23835";
+  public static final String s23836 = "23836";
+  public static final String s23837 = "23837";
+  public static final String s23838 = "23838";
+  public static final String s23839 = "23839";
+  public static final String s23840 = "23840";
+  public static final String s23841 = "23841";
+  public static final String s23842 = "23842";
+  public static final String s23843 = "23843";
+  public static final String s23844 = "23844";
+  public static final String s23845 = "23845";
+  public static final String s23846 = "23846";
+  public static final String s23847 = "23847";
+  public static final String s23848 = "23848";
+  public static final String s23849 = "23849";
+  public static final String s23850 = "23850";
+  public static final String s23851 = "23851";
+  public static final String s23852 = "23852";
+  public static final String s23853 = "23853";
+  public static final String s23854 = "23854";
+  public static final String s23855 = "23855";
+  public static final String s23856 = "23856";
+  public static final String s23857 = "23857";
+  public static final String s23858 = "23858";
+  public static final String s23859 = "23859";
+  public static final String s23860 = "23860";
+  public static final String s23861 = "23861";
+  public static final String s23862 = "23862";
+  public static final String s23863 = "23863";
+  public static final String s23864 = "23864";
+  public static final String s23865 = "23865";
+  public static final String s23866 = "23866";
+  public static final String s23867 = "23867";
+  public static final String s23868 = "23868";
+  public static final String s23869 = "23869";
+  public static final String s23870 = "23870";
+  public static final String s23871 = "23871";
+  public static final String s23872 = "23872";
+  public static final String s23873 = "23873";
+  public static final String s23874 = "23874";
+  public static final String s23875 = "23875";
+  public static final String s23876 = "23876";
+  public static final String s23877 = "23877";
+  public static final String s23878 = "23878";
+  public static final String s23879 = "23879";
+  public static final String s23880 = "23880";
+  public static final String s23881 = "23881";
+  public static final String s23882 = "23882";
+  public static final String s23883 = "23883";
+  public static final String s23884 = "23884";
+  public static final String s23885 = "23885";
+  public static final String s23886 = "23886";
+  public static final String s23887 = "23887";
+  public static final String s23888 = "23888";
+  public static final String s23889 = "23889";
+  public static final String s23890 = "23890";
+  public static final String s23891 = "23891";
+  public static final String s23892 = "23892";
+  public static final String s23893 = "23893";
+  public static final String s23894 = "23894";
+  public static final String s23895 = "23895";
+  public static final String s23896 = "23896";
+  public static final String s23897 = "23897";
+  public static final String s23898 = "23898";
+  public static final String s23899 = "23899";
+  public static final String s23900 = "23900";
+  public static final String s23901 = "23901";
+  public static final String s23902 = "23902";
+  public static final String s23903 = "23903";
+  public static final String s23904 = "23904";
+  public static final String s23905 = "23905";
+  public static final String s23906 = "23906";
+  public static final String s23907 = "23907";
+  public static final String s23908 = "23908";
+  public static final String s23909 = "23909";
+  public static final String s23910 = "23910";
+  public static final String s23911 = "23911";
+  public static final String s23912 = "23912";
+  public static final String s23913 = "23913";
+  public static final String s23914 = "23914";
+  public static final String s23915 = "23915";
+  public static final String s23916 = "23916";
+  public static final String s23917 = "23917";
+  public static final String s23918 = "23918";
+  public static final String s23919 = "23919";
+  public static final String s23920 = "23920";
+  public static final String s23921 = "23921";
+  public static final String s23922 = "23922";
+  public static final String s23923 = "23923";
+  public static final String s23924 = "23924";
+  public static final String s23925 = "23925";
+  public static final String s23926 = "23926";
+  public static final String s23927 = "23927";
+  public static final String s23928 = "23928";
+  public static final String s23929 = "23929";
+  public static final String s23930 = "23930";
+  public static final String s23931 = "23931";
+  public static final String s23932 = "23932";
+  public static final String s23933 = "23933";
+  public static final String s23934 = "23934";
+  public static final String s23935 = "23935";
+  public static final String s23936 = "23936";
+  public static final String s23937 = "23937";
+  public static final String s23938 = "23938";
+  public static final String s23939 = "23939";
+  public static final String s23940 = "23940";
+  public static final String s23941 = "23941";
+  public static final String s23942 = "23942";
+  public static final String s23943 = "23943";
+  public static final String s23944 = "23944";
+  public static final String s23945 = "23945";
+  public static final String s23946 = "23946";
+  public static final String s23947 = "23947";
+  public static final String s23948 = "23948";
+  public static final String s23949 = "23949";
+  public static final String s23950 = "23950";
+  public static final String s23951 = "23951";
+  public static final String s23952 = "23952";
+  public static final String s23953 = "23953";
+  public static final String s23954 = "23954";
+  public static final String s23955 = "23955";
+  public static final String s23956 = "23956";
+  public static final String s23957 = "23957";
+  public static final String s23958 = "23958";
+  public static final String s23959 = "23959";
+  public static final String s23960 = "23960";
+  public static final String s23961 = "23961";
+  public static final String s23962 = "23962";
+  public static final String s23963 = "23963";
+  public static final String s23964 = "23964";
+  public static final String s23965 = "23965";
+  public static final String s23966 = "23966";
+  public static final String s23967 = "23967";
+  public static final String s23968 = "23968";
+  public static final String s23969 = "23969";
+  public static final String s23970 = "23970";
+  public static final String s23971 = "23971";
+  public static final String s23972 = "23972";
+  public static final String s23973 = "23973";
+  public static final String s23974 = "23974";
+  public static final String s23975 = "23975";
+  public static final String s23976 = "23976";
+  public static final String s23977 = "23977";
+  public static final String s23978 = "23978";
+  public static final String s23979 = "23979";
+  public static final String s23980 = "23980";
+  public static final String s23981 = "23981";
+  public static final String s23982 = "23982";
+  public static final String s23983 = "23983";
+  public static final String s23984 = "23984";
+  public static final String s23985 = "23985";
+  public static final String s23986 = "23986";
+  public static final String s23987 = "23987";
+  public static final String s23988 = "23988";
+  public static final String s23989 = "23989";
+  public static final String s23990 = "23990";
+  public static final String s23991 = "23991";
+  public static final String s23992 = "23992";
+  public static final String s23993 = "23993";
+  public static final String s23994 = "23994";
+  public static final String s23995 = "23995";
+  public static final String s23996 = "23996";
+  public static final String s23997 = "23997";
+  public static final String s23998 = "23998";
+  public static final String s23999 = "23999";
+  public static final String s24000 = "24000";
+  public static final String s24001 = "24001";
+  public static final String s24002 = "24002";
+  public static final String s24003 = "24003";
+  public static final String s24004 = "24004";
+  public static final String s24005 = "24005";
+  public static final String s24006 = "24006";
+  public static final String s24007 = "24007";
+  public static final String s24008 = "24008";
+  public static final String s24009 = "24009";
+  public static final String s24010 = "24010";
+  public static final String s24011 = "24011";
+  public static final String s24012 = "24012";
+  public static final String s24013 = "24013";
+  public static final String s24014 = "24014";
+  public static final String s24015 = "24015";
+  public static final String s24016 = "24016";
+  public static final String s24017 = "24017";
+  public static final String s24018 = "24018";
+  public static final String s24019 = "24019";
+  public static final String s24020 = "24020";
+  public static final String s24021 = "24021";
+  public static final String s24022 = "24022";
+  public static final String s24023 = "24023";
+  public static final String s24024 = "24024";
+  public static final String s24025 = "24025";
+  public static final String s24026 = "24026";
+  public static final String s24027 = "24027";
+  public static final String s24028 = "24028";
+  public static final String s24029 = "24029";
+  public static final String s24030 = "24030";
+  public static final String s24031 = "24031";
+  public static final String s24032 = "24032";
+  public static final String s24033 = "24033";
+  public static final String s24034 = "24034";
+  public static final String s24035 = "24035";
+  public static final String s24036 = "24036";
+  public static final String s24037 = "24037";
+  public static final String s24038 = "24038";
+  public static final String s24039 = "24039";
+  public static final String s24040 = "24040";
+  public static final String s24041 = "24041";
+  public static final String s24042 = "24042";
+  public static final String s24043 = "24043";
+  public static final String s24044 = "24044";
+  public static final String s24045 = "24045";
+  public static final String s24046 = "24046";
+  public static final String s24047 = "24047";
+  public static final String s24048 = "24048";
+  public static final String s24049 = "24049";
+  public static final String s24050 = "24050";
+  public static final String s24051 = "24051";
+  public static final String s24052 = "24052";
+  public static final String s24053 = "24053";
+  public static final String s24054 = "24054";
+  public static final String s24055 = "24055";
+  public static final String s24056 = "24056";
+  public static final String s24057 = "24057";
+  public static final String s24058 = "24058";
+  public static final String s24059 = "24059";
+  public static final String s24060 = "24060";
+  public static final String s24061 = "24061";
+  public static final String s24062 = "24062";
+  public static final String s24063 = "24063";
+  public static final String s24064 = "24064";
+  public static final String s24065 = "24065";
+  public static final String s24066 = "24066";
+  public static final String s24067 = "24067";
+  public static final String s24068 = "24068";
+  public static final String s24069 = "24069";
+  public static final String s24070 = "24070";
+  public static final String s24071 = "24071";
+  public static final String s24072 = "24072";
+  public static final String s24073 = "24073";
+  public static final String s24074 = "24074";
+  public static final String s24075 = "24075";
+  public static final String s24076 = "24076";
+  public static final String s24077 = "24077";
+  public static final String s24078 = "24078";
+  public static final String s24079 = "24079";
+  public static final String s24080 = "24080";
+  public static final String s24081 = "24081";
+  public static final String s24082 = "24082";
+  public static final String s24083 = "24083";
+  public static final String s24084 = "24084";
+  public static final String s24085 = "24085";
+  public static final String s24086 = "24086";
+  public static final String s24087 = "24087";
+  public static final String s24088 = "24088";
+  public static final String s24089 = "24089";
+  public static final String s24090 = "24090";
+  public static final String s24091 = "24091";
+  public static final String s24092 = "24092";
+  public static final String s24093 = "24093";
+  public static final String s24094 = "24094";
+  public static final String s24095 = "24095";
+  public static final String s24096 = "24096";
+  public static final String s24097 = "24097";
+  public static final String s24098 = "24098";
+  public static final String s24099 = "24099";
+  public static final String s24100 = "24100";
+  public static final String s24101 = "24101";
+  public static final String s24102 = "24102";
+  public static final String s24103 = "24103";
+  public static final String s24104 = "24104";
+  public static final String s24105 = "24105";
+  public static final String s24106 = "24106";
+  public static final String s24107 = "24107";
+  public static final String s24108 = "24108";
+  public static final String s24109 = "24109";
+  public static final String s24110 = "24110";
+  public static final String s24111 = "24111";
+  public static final String s24112 = "24112";
+  public static final String s24113 = "24113";
+  public static final String s24114 = "24114";
+  public static final String s24115 = "24115";
+  public static final String s24116 = "24116";
+  public static final String s24117 = "24117";
+  public static final String s24118 = "24118";
+  public static final String s24119 = "24119";
+  public static final String s24120 = "24120";
+  public static final String s24121 = "24121";
+  public static final String s24122 = "24122";
+  public static final String s24123 = "24123";
+  public static final String s24124 = "24124";
+  public static final String s24125 = "24125";
+  public static final String s24126 = "24126";
+  public static final String s24127 = "24127";
+  public static final String s24128 = "24128";
+  public static final String s24129 = "24129";
+  public static final String s24130 = "24130";
+  public static final String s24131 = "24131";
+  public static final String s24132 = "24132";
+  public static final String s24133 = "24133";
+  public static final String s24134 = "24134";
+  public static final String s24135 = "24135";
+  public static final String s24136 = "24136";
+  public static final String s24137 = "24137";
+  public static final String s24138 = "24138";
+  public static final String s24139 = "24139";
+  public static final String s24140 = "24140";
+  public static final String s24141 = "24141";
+  public static final String s24142 = "24142";
+  public static final String s24143 = "24143";
+  public static final String s24144 = "24144";
+  public static final String s24145 = "24145";
+  public static final String s24146 = "24146";
+  public static final String s24147 = "24147";
+  public static final String s24148 = "24148";
+  public static final String s24149 = "24149";
+  public static final String s24150 = "24150";
+  public static final String s24151 = "24151";
+  public static final String s24152 = "24152";
+  public static final String s24153 = "24153";
+  public static final String s24154 = "24154";
+  public static final String s24155 = "24155";
+  public static final String s24156 = "24156";
+  public static final String s24157 = "24157";
+  public static final String s24158 = "24158";
+  public static final String s24159 = "24159";
+  public static final String s24160 = "24160";
+  public static final String s24161 = "24161";
+  public static final String s24162 = "24162";
+  public static final String s24163 = "24163";
+  public static final String s24164 = "24164";
+  public static final String s24165 = "24165";
+  public static final String s24166 = "24166";
+  public static final String s24167 = "24167";
+  public static final String s24168 = "24168";
+  public static final String s24169 = "24169";
+  public static final String s24170 = "24170";
+  public static final String s24171 = "24171";
+  public static final String s24172 = "24172";
+  public static final String s24173 = "24173";
+  public static final String s24174 = "24174";
+  public static final String s24175 = "24175";
+  public static final String s24176 = "24176";
+  public static final String s24177 = "24177";
+  public static final String s24178 = "24178";
+  public static final String s24179 = "24179";
+  public static final String s24180 = "24180";
+  public static final String s24181 = "24181";
+  public static final String s24182 = "24182";
+  public static final String s24183 = "24183";
+  public static final String s24184 = "24184";
+  public static final String s24185 = "24185";
+  public static final String s24186 = "24186";
+  public static final String s24187 = "24187";
+  public static final String s24188 = "24188";
+  public static final String s24189 = "24189";
+  public static final String s24190 = "24190";
+  public static final String s24191 = "24191";
+  public static final String s24192 = "24192";
+  public static final String s24193 = "24193";
+  public static final String s24194 = "24194";
+  public static final String s24195 = "24195";
+  public static final String s24196 = "24196";
+  public static final String s24197 = "24197";
+  public static final String s24198 = "24198";
+  public static final String s24199 = "24199";
+  public static final String s24200 = "24200";
+  public static final String s24201 = "24201";
+  public static final String s24202 = "24202";
+  public static final String s24203 = "24203";
+  public static final String s24204 = "24204";
+  public static final String s24205 = "24205";
+  public static final String s24206 = "24206";
+  public static final String s24207 = "24207";
+  public static final String s24208 = "24208";
+  public static final String s24209 = "24209";
+  public static final String s24210 = "24210";
+  public static final String s24211 = "24211";
+  public static final String s24212 = "24212";
+  public static final String s24213 = "24213";
+  public static final String s24214 = "24214";
+  public static final String s24215 = "24215";
+  public static final String s24216 = "24216";
+  public static final String s24217 = "24217";
+  public static final String s24218 = "24218";
+  public static final String s24219 = "24219";
+  public static final String s24220 = "24220";
+  public static final String s24221 = "24221";
+  public static final String s24222 = "24222";
+  public static final String s24223 = "24223";
+  public static final String s24224 = "24224";
+  public static final String s24225 = "24225";
+  public static final String s24226 = "24226";
+  public static final String s24227 = "24227";
+  public static final String s24228 = "24228";
+  public static final String s24229 = "24229";
+  public static final String s24230 = "24230";
+  public static final String s24231 = "24231";
+  public static final String s24232 = "24232";
+  public static final String s24233 = "24233";
+  public static final String s24234 = "24234";
+  public static final String s24235 = "24235";
+  public static final String s24236 = "24236";
+  public static final String s24237 = "24237";
+  public static final String s24238 = "24238";
+  public static final String s24239 = "24239";
+  public static final String s24240 = "24240";
+  public static final String s24241 = "24241";
+  public static final String s24242 = "24242";
+  public static final String s24243 = "24243";
+  public static final String s24244 = "24244";
+  public static final String s24245 = "24245";
+  public static final String s24246 = "24246";
+  public static final String s24247 = "24247";
+  public static final String s24248 = "24248";
+  public static final String s24249 = "24249";
+  public static final String s24250 = "24250";
+  public static final String s24251 = "24251";
+  public static final String s24252 = "24252";
+  public static final String s24253 = "24253";
+  public static final String s24254 = "24254";
+  public static final String s24255 = "24255";
+  public static final String s24256 = "24256";
+  public static final String s24257 = "24257";
+  public static final String s24258 = "24258";
+  public static final String s24259 = "24259";
+  public static final String s24260 = "24260";
+  public static final String s24261 = "24261";
+  public static final String s24262 = "24262";
+  public static final String s24263 = "24263";
+  public static final String s24264 = "24264";
+  public static final String s24265 = "24265";
+  public static final String s24266 = "24266";
+  public static final String s24267 = "24267";
+  public static final String s24268 = "24268";
+  public static final String s24269 = "24269";
+  public static final String s24270 = "24270";
+  public static final String s24271 = "24271";
+  public static final String s24272 = "24272";
+  public static final String s24273 = "24273";
+  public static final String s24274 = "24274";
+  public static final String s24275 = "24275";
+  public static final String s24276 = "24276";
+  public static final String s24277 = "24277";
+  public static final String s24278 = "24278";
+  public static final String s24279 = "24279";
+  public static final String s24280 = "24280";
+  public static final String s24281 = "24281";
+  public static final String s24282 = "24282";
+  public static final String s24283 = "24283";
+  public static final String s24284 = "24284";
+  public static final String s24285 = "24285";
+  public static final String s24286 = "24286";
+  public static final String s24287 = "24287";
+  public static final String s24288 = "24288";
+  public static final String s24289 = "24289";
+  public static final String s24290 = "24290";
+  public static final String s24291 = "24291";
+  public static final String s24292 = "24292";
+  public static final String s24293 = "24293";
+  public static final String s24294 = "24294";
+  public static final String s24295 = "24295";
+  public static final String s24296 = "24296";
+  public static final String s24297 = "24297";
+  public static final String s24298 = "24298";
+  public static final String s24299 = "24299";
+  public static final String s24300 = "24300";
+  public static final String s24301 = "24301";
+  public static final String s24302 = "24302";
+  public static final String s24303 = "24303";
+  public static final String s24304 = "24304";
+  public static final String s24305 = "24305";
+  public static final String s24306 = "24306";
+  public static final String s24307 = "24307";
+  public static final String s24308 = "24308";
+  public static final String s24309 = "24309";
+  public static final String s24310 = "24310";
+  public static final String s24311 = "24311";
+  public static final String s24312 = "24312";
+  public static final String s24313 = "24313";
+  public static final String s24314 = "24314";
+  public static final String s24315 = "24315";
+  public static final String s24316 = "24316";
+  public static final String s24317 = "24317";
+  public static final String s24318 = "24318";
+  public static final String s24319 = "24319";
+  public static final String s24320 = "24320";
+  public static final String s24321 = "24321";
+  public static final String s24322 = "24322";
+  public static final String s24323 = "24323";
+  public static final String s24324 = "24324";
+  public static final String s24325 = "24325";
+  public static final String s24326 = "24326";
+  public static final String s24327 = "24327";
+  public static final String s24328 = "24328";
+  public static final String s24329 = "24329";
+  public static final String s24330 = "24330";
+  public static final String s24331 = "24331";
+  public static final String s24332 = "24332";
+  public static final String s24333 = "24333";
+  public static final String s24334 = "24334";
+  public static final String s24335 = "24335";
+  public static final String s24336 = "24336";
+  public static final String s24337 = "24337";
+  public static final String s24338 = "24338";
+  public static final String s24339 = "24339";
+  public static final String s24340 = "24340";
+  public static final String s24341 = "24341";
+  public static final String s24342 = "24342";
+  public static final String s24343 = "24343";
+  public static final String s24344 = "24344";
+  public static final String s24345 = "24345";
+  public static final String s24346 = "24346";
+  public static final String s24347 = "24347";
+  public static final String s24348 = "24348";
+  public static final String s24349 = "24349";
+  public static final String s24350 = "24350";
+  public static final String s24351 = "24351";
+  public static final String s24352 = "24352";
+  public static final String s24353 = "24353";
+  public static final String s24354 = "24354";
+  public static final String s24355 = "24355";
+  public static final String s24356 = "24356";
+  public static final String s24357 = "24357";
+  public static final String s24358 = "24358";
+  public static final String s24359 = "24359";
+  public static final String s24360 = "24360";
+  public static final String s24361 = "24361";
+  public static final String s24362 = "24362";
+  public static final String s24363 = "24363";
+  public static final String s24364 = "24364";
+  public static final String s24365 = "24365";
+  public static final String s24366 = "24366";
+  public static final String s24367 = "24367";
+  public static final String s24368 = "24368";
+  public static final String s24369 = "24369";
+  public static final String s24370 = "24370";
+  public static final String s24371 = "24371";
+  public static final String s24372 = "24372";
+  public static final String s24373 = "24373";
+  public static final String s24374 = "24374";
+  public static final String s24375 = "24375";
+  public static final String s24376 = "24376";
+  public static final String s24377 = "24377";
+  public static final String s24378 = "24378";
+  public static final String s24379 = "24379";
+  public static final String s24380 = "24380";
+  public static final String s24381 = "24381";
+  public static final String s24382 = "24382";
+  public static final String s24383 = "24383";
+  public static final String s24384 = "24384";
+  public static final String s24385 = "24385";
+  public static final String s24386 = "24386";
+  public static final String s24387 = "24387";
+  public static final String s24388 = "24388";
+  public static final String s24389 = "24389";
+  public static final String s24390 = "24390";
+  public static final String s24391 = "24391";
+  public static final String s24392 = "24392";
+  public static final String s24393 = "24393";
+  public static final String s24394 = "24394";
+  public static final String s24395 = "24395";
+  public static final String s24396 = "24396";
+  public static final String s24397 = "24397";
+  public static final String s24398 = "24398";
+  public static final String s24399 = "24399";
+  public static final String s24400 = "24400";
+  public static final String s24401 = "24401";
+  public static final String s24402 = "24402";
+  public static final String s24403 = "24403";
+  public static final String s24404 = "24404";
+  public static final String s24405 = "24405";
+  public static final String s24406 = "24406";
+  public static final String s24407 = "24407";
+  public static final String s24408 = "24408";
+  public static final String s24409 = "24409";
+  public static final String s24410 = "24410";
+  public static final String s24411 = "24411";
+  public static final String s24412 = "24412";
+  public static final String s24413 = "24413";
+  public static final String s24414 = "24414";
+  public static final String s24415 = "24415";
+  public static final String s24416 = "24416";
+  public static final String s24417 = "24417";
+  public static final String s24418 = "24418";
+  public static final String s24419 = "24419";
+  public static final String s24420 = "24420";
+  public static final String s24421 = "24421";
+  public static final String s24422 = "24422";
+  public static final String s24423 = "24423";
+  public static final String s24424 = "24424";
+  public static final String s24425 = "24425";
+  public static final String s24426 = "24426";
+  public static final String s24427 = "24427";
+  public static final String s24428 = "24428";
+  public static final String s24429 = "24429";
+  public static final String s24430 = "24430";
+  public static final String s24431 = "24431";
+  public static final String s24432 = "24432";
+  public static final String s24433 = "24433";
+  public static final String s24434 = "24434";
+  public static final String s24435 = "24435";
+  public static final String s24436 = "24436";
+  public static final String s24437 = "24437";
+  public static final String s24438 = "24438";
+  public static final String s24439 = "24439";
+  public static final String s24440 = "24440";
+  public static final String s24441 = "24441";
+  public static final String s24442 = "24442";
+  public static final String s24443 = "24443";
+  public static final String s24444 = "24444";
+  public static final String s24445 = "24445";
+  public static final String s24446 = "24446";
+  public static final String s24447 = "24447";
+  public static final String s24448 = "24448";
+  public static final String s24449 = "24449";
+  public static final String s24450 = "24450";
+  public static final String s24451 = "24451";
+  public static final String s24452 = "24452";
+  public static final String s24453 = "24453";
+  public static final String s24454 = "24454";
+  public static final String s24455 = "24455";
+  public static final String s24456 = "24456";
+  public static final String s24457 = "24457";
+  public static final String s24458 = "24458";
+  public static final String s24459 = "24459";
+  public static final String s24460 = "24460";
+  public static final String s24461 = "24461";
+  public static final String s24462 = "24462";
+  public static final String s24463 = "24463";
+  public static final String s24464 = "24464";
+  public static final String s24465 = "24465";
+  public static final String s24466 = "24466";
+  public static final String s24467 = "24467";
+  public static final String s24468 = "24468";
+  public static final String s24469 = "24469";
+  public static final String s24470 = "24470";
+  public static final String s24471 = "24471";
+  public static final String s24472 = "24472";
+  public static final String s24473 = "24473";
+  public static final String s24474 = "24474";
+  public static final String s24475 = "24475";
+  public static final String s24476 = "24476";
+  public static final String s24477 = "24477";
+  public static final String s24478 = "24478";
+  public static final String s24479 = "24479";
+  public static final String s24480 = "24480";
+  public static final String s24481 = "24481";
+  public static final String s24482 = "24482";
+  public static final String s24483 = "24483";
+  public static final String s24484 = "24484";
+  public static final String s24485 = "24485";
+  public static final String s24486 = "24486";
+  public static final String s24487 = "24487";
+  public static final String s24488 = "24488";
+  public static final String s24489 = "24489";
+  public static final String s24490 = "24490";
+  public static final String s24491 = "24491";
+  public static final String s24492 = "24492";
+  public static final String s24493 = "24493";
+  public static final String s24494 = "24494";
+  public static final String s24495 = "24495";
+  public static final String s24496 = "24496";
+  public static final String s24497 = "24497";
+  public static final String s24498 = "24498";
+  public static final String s24499 = "24499";
+  public static final String s24500 = "24500";
+  public static final String s24501 = "24501";
+  public static final String s24502 = "24502";
+  public static final String s24503 = "24503";
+  public static final String s24504 = "24504";
+  public static final String s24505 = "24505";
+  public static final String s24506 = "24506";
+  public static final String s24507 = "24507";
+  public static final String s24508 = "24508";
+  public static final String s24509 = "24509";
+  public static final String s24510 = "24510";
+  public static final String s24511 = "24511";
+  public static final String s24512 = "24512";
+  public static final String s24513 = "24513";
+  public static final String s24514 = "24514";
+  public static final String s24515 = "24515";
+  public static final String s24516 = "24516";
+  public static final String s24517 = "24517";
+  public static final String s24518 = "24518";
+  public static final String s24519 = "24519";
+  public static final String s24520 = "24520";
+  public static final String s24521 = "24521";
+  public static final String s24522 = "24522";
+  public static final String s24523 = "24523";
+  public static final String s24524 = "24524";
+  public static final String s24525 = "24525";
+  public static final String s24526 = "24526";
+  public static final String s24527 = "24527";
+  public static final String s24528 = "24528";
+  public static final String s24529 = "24529";
+  public static final String s24530 = "24530";
+  public static final String s24531 = "24531";
+  public static final String s24532 = "24532";
+  public static final String s24533 = "24533";
+  public static final String s24534 = "24534";
+  public static final String s24535 = "24535";
+  public static final String s24536 = "24536";
+  public static final String s24537 = "24537";
+  public static final String s24538 = "24538";
+  public static final String s24539 = "24539";
+  public static final String s24540 = "24540";
+  public static final String s24541 = "24541";
+  public static final String s24542 = "24542";
+  public static final String s24543 = "24543";
+  public static final String s24544 = "24544";
+  public static final String s24545 = "24545";
+  public static final String s24546 = "24546";
+  public static final String s24547 = "24547";
+  public static final String s24548 = "24548";
+  public static final String s24549 = "24549";
+  public static final String s24550 = "24550";
+  public static final String s24551 = "24551";
+  public static final String s24552 = "24552";
+  public static final String s24553 = "24553";
+  public static final String s24554 = "24554";
+  public static final String s24555 = "24555";
+  public static final String s24556 = "24556";
+  public static final String s24557 = "24557";
+  public static final String s24558 = "24558";
+  public static final String s24559 = "24559";
+  public static final String s24560 = "24560";
+  public static final String s24561 = "24561";
+  public static final String s24562 = "24562";
+  public static final String s24563 = "24563";
+  public static final String s24564 = "24564";
+  public static final String s24565 = "24565";
+  public static final String s24566 = "24566";
+  public static final String s24567 = "24567";
+  public static final String s24568 = "24568";
+  public static final String s24569 = "24569";
+  public static final String s24570 = "24570";
+  public static final String s24571 = "24571";
+  public static final String s24572 = "24572";
+  public static final String s24573 = "24573";
+  public static final String s24574 = "24574";
+  public static final String s24575 = "24575";
+  public static final String s24576 = "24576";
+  public static final String s24577 = "24577";
+  public static final String s24578 = "24578";
+  public static final String s24579 = "24579";
+  public static final String s24580 = "24580";
+  public static final String s24581 = "24581";
+  public static final String s24582 = "24582";
+  public static final String s24583 = "24583";
+  public static final String s24584 = "24584";
+  public static final String s24585 = "24585";
+  public static final String s24586 = "24586";
+  public static final String s24587 = "24587";
+  public static final String s24588 = "24588";
+  public static final String s24589 = "24589";
+  public static final String s24590 = "24590";
+  public static final String s24591 = "24591";
+  public static final String s24592 = "24592";
+  public static final String s24593 = "24593";
+  public static final String s24594 = "24594";
+  public static final String s24595 = "24595";
+  public static final String s24596 = "24596";
+  public static final String s24597 = "24597";
+  public static final String s24598 = "24598";
+  public static final String s24599 = "24599";
+  public static final String s24600 = "24600";
+  public static final String s24601 = "24601";
+  public static final String s24602 = "24602";
+  public static final String s24603 = "24603";
+  public static final String s24604 = "24604";
+  public static final String s24605 = "24605";
+  public static final String s24606 = "24606";
+  public static final String s24607 = "24607";
+  public static final String s24608 = "24608";
+  public static final String s24609 = "24609";
+  public static final String s24610 = "24610";
+  public static final String s24611 = "24611";
+  public static final String s24612 = "24612";
+  public static final String s24613 = "24613";
+  public static final String s24614 = "24614";
+  public static final String s24615 = "24615";
+  public static final String s24616 = "24616";
+  public static final String s24617 = "24617";
+  public static final String s24618 = "24618";
+  public static final String s24619 = "24619";
+  public static final String s24620 = "24620";
+  public static final String s24621 = "24621";
+  public static final String s24622 = "24622";
+  public static final String s24623 = "24623";
+  public static final String s24624 = "24624";
+  public static final String s24625 = "24625";
+  public static final String s24626 = "24626";
+  public static final String s24627 = "24627";
+  public static final String s24628 = "24628";
+  public static final String s24629 = "24629";
+  public static final String s24630 = "24630";
+  public static final String s24631 = "24631";
+  public static final String s24632 = "24632";
+  public static final String s24633 = "24633";
+  public static final String s24634 = "24634";
+  public static final String s24635 = "24635";
+  public static final String s24636 = "24636";
+  public static final String s24637 = "24637";
+  public static final String s24638 = "24638";
+  public static final String s24639 = "24639";
+  public static final String s24640 = "24640";
+  public static final String s24641 = "24641";
+  public static final String s24642 = "24642";
+  public static final String s24643 = "24643";
+  public static final String s24644 = "24644";
+  public static final String s24645 = "24645";
+  public static final String s24646 = "24646";
+  public static final String s24647 = "24647";
+  public static final String s24648 = "24648";
+  public static final String s24649 = "24649";
+  public static final String s24650 = "24650";
+  public static final String s24651 = "24651";
+  public static final String s24652 = "24652";
+  public static final String s24653 = "24653";
+  public static final String s24654 = "24654";
+  public static final String s24655 = "24655";
+  public static final String s24656 = "24656";
+  public static final String s24657 = "24657";
+  public static final String s24658 = "24658";
+  public static final String s24659 = "24659";
+  public static final String s24660 = "24660";
+  public static final String s24661 = "24661";
+  public static final String s24662 = "24662";
+  public static final String s24663 = "24663";
+  public static final String s24664 = "24664";
+  public static final String s24665 = "24665";
+  public static final String s24666 = "24666";
+  public static final String s24667 = "24667";
+  public static final String s24668 = "24668";
+  public static final String s24669 = "24669";
+  public static final String s24670 = "24670";
+  public static final String s24671 = "24671";
+  public static final String s24672 = "24672";
+  public static final String s24673 = "24673";
+  public static final String s24674 = "24674";
+  public static final String s24675 = "24675";
+  public static final String s24676 = "24676";
+  public static final String s24677 = "24677";
+  public static final String s24678 = "24678";
+  public static final String s24679 = "24679";
+  public static final String s24680 = "24680";
+  public static final String s24681 = "24681";
+  public static final String s24682 = "24682";
+  public static final String s24683 = "24683";
+  public static final String s24684 = "24684";
+  public static final String s24685 = "24685";
+  public static final String s24686 = "24686";
+  public static final String s24687 = "24687";
+  public static final String s24688 = "24688";
+  public static final String s24689 = "24689";
+  public static final String s24690 = "24690";
+  public static final String s24691 = "24691";
+  public static final String s24692 = "24692";
+  public static final String s24693 = "24693";
+  public static final String s24694 = "24694";
+  public static final String s24695 = "24695";
+  public static final String s24696 = "24696";
+  public static final String s24697 = "24697";
+  public static final String s24698 = "24698";
+  public static final String s24699 = "24699";
+  public static final String s24700 = "24700";
+  public static final String s24701 = "24701";
+  public static final String s24702 = "24702";
+  public static final String s24703 = "24703";
+  public static final String s24704 = "24704";
+  public static final String s24705 = "24705";
+  public static final String s24706 = "24706";
+  public static final String s24707 = "24707";
+  public static final String s24708 = "24708";
+  public static final String s24709 = "24709";
+  public static final String s24710 = "24710";
+  public static final String s24711 = "24711";
+  public static final String s24712 = "24712";
+  public static final String s24713 = "24713";
+  public static final String s24714 = "24714";
+  public static final String s24715 = "24715";
+  public static final String s24716 = "24716";
+  public static final String s24717 = "24717";
+  public static final String s24718 = "24718";
+  public static final String s24719 = "24719";
+  public static final String s24720 = "24720";
+  public static final String s24721 = "24721";
+  public static final String s24722 = "24722";
+  public static final String s24723 = "24723";
+  public static final String s24724 = "24724";
+  public static final String s24725 = "24725";
+  public static final String s24726 = "24726";
+  public static final String s24727 = "24727";
+  public static final String s24728 = "24728";
+  public static final String s24729 = "24729";
+  public static final String s24730 = "24730";
+  public static final String s24731 = "24731";
+  public static final String s24732 = "24732";
+  public static final String s24733 = "24733";
+  public static final String s24734 = "24734";
+  public static final String s24735 = "24735";
+  public static final String s24736 = "24736";
+  public static final String s24737 = "24737";
+  public static final String s24738 = "24738";
+  public static final String s24739 = "24739";
+  public static final String s24740 = "24740";
+  public static final String s24741 = "24741";
+  public static final String s24742 = "24742";
+  public static final String s24743 = "24743";
+  public static final String s24744 = "24744";
+  public static final String s24745 = "24745";
+  public static final String s24746 = "24746";
+  public static final String s24747 = "24747";
+  public static final String s24748 = "24748";
+  public static final String s24749 = "24749";
+  public static final String s24750 = "24750";
+  public static final String s24751 = "24751";
+  public static final String s24752 = "24752";
+  public static final String s24753 = "24753";
+  public static final String s24754 = "24754";
+  public static final String s24755 = "24755";
+  public static final String s24756 = "24756";
+  public static final String s24757 = "24757";
+  public static final String s24758 = "24758";
+  public static final String s24759 = "24759";
+  public static final String s24760 = "24760";
+  public static final String s24761 = "24761";
+  public static final String s24762 = "24762";
+  public static final String s24763 = "24763";
+  public static final String s24764 = "24764";
+  public static final String s24765 = "24765";
+  public static final String s24766 = "24766";
+  public static final String s24767 = "24767";
+  public static final String s24768 = "24768";
+  public static final String s24769 = "24769";
+  public static final String s24770 = "24770";
+  public static final String s24771 = "24771";
+  public static final String s24772 = "24772";
+  public static final String s24773 = "24773";
+  public static final String s24774 = "24774";
+  public static final String s24775 = "24775";
+  public static final String s24776 = "24776";
+  public static final String s24777 = "24777";
+  public static final String s24778 = "24778";
+  public static final String s24779 = "24779";
+  public static final String s24780 = "24780";
+  public static final String s24781 = "24781";
+  public static final String s24782 = "24782";
+  public static final String s24783 = "24783";
+  public static final String s24784 = "24784";
+  public static final String s24785 = "24785";
+  public static final String s24786 = "24786";
+  public static final String s24787 = "24787";
+  public static final String s24788 = "24788";
+  public static final String s24789 = "24789";
+  public static final String s24790 = "24790";
+  public static final String s24791 = "24791";
+  public static final String s24792 = "24792";
+  public static final String s24793 = "24793";
+  public static final String s24794 = "24794";
+  public static final String s24795 = "24795";
+  public static final String s24796 = "24796";
+  public static final String s24797 = "24797";
+  public static final String s24798 = "24798";
+  public static final String s24799 = "24799";
+  public static final String s24800 = "24800";
+  public static final String s24801 = "24801";
+  public static final String s24802 = "24802";
+  public static final String s24803 = "24803";
+  public static final String s24804 = "24804";
+  public static final String s24805 = "24805";
+  public static final String s24806 = "24806";
+  public static final String s24807 = "24807";
+  public static final String s24808 = "24808";
+  public static final String s24809 = "24809";
+  public static final String s24810 = "24810";
+  public static final String s24811 = "24811";
+  public static final String s24812 = "24812";
+  public static final String s24813 = "24813";
+  public static final String s24814 = "24814";
+  public static final String s24815 = "24815";
+  public static final String s24816 = "24816";
+  public static final String s24817 = "24817";
+  public static final String s24818 = "24818";
+  public static final String s24819 = "24819";
+  public static final String s24820 = "24820";
+  public static final String s24821 = "24821";
+  public static final String s24822 = "24822";
+  public static final String s24823 = "24823";
+  public static final String s24824 = "24824";
+  public static final String s24825 = "24825";
+  public static final String s24826 = "24826";
+  public static final String s24827 = "24827";
+  public static final String s24828 = "24828";
+  public static final String s24829 = "24829";
+  public static final String s24830 = "24830";
+  public static final String s24831 = "24831";
+  public static final String s24832 = "24832";
+  public static final String s24833 = "24833";
+  public static final String s24834 = "24834";
+  public static final String s24835 = "24835";
+  public static final String s24836 = "24836";
+  public static final String s24837 = "24837";
+  public static final String s24838 = "24838";
+  public static final String s24839 = "24839";
+  public static final String s24840 = "24840";
+  public static final String s24841 = "24841";
+  public static final String s24842 = "24842";
+  public static final String s24843 = "24843";
+  public static final String s24844 = "24844";
+  public static final String s24845 = "24845";
+  public static final String s24846 = "24846";
+  public static final String s24847 = "24847";
+  public static final String s24848 = "24848";
+  public static final String s24849 = "24849";
+  public static final String s24850 = "24850";
+  public static final String s24851 = "24851";
+  public static final String s24852 = "24852";
+  public static final String s24853 = "24853";
+  public static final String s24854 = "24854";
+  public static final String s24855 = "24855";
+  public static final String s24856 = "24856";
+  public static final String s24857 = "24857";
+  public static final String s24858 = "24858";
+  public static final String s24859 = "24859";
+  public static final String s24860 = "24860";
+  public static final String s24861 = "24861";
+  public static final String s24862 = "24862";
+  public static final String s24863 = "24863";
+  public static final String s24864 = "24864";
+  public static final String s24865 = "24865";
+  public static final String s24866 = "24866";
+  public static final String s24867 = "24867";
+  public static final String s24868 = "24868";
+  public static final String s24869 = "24869";
+  public static final String s24870 = "24870";
+  public static final String s24871 = "24871";
+  public static final String s24872 = "24872";
+  public static final String s24873 = "24873";
+  public static final String s24874 = "24874";
+  public static final String s24875 = "24875";
+  public static final String s24876 = "24876";
+  public static final String s24877 = "24877";
+  public static final String s24878 = "24878";
+  public static final String s24879 = "24879";
+  public static final String s24880 = "24880";
+  public static final String s24881 = "24881";
+  public static final String s24882 = "24882";
+  public static final String s24883 = "24883";
+  public static final String s24884 = "24884";
+  public static final String s24885 = "24885";
+  public static final String s24886 = "24886";
+  public static final String s24887 = "24887";
+  public static final String s24888 = "24888";
+  public static final String s24889 = "24889";
+  public static final String s24890 = "24890";
+  public static final String s24891 = "24891";
+  public static final String s24892 = "24892";
+  public static final String s24893 = "24893";
+  public static final String s24894 = "24894";
+  public static final String s24895 = "24895";
+  public static final String s24896 = "24896";
+  public static final String s24897 = "24897";
+  public static final String s24898 = "24898";
+  public static final String s24899 = "24899";
+  public static final String s24900 = "24900";
+  public static final String s24901 = "24901";
+  public static final String s24902 = "24902";
+  public static final String s24903 = "24903";
+  public static final String s24904 = "24904";
+  public static final String s24905 = "24905";
+  public static final String s24906 = "24906";
+  public static final String s24907 = "24907";
+  public static final String s24908 = "24908";
+  public static final String s24909 = "24909";
+  public static final String s24910 = "24910";
+  public static final String s24911 = "24911";
+  public static final String s24912 = "24912";
+  public static final String s24913 = "24913";
+  public static final String s24914 = "24914";
+  public static final String s24915 = "24915";
+  public static final String s24916 = "24916";
+  public static final String s24917 = "24917";
+  public static final String s24918 = "24918";
+  public static final String s24919 = "24919";
+  public static final String s24920 = "24920";
+  public static final String s24921 = "24921";
+  public static final String s24922 = "24922";
+  public static final String s24923 = "24923";
+  public static final String s24924 = "24924";
+  public static final String s24925 = "24925";
+  public static final String s24926 = "24926";
+  public static final String s24927 = "24927";
+  public static final String s24928 = "24928";
+  public static final String s24929 = "24929";
+  public static final String s24930 = "24930";
+  public static final String s24931 = "24931";
+  public static final String s24932 = "24932";
+  public static final String s24933 = "24933";
+  public static final String s24934 = "24934";
+  public static final String s24935 = "24935";
+  public static final String s24936 = "24936";
+  public static final String s24937 = "24937";
+  public static final String s24938 = "24938";
+  public static final String s24939 = "24939";
+  public static final String s24940 = "24940";
+  public static final String s24941 = "24941";
+  public static final String s24942 = "24942";
+  public static final String s24943 = "24943";
+  public static final String s24944 = "24944";
+  public static final String s24945 = "24945";
+  public static final String s24946 = "24946";
+  public static final String s24947 = "24947";
+  public static final String s24948 = "24948";
+  public static final String s24949 = "24949";
+  public static final String s24950 = "24950";
+  public static final String s24951 = "24951";
+  public static final String s24952 = "24952";
+  public static final String s24953 = "24953";
+  public static final String s24954 = "24954";
+  public static final String s24955 = "24955";
+  public static final String s24956 = "24956";
+  public static final String s24957 = "24957";
+  public static final String s24958 = "24958";
+  public static final String s24959 = "24959";
+  public static final String s24960 = "24960";
+  public static final String s24961 = "24961";
+  public static final String s24962 = "24962";
+  public static final String s24963 = "24963";
+  public static final String s24964 = "24964";
+  public static final String s24965 = "24965";
+  public static final String s24966 = "24966";
+  public static final String s24967 = "24967";
+  public static final String s24968 = "24968";
+  public static final String s24969 = "24969";
+  public static final String s24970 = "24970";
+  public static final String s24971 = "24971";
+  public static final String s24972 = "24972";
+  public static final String s24973 = "24973";
+  public static final String s24974 = "24974";
+  public static final String s24975 = "24975";
+  public static final String s24976 = "24976";
+  public static final String s24977 = "24977";
+  public static final String s24978 = "24978";
+  public static final String s24979 = "24979";
+  public static final String s24980 = "24980";
+  public static final String s24981 = "24981";
+  public static final String s24982 = "24982";
+  public static final String s24983 = "24983";
+  public static final String s24984 = "24984";
+  public static final String s24985 = "24985";
+  public static final String s24986 = "24986";
+  public static final String s24987 = "24987";
+  public static final String s24988 = "24988";
+  public static final String s24989 = "24989";
+  public static final String s24990 = "24990";
+  public static final String s24991 = "24991";
+  public static final String s24992 = "24992";
+  public static final String s24993 = "24993";
+  public static final String s24994 = "24994";
+  public static final String s24995 = "24995";
+  public static final String s24996 = "24996";
+  public static final String s24997 = "24997";
+  public static final String s24998 = "24998";
+  public static final String s24999 = "24999";
+  public static final String s25000 = "25000";
+  public static final String s25001 = "25001";
+  public static final String s25002 = "25002";
+  public static final String s25003 = "25003";
+  public static final String s25004 = "25004";
+  public static final String s25005 = "25005";
+  public static final String s25006 = "25006";
+  public static final String s25007 = "25007";
+  public static final String s25008 = "25008";
+  public static final String s25009 = "25009";
+  public static final String s25010 = "25010";
+  public static final String s25011 = "25011";
+  public static final String s25012 = "25012";
+  public static final String s25013 = "25013";
+  public static final String s25014 = "25014";
+  public static final String s25015 = "25015";
+  public static final String s25016 = "25016";
+  public static final String s25017 = "25017";
+  public static final String s25018 = "25018";
+  public static final String s25019 = "25019";
+  public static final String s25020 = "25020";
+  public static final String s25021 = "25021";
+  public static final String s25022 = "25022";
+  public static final String s25023 = "25023";
+  public static final String s25024 = "25024";
+  public static final String s25025 = "25025";
+  public static final String s25026 = "25026";
+  public static final String s25027 = "25027";
+  public static final String s25028 = "25028";
+  public static final String s25029 = "25029";
+  public static final String s25030 = "25030";
+  public static final String s25031 = "25031";
+  public static final String s25032 = "25032";
+  public static final String s25033 = "25033";
+  public static final String s25034 = "25034";
+  public static final String s25035 = "25035";
+  public static final String s25036 = "25036";
+  public static final String s25037 = "25037";
+  public static final String s25038 = "25038";
+  public static final String s25039 = "25039";
+  public static final String s25040 = "25040";
+  public static final String s25041 = "25041";
+  public static final String s25042 = "25042";
+  public static final String s25043 = "25043";
+  public static final String s25044 = "25044";
+  public static final String s25045 = "25045";
+  public static final String s25046 = "25046";
+  public static final String s25047 = "25047";
+  public static final String s25048 = "25048";
+  public static final String s25049 = "25049";
+  public static final String s25050 = "25050";
+  public static final String s25051 = "25051";
+  public static final String s25052 = "25052";
+  public static final String s25053 = "25053";
+  public static final String s25054 = "25054";
+  public static final String s25055 = "25055";
+  public static final String s25056 = "25056";
+  public static final String s25057 = "25057";
+  public static final String s25058 = "25058";
+  public static final String s25059 = "25059";
+  public static final String s25060 = "25060";
+  public static final String s25061 = "25061";
+  public static final String s25062 = "25062";
+  public static final String s25063 = "25063";
+  public static final String s25064 = "25064";
+  public static final String s25065 = "25065";
+  public static final String s25066 = "25066";
+  public static final String s25067 = "25067";
+  public static final String s25068 = "25068";
+  public static final String s25069 = "25069";
+  public static final String s25070 = "25070";
+  public static final String s25071 = "25071";
+  public static final String s25072 = "25072";
+  public static final String s25073 = "25073";
+  public static final String s25074 = "25074";
+  public static final String s25075 = "25075";
+  public static final String s25076 = "25076";
+  public static final String s25077 = "25077";
+  public static final String s25078 = "25078";
+  public static final String s25079 = "25079";
+  public static final String s25080 = "25080";
+  public static final String s25081 = "25081";
+  public static final String s25082 = "25082";
+  public static final String s25083 = "25083";
+  public static final String s25084 = "25084";
+  public static final String s25085 = "25085";
+  public static final String s25086 = "25086";
+  public static final String s25087 = "25087";
+  public static final String s25088 = "25088";
+  public static final String s25089 = "25089";
+  public static final String s25090 = "25090";
+  public static final String s25091 = "25091";
+  public static final String s25092 = "25092";
+  public static final String s25093 = "25093";
+  public static final String s25094 = "25094";
+  public static final String s25095 = "25095";
+  public static final String s25096 = "25096";
+  public static final String s25097 = "25097";
+  public static final String s25098 = "25098";
+  public static final String s25099 = "25099";
+  public static final String s25100 = "25100";
+  public static final String s25101 = "25101";
+  public static final String s25102 = "25102";
+  public static final String s25103 = "25103";
+  public static final String s25104 = "25104";
+  public static final String s25105 = "25105";
+  public static final String s25106 = "25106";
+  public static final String s25107 = "25107";
+  public static final String s25108 = "25108";
+  public static final String s25109 = "25109";
+  public static final String s25110 = "25110";
+  public static final String s25111 = "25111";
+  public static final String s25112 = "25112";
+  public static final String s25113 = "25113";
+  public static final String s25114 = "25114";
+  public static final String s25115 = "25115";
+  public static final String s25116 = "25116";
+  public static final String s25117 = "25117";
+  public static final String s25118 = "25118";
+  public static final String s25119 = "25119";
+  public static final String s25120 = "25120";
+  public static final String s25121 = "25121";
+  public static final String s25122 = "25122";
+  public static final String s25123 = "25123";
+  public static final String s25124 = "25124";
+  public static final String s25125 = "25125";
+  public static final String s25126 = "25126";
+  public static final String s25127 = "25127";
+  public static final String s25128 = "25128";
+  public static final String s25129 = "25129";
+  public static final String s25130 = "25130";
+  public static final String s25131 = "25131";
+  public static final String s25132 = "25132";
+  public static final String s25133 = "25133";
+  public static final String s25134 = "25134";
+  public static final String s25135 = "25135";
+  public static final String s25136 = "25136";
+  public static final String s25137 = "25137";
+  public static final String s25138 = "25138";
+  public static final String s25139 = "25139";
+  public static final String s25140 = "25140";
+  public static final String s25141 = "25141";
+  public static final String s25142 = "25142";
+  public static final String s25143 = "25143";
+  public static final String s25144 = "25144";
+  public static final String s25145 = "25145";
+  public static final String s25146 = "25146";
+  public static final String s25147 = "25147";
+  public static final String s25148 = "25148";
+  public static final String s25149 = "25149";
+  public static final String s25150 = "25150";
+  public static final String s25151 = "25151";
+  public static final String s25152 = "25152";
+  public static final String s25153 = "25153";
+  public static final String s25154 = "25154";
+  public static final String s25155 = "25155";
+  public static final String s25156 = "25156";
+  public static final String s25157 = "25157";
+  public static final String s25158 = "25158";
+  public static final String s25159 = "25159";
+  public static final String s25160 = "25160";
+  public static final String s25161 = "25161";
+  public static final String s25162 = "25162";
+  public static final String s25163 = "25163";
+  public static final String s25164 = "25164";
+  public static final String s25165 = "25165";
+  public static final String s25166 = "25166";
+  public static final String s25167 = "25167";
+  public static final String s25168 = "25168";
+  public static final String s25169 = "25169";
+  public static final String s25170 = "25170";
+  public static final String s25171 = "25171";
+  public static final String s25172 = "25172";
+  public static final String s25173 = "25173";
+  public static final String s25174 = "25174";
+  public static final String s25175 = "25175";
+  public static final String s25176 = "25176";
+  public static final String s25177 = "25177";
+  public static final String s25178 = "25178";
+  public static final String s25179 = "25179";
+  public static final String s25180 = "25180";
+  public static final String s25181 = "25181";
+  public static final String s25182 = "25182";
+  public static final String s25183 = "25183";
+  public static final String s25184 = "25184";
+  public static final String s25185 = "25185";
+  public static final String s25186 = "25186";
+  public static final String s25187 = "25187";
+  public static final String s25188 = "25188";
+  public static final String s25189 = "25189";
+  public static final String s25190 = "25190";
+  public static final String s25191 = "25191";
+  public static final String s25192 = "25192";
+  public static final String s25193 = "25193";
+  public static final String s25194 = "25194";
+  public static final String s25195 = "25195";
+  public static final String s25196 = "25196";
+  public static final String s25197 = "25197";
+  public static final String s25198 = "25198";
+  public static final String s25199 = "25199";
+  public static final String s25200 = "25200";
+  public static final String s25201 = "25201";
+  public static final String s25202 = "25202";
+  public static final String s25203 = "25203";
+  public static final String s25204 = "25204";
+  public static final String s25205 = "25205";
+  public static final String s25206 = "25206";
+  public static final String s25207 = "25207";
+  public static final String s25208 = "25208";
+  public static final String s25209 = "25209";
+  public static final String s25210 = "25210";
+  public static final String s25211 = "25211";
+  public static final String s25212 = "25212";
+  public static final String s25213 = "25213";
+  public static final String s25214 = "25214";
+  public static final String s25215 = "25215";
+  public static final String s25216 = "25216";
+  public static final String s25217 = "25217";
+  public static final String s25218 = "25218";
+  public static final String s25219 = "25219";
+  public static final String s25220 = "25220";
+  public static final String s25221 = "25221";
+  public static final String s25222 = "25222";
+  public static final String s25223 = "25223";
+  public static final String s25224 = "25224";
+  public static final String s25225 = "25225";
+  public static final String s25226 = "25226";
+  public static final String s25227 = "25227";
+  public static final String s25228 = "25228";
+  public static final String s25229 = "25229";
+  public static final String s25230 = "25230";
+  public static final String s25231 = "25231";
+  public static final String s25232 = "25232";
+  public static final String s25233 = "25233";
+  public static final String s25234 = "25234";
+  public static final String s25235 = "25235";
+  public static final String s25236 = "25236";
+  public static final String s25237 = "25237";
+  public static final String s25238 = "25238";
+  public static final String s25239 = "25239";
+  public static final String s25240 = "25240";
+  public static final String s25241 = "25241";
+  public static final String s25242 = "25242";
+  public static final String s25243 = "25243";
+  public static final String s25244 = "25244";
+  public static final String s25245 = "25245";
+  public static final String s25246 = "25246";
+  public static final String s25247 = "25247";
+  public static final String s25248 = "25248";
+  public static final String s25249 = "25249";
+  public static final String s25250 = "25250";
+  public static final String s25251 = "25251";
+  public static final String s25252 = "25252";
+  public static final String s25253 = "25253";
+  public static final String s25254 = "25254";
+  public static final String s25255 = "25255";
+  public static final String s25256 = "25256";
+  public static final String s25257 = "25257";
+  public static final String s25258 = "25258";
+  public static final String s25259 = "25259";
+  public static final String s25260 = "25260";
+  public static final String s25261 = "25261";
+  public static final String s25262 = "25262";
+  public static final String s25263 = "25263";
+  public static final String s25264 = "25264";
+  public static final String s25265 = "25265";
+  public static final String s25266 = "25266";
+  public static final String s25267 = "25267";
+  public static final String s25268 = "25268";
+  public static final String s25269 = "25269";
+  public static final String s25270 = "25270";
+  public static final String s25271 = "25271";
+  public static final String s25272 = "25272";
+  public static final String s25273 = "25273";
+  public static final String s25274 = "25274";
+  public static final String s25275 = "25275";
+  public static final String s25276 = "25276";
+  public static final String s25277 = "25277";
+  public static final String s25278 = "25278";
+  public static final String s25279 = "25279";
+  public static final String s25280 = "25280";
+  public static final String s25281 = "25281";
+  public static final String s25282 = "25282";
+  public static final String s25283 = "25283";
+  public static final String s25284 = "25284";
+  public static final String s25285 = "25285";
+  public static final String s25286 = "25286";
+  public static final String s25287 = "25287";
+  public static final String s25288 = "25288";
+  public static final String s25289 = "25289";
+  public static final String s25290 = "25290";
+  public static final String s25291 = "25291";
+  public static final String s25292 = "25292";
+  public static final String s25293 = "25293";
+  public static final String s25294 = "25294";
+  public static final String s25295 = "25295";
+  public static final String s25296 = "25296";
+  public static final String s25297 = "25297";
+  public static final String s25298 = "25298";
+  public static final String s25299 = "25299";
+  public static final String s25300 = "25300";
+  public static final String s25301 = "25301";
+  public static final String s25302 = "25302";
+  public static final String s25303 = "25303";
+  public static final String s25304 = "25304";
+  public static final String s25305 = "25305";
+  public static final String s25306 = "25306";
+  public static final String s25307 = "25307";
+  public static final String s25308 = "25308";
+  public static final String s25309 = "25309";
+  public static final String s25310 = "25310";
+  public static final String s25311 = "25311";
+  public static final String s25312 = "25312";
+  public static final String s25313 = "25313";
+  public static final String s25314 = "25314";
+  public static final String s25315 = "25315";
+  public static final String s25316 = "25316";
+  public static final String s25317 = "25317";
+  public static final String s25318 = "25318";
+  public static final String s25319 = "25319";
+  public static final String s25320 = "25320";
+  public static final String s25321 = "25321";
+  public static final String s25322 = "25322";
+  public static final String s25323 = "25323";
+  public static final String s25324 = "25324";
+  public static final String s25325 = "25325";
+  public static final String s25326 = "25326";
+  public static final String s25327 = "25327";
+  public static final String s25328 = "25328";
+  public static final String s25329 = "25329";
+  public static final String s25330 = "25330";
+  public static final String s25331 = "25331";
+  public static final String s25332 = "25332";
+  public static final String s25333 = "25333";
+  public static final String s25334 = "25334";
+  public static final String s25335 = "25335";
+  public static final String s25336 = "25336";
+  public static final String s25337 = "25337";
+  public static final String s25338 = "25338";
+  public static final String s25339 = "25339";
+  public static final String s25340 = "25340";
+  public static final String s25341 = "25341";
+  public static final String s25342 = "25342";
+  public static final String s25343 = "25343";
+  public static final String s25344 = "25344";
+  public static final String s25345 = "25345";
+  public static final String s25346 = "25346";
+  public static final String s25347 = "25347";
+  public static final String s25348 = "25348";
+  public static final String s25349 = "25349";
+  public static final String s25350 = "25350";
+  public static final String s25351 = "25351";
+  public static final String s25352 = "25352";
+  public static final String s25353 = "25353";
+  public static final String s25354 = "25354";
+  public static final String s25355 = "25355";
+  public static final String s25356 = "25356";
+  public static final String s25357 = "25357";
+  public static final String s25358 = "25358";
+  public static final String s25359 = "25359";
+  public static final String s25360 = "25360";
+  public static final String s25361 = "25361";
+  public static final String s25362 = "25362";
+  public static final String s25363 = "25363";
+  public static final String s25364 = "25364";
+  public static final String s25365 = "25365";
+  public static final String s25366 = "25366";
+  public static final String s25367 = "25367";
+  public static final String s25368 = "25368";
+  public static final String s25369 = "25369";
+  public static final String s25370 = "25370";
+  public static final String s25371 = "25371";
+  public static final String s25372 = "25372";
+  public static final String s25373 = "25373";
+  public static final String s25374 = "25374";
+  public static final String s25375 = "25375";
+  public static final String s25376 = "25376";
+  public static final String s25377 = "25377";
+  public static final String s25378 = "25378";
+  public static final String s25379 = "25379";
+  public static final String s25380 = "25380";
+  public static final String s25381 = "25381";
+  public static final String s25382 = "25382";
+  public static final String s25383 = "25383";
+  public static final String s25384 = "25384";
+  public static final String s25385 = "25385";
+  public static final String s25386 = "25386";
+  public static final String s25387 = "25387";
+  public static final String s25388 = "25388";
+  public static final String s25389 = "25389";
+  public static final String s25390 = "25390";
+  public static final String s25391 = "25391";
+  public static final String s25392 = "25392";
+  public static final String s25393 = "25393";
+  public static final String s25394 = "25394";
+  public static final String s25395 = "25395";
+  public static final String s25396 = "25396";
+  public static final String s25397 = "25397";
+  public static final String s25398 = "25398";
+  public static final String s25399 = "25399";
+  public static final String s25400 = "25400";
+  public static final String s25401 = "25401";
+  public static final String s25402 = "25402";
+  public static final String s25403 = "25403";
+  public static final String s25404 = "25404";
+  public static final String s25405 = "25405";
+  public static final String s25406 = "25406";
+  public static final String s25407 = "25407";
+  public static final String s25408 = "25408";
+  public static final String s25409 = "25409";
+  public static final String s25410 = "25410";
+  public static final String s25411 = "25411";
+  public static final String s25412 = "25412";
+  public static final String s25413 = "25413";
+  public static final String s25414 = "25414";
+  public static final String s25415 = "25415";
+  public static final String s25416 = "25416";
+  public static final String s25417 = "25417";
+  public static final String s25418 = "25418";
+  public static final String s25419 = "25419";
+  public static final String s25420 = "25420";
+  public static final String s25421 = "25421";
+  public static final String s25422 = "25422";
+  public static final String s25423 = "25423";
+  public static final String s25424 = "25424";
+  public static final String s25425 = "25425";
+  public static final String s25426 = "25426";
+  public static final String s25427 = "25427";
+  public static final String s25428 = "25428";
+  public static final String s25429 = "25429";
+  public static final String s25430 = "25430";
+  public static final String s25431 = "25431";
+  public static final String s25432 = "25432";
+  public static final String s25433 = "25433";
+  public static final String s25434 = "25434";
+  public static final String s25435 = "25435";
+  public static final String s25436 = "25436";
+  public static final String s25437 = "25437";
+  public static final String s25438 = "25438";
+  public static final String s25439 = "25439";
+  public static final String s25440 = "25440";
+  public static final String s25441 = "25441";
+  public static final String s25442 = "25442";
+  public static final String s25443 = "25443";
+  public static final String s25444 = "25444";
+  public static final String s25445 = "25445";
+  public static final String s25446 = "25446";
+  public static final String s25447 = "25447";
+  public static final String s25448 = "25448";
+  public static final String s25449 = "25449";
+  public static final String s25450 = "25450";
+  public static final String s25451 = "25451";
+  public static final String s25452 = "25452";
+  public static final String s25453 = "25453";
+  public static final String s25454 = "25454";
+  public static final String s25455 = "25455";
+  public static final String s25456 = "25456";
+  public static final String s25457 = "25457";
+  public static final String s25458 = "25458";
+  public static final String s25459 = "25459";
+  public static final String s25460 = "25460";
+  public static final String s25461 = "25461";
+  public static final String s25462 = "25462";
+  public static final String s25463 = "25463";
+  public static final String s25464 = "25464";
+  public static final String s25465 = "25465";
+  public static final String s25466 = "25466";
+  public static final String s25467 = "25467";
+  public static final String s25468 = "25468";
+  public static final String s25469 = "25469";
+  public static final String s25470 = "25470";
+  public static final String s25471 = "25471";
+  public static final String s25472 = "25472";
+  public static final String s25473 = "25473";
+  public static final String s25474 = "25474";
+  public static final String s25475 = "25475";
+  public static final String s25476 = "25476";
+  public static final String s25477 = "25477";
+  public static final String s25478 = "25478";
+  public static final String s25479 = "25479";
+  public static final String s25480 = "25480";
+  public static final String s25481 = "25481";
+  public static final String s25482 = "25482";
+  public static final String s25483 = "25483";
+  public static final String s25484 = "25484";
+  public static final String s25485 = "25485";
+  public static final String s25486 = "25486";
+  public static final String s25487 = "25487";
+  public static final String s25488 = "25488";
+  public static final String s25489 = "25489";
+  public static final String s25490 = "25490";
+  public static final String s25491 = "25491";
+  public static final String s25492 = "25492";
+  public static final String s25493 = "25493";
+  public static final String s25494 = "25494";
+  public static final String s25495 = "25495";
+  public static final String s25496 = "25496";
+  public static final String s25497 = "25497";
+  public static final String s25498 = "25498";
+  public static final String s25499 = "25499";
+  public static final String s25500 = "25500";
+  public static final String s25501 = "25501";
+  public static final String s25502 = "25502";
+  public static final String s25503 = "25503";
+  public static final String s25504 = "25504";
+  public static final String s25505 = "25505";
+  public static final String s25506 = "25506";
+  public static final String s25507 = "25507";
+  public static final String s25508 = "25508";
+  public static final String s25509 = "25509";
+  public static final String s25510 = "25510";
+  public static final String s25511 = "25511";
+  public static final String s25512 = "25512";
+  public static final String s25513 = "25513";
+  public static final String s25514 = "25514";
+  public static final String s25515 = "25515";
+  public static final String s25516 = "25516";
+  public static final String s25517 = "25517";
+  public static final String s25518 = "25518";
+  public static final String s25519 = "25519";
+  public static final String s25520 = "25520";
+  public static final String s25521 = "25521";
+  public static final String s25522 = "25522";
+  public static final String s25523 = "25523";
+  public static final String s25524 = "25524";
+  public static final String s25525 = "25525";
+  public static final String s25526 = "25526";
+  public static final String s25527 = "25527";
+  public static final String s25528 = "25528";
+  public static final String s25529 = "25529";
+  public static final String s25530 = "25530";
+  public static final String s25531 = "25531";
+  public static final String s25532 = "25532";
+  public static final String s25533 = "25533";
+  public static final String s25534 = "25534";
+  public static final String s25535 = "25535";
+  public static final String s25536 = "25536";
+  public static final String s25537 = "25537";
+  public static final String s25538 = "25538";
+  public static final String s25539 = "25539";
+  public static final String s25540 = "25540";
+  public static final String s25541 = "25541";
+  public static final String s25542 = "25542";
+  public static final String s25543 = "25543";
+  public static final String s25544 = "25544";
+  public static final String s25545 = "25545";
+  public static final String s25546 = "25546";
+  public static final String s25547 = "25547";
+  public static final String s25548 = "25548";
+  public static final String s25549 = "25549";
+  public static final String s25550 = "25550";
+  public static final String s25551 = "25551";
+  public static final String s25552 = "25552";
+  public static final String s25553 = "25553";
+  public static final String s25554 = "25554";
+  public static final String s25555 = "25555";
+  public static final String s25556 = "25556";
+  public static final String s25557 = "25557";
+  public static final String s25558 = "25558";
+  public static final String s25559 = "25559";
+  public static final String s25560 = "25560";
+  public static final String s25561 = "25561";
+  public static final String s25562 = "25562";
+  public static final String s25563 = "25563";
+  public static final String s25564 = "25564";
+  public static final String s25565 = "25565";
+  public static final String s25566 = "25566";
+  public static final String s25567 = "25567";
+  public static final String s25568 = "25568";
+  public static final String s25569 = "25569";
+  public static final String s25570 = "25570";
+  public static final String s25571 = "25571";
+  public static final String s25572 = "25572";
+  public static final String s25573 = "25573";
+  public static final String s25574 = "25574";
+  public static final String s25575 = "25575";
+  public static final String s25576 = "25576";
+  public static final String s25577 = "25577";
+  public static final String s25578 = "25578";
+  public static final String s25579 = "25579";
+  public static final String s25580 = "25580";
+  public static final String s25581 = "25581";
+  public static final String s25582 = "25582";
+  public static final String s25583 = "25583";
+  public static final String s25584 = "25584";
+  public static final String s25585 = "25585";
+  public static final String s25586 = "25586";
+  public static final String s25587 = "25587";
+  public static final String s25588 = "25588";
+  public static final String s25589 = "25589";
+  public static final String s25590 = "25590";
+  public static final String s25591 = "25591";
+  public static final String s25592 = "25592";
+  public static final String s25593 = "25593";
+  public static final String s25594 = "25594";
+  public static final String s25595 = "25595";
+  public static final String s25596 = "25596";
+  public static final String s25597 = "25597";
+  public static final String s25598 = "25598";
+  public static final String s25599 = "25599";
+  public static final String s25600 = "25600";
+  public static final String s25601 = "25601";
+  public static final String s25602 = "25602";
+  public static final String s25603 = "25603";
+  public static final String s25604 = "25604";
+  public static final String s25605 = "25605";
+  public static final String s25606 = "25606";
+  public static final String s25607 = "25607";
+  public static final String s25608 = "25608";
+  public static final String s25609 = "25609";
+  public static final String s25610 = "25610";
+  public static final String s25611 = "25611";
+  public static final String s25612 = "25612";
+  public static final String s25613 = "25613";
+  public static final String s25614 = "25614";
+  public static final String s25615 = "25615";
+  public static final String s25616 = "25616";
+  public static final String s25617 = "25617";
+  public static final String s25618 = "25618";
+  public static final String s25619 = "25619";
+  public static final String s25620 = "25620";
+  public static final String s25621 = "25621";
+  public static final String s25622 = "25622";
+  public static final String s25623 = "25623";
+  public static final String s25624 = "25624";
+  public static final String s25625 = "25625";
+  public static final String s25626 = "25626";
+  public static final String s25627 = "25627";
+  public static final String s25628 = "25628";
+  public static final String s25629 = "25629";
+  public static final String s25630 = "25630";
+  public static final String s25631 = "25631";
+  public static final String s25632 = "25632";
+  public static final String s25633 = "25633";
+  public static final String s25634 = "25634";
+  public static final String s25635 = "25635";
+  public static final String s25636 = "25636";
+  public static final String s25637 = "25637";
+  public static final String s25638 = "25638";
+  public static final String s25639 = "25639";
+  public static final String s25640 = "25640";
+  public static final String s25641 = "25641";
+  public static final String s25642 = "25642";
+  public static final String s25643 = "25643";
+  public static final String s25644 = "25644";
+  public static final String s25645 = "25645";
+  public static final String s25646 = "25646";
+  public static final String s25647 = "25647";
+  public static final String s25648 = "25648";
+  public static final String s25649 = "25649";
+  public static final String s25650 = "25650";
+  public static final String s25651 = "25651";
+  public static final String s25652 = "25652";
+  public static final String s25653 = "25653";
+  public static final String s25654 = "25654";
+  public static final String s25655 = "25655";
+  public static final String s25656 = "25656";
+  public static final String s25657 = "25657";
+  public static final String s25658 = "25658";
+  public static final String s25659 = "25659";
+  public static final String s25660 = "25660";
+  public static final String s25661 = "25661";
+  public static final String s25662 = "25662";
+  public static final String s25663 = "25663";
+  public static final String s25664 = "25664";
+  public static final String s25665 = "25665";
+  public static final String s25666 = "25666";
+  public static final String s25667 = "25667";
+  public static final String s25668 = "25668";
+  public static final String s25669 = "25669";
+  public static final String s25670 = "25670";
+  public static final String s25671 = "25671";
+  public static final String s25672 = "25672";
+  public static final String s25673 = "25673";
+  public static final String s25674 = "25674";
+  public static final String s25675 = "25675";
+  public static final String s25676 = "25676";
+  public static final String s25677 = "25677";
+  public static final String s25678 = "25678";
+  public static final String s25679 = "25679";
+  public static final String s25680 = "25680";
+  public static final String s25681 = "25681";
+  public static final String s25682 = "25682";
+  public static final String s25683 = "25683";
+  public static final String s25684 = "25684";
+  public static final String s25685 = "25685";
+  public static final String s25686 = "25686";
+  public static final String s25687 = "25687";
+  public static final String s25688 = "25688";
+  public static final String s25689 = "25689";
+  public static final String s25690 = "25690";
+  public static final String s25691 = "25691";
+  public static final String s25692 = "25692";
+  public static final String s25693 = "25693";
+  public static final String s25694 = "25694";
+  public static final String s25695 = "25695";
+  public static final String s25696 = "25696";
+  public static final String s25697 = "25697";
+  public static final String s25698 = "25698";
+  public static final String s25699 = "25699";
+  public static final String s25700 = "25700";
+  public static final String s25701 = "25701";
+  public static final String s25702 = "25702";
+  public static final String s25703 = "25703";
+  public static final String s25704 = "25704";
+  public static final String s25705 = "25705";
+  public static final String s25706 = "25706";
+  public static final String s25707 = "25707";
+  public static final String s25708 = "25708";
+  public static final String s25709 = "25709";
+  public static final String s25710 = "25710";
+  public static final String s25711 = "25711";
+  public static final String s25712 = "25712";
+  public static final String s25713 = "25713";
+  public static final String s25714 = "25714";
+  public static final String s25715 = "25715";
+  public static final String s25716 = "25716";
+  public static final String s25717 = "25717";
+  public static final String s25718 = "25718";
+  public static final String s25719 = "25719";
+  public static final String s25720 = "25720";
+  public static final String s25721 = "25721";
+  public static final String s25722 = "25722";
+  public static final String s25723 = "25723";
+  public static final String s25724 = "25724";
+  public static final String s25725 = "25725";
+  public static final String s25726 = "25726";
+  public static final String s25727 = "25727";
+  public static final String s25728 = "25728";
+  public static final String s25729 = "25729";
+  public static final String s25730 = "25730";
+  public static final String s25731 = "25731";
+  public static final String s25732 = "25732";
+  public static final String s25733 = "25733";
+  public static final String s25734 = "25734";
+  public static final String s25735 = "25735";
+  public static final String s25736 = "25736";
+  public static final String s25737 = "25737";
+  public static final String s25738 = "25738";
+  public static final String s25739 = "25739";
+  public static final String s25740 = "25740";
+  public static final String s25741 = "25741";
+  public static final String s25742 = "25742";
+  public static final String s25743 = "25743";
+  public static final String s25744 = "25744";
+  public static final String s25745 = "25745";
+  public static final String s25746 = "25746";
+  public static final String s25747 = "25747";
+  public static final String s25748 = "25748";
+  public static final String s25749 = "25749";
+  public static final String s25750 = "25750";
+  public static final String s25751 = "25751";
+  public static final String s25752 = "25752";
+  public static final String s25753 = "25753";
+  public static final String s25754 = "25754";
+  public static final String s25755 = "25755";
+  public static final String s25756 = "25756";
+  public static final String s25757 = "25757";
+  public static final String s25758 = "25758";
+  public static final String s25759 = "25759";
+  public static final String s25760 = "25760";
+  public static final String s25761 = "25761";
+  public static final String s25762 = "25762";
+  public static final String s25763 = "25763";
+  public static final String s25764 = "25764";
+  public static final String s25765 = "25765";
+  public static final String s25766 = "25766";
+  public static final String s25767 = "25767";
+  public static final String s25768 = "25768";
+  public static final String s25769 = "25769";
+  public static final String s25770 = "25770";
+  public static final String s25771 = "25771";
+  public static final String s25772 = "25772";
+  public static final String s25773 = "25773";
+  public static final String s25774 = "25774";
+  public static final String s25775 = "25775";
+  public static final String s25776 = "25776";
+  public static final String s25777 = "25777";
+  public static final String s25778 = "25778";
+  public static final String s25779 = "25779";
+  public static final String s25780 = "25780";
+  public static final String s25781 = "25781";
+  public static final String s25782 = "25782";
+  public static final String s25783 = "25783";
+  public static final String s25784 = "25784";
+  public static final String s25785 = "25785";
+  public static final String s25786 = "25786";
+  public static final String s25787 = "25787";
+  public static final String s25788 = "25788";
+  public static final String s25789 = "25789";
+  public static final String s25790 = "25790";
+  public static final String s25791 = "25791";
+  public static final String s25792 = "25792";
+  public static final String s25793 = "25793";
+  public static final String s25794 = "25794";
+  public static final String s25795 = "25795";
+  public static final String s25796 = "25796";
+  public static final String s25797 = "25797";
+  public static final String s25798 = "25798";
+  public static final String s25799 = "25799";
+  public static final String s25800 = "25800";
+  public static final String s25801 = "25801";
+  public static final String s25802 = "25802";
+  public static final String s25803 = "25803";
+  public static final String s25804 = "25804";
+  public static final String s25805 = "25805";
+  public static final String s25806 = "25806";
+  public static final String s25807 = "25807";
+  public static final String s25808 = "25808";
+  public static final String s25809 = "25809";
+  public static final String s25810 = "25810";
+  public static final String s25811 = "25811";
+  public static final String s25812 = "25812";
+  public static final String s25813 = "25813";
+  public static final String s25814 = "25814";
+  public static final String s25815 = "25815";
+  public static final String s25816 = "25816";
+  public static final String s25817 = "25817";
+  public static final String s25818 = "25818";
+  public static final String s25819 = "25819";
+  public static final String s25820 = "25820";
+  public static final String s25821 = "25821";
+  public static final String s25822 = "25822";
+  public static final String s25823 = "25823";
+  public static final String s25824 = "25824";
+  public static final String s25825 = "25825";
+  public static final String s25826 = "25826";
+  public static final String s25827 = "25827";
+  public static final String s25828 = "25828";
+  public static final String s25829 = "25829";
+  public static final String s25830 = "25830";
+  public static final String s25831 = "25831";
+  public static final String s25832 = "25832";
+  public static final String s25833 = "25833";
+  public static final String s25834 = "25834";
+  public static final String s25835 = "25835";
+  public static final String s25836 = "25836";
+  public static final String s25837 = "25837";
+  public static final String s25838 = "25838";
+  public static final String s25839 = "25839";
+  public static final String s25840 = "25840";
+  public static final String s25841 = "25841";
+  public static final String s25842 = "25842";
+  public static final String s25843 = "25843";
+  public static final String s25844 = "25844";
+  public static final String s25845 = "25845";
+  public static final String s25846 = "25846";
+  public static final String s25847 = "25847";
+  public static final String s25848 = "25848";
+  public static final String s25849 = "25849";
+  public static final String s25850 = "25850";
+  public static final String s25851 = "25851";
+  public static final String s25852 = "25852";
+  public static final String s25853 = "25853";
+  public static final String s25854 = "25854";
+  public static final String s25855 = "25855";
+  public static final String s25856 = "25856";
+  public static final String s25857 = "25857";
+  public static final String s25858 = "25858";
+  public static final String s25859 = "25859";
+  public static final String s25860 = "25860";
+  public static final String s25861 = "25861";
+  public static final String s25862 = "25862";
+  public static final String s25863 = "25863";
+  public static final String s25864 = "25864";
+  public static final String s25865 = "25865";
+  public static final String s25866 = "25866";
+  public static final String s25867 = "25867";
+  public static final String s25868 = "25868";
+  public static final String s25869 = "25869";
+  public static final String s25870 = "25870";
+  public static final String s25871 = "25871";
+  public static final String s25872 = "25872";
+  public static final String s25873 = "25873";
+  public static final String s25874 = "25874";
+  public static final String s25875 = "25875";
+  public static final String s25876 = "25876";
+  public static final String s25877 = "25877";
+  public static final String s25878 = "25878";
+  public static final String s25879 = "25879";
+  public static final String s25880 = "25880";
+  public static final String s25881 = "25881";
+  public static final String s25882 = "25882";
+  public static final String s25883 = "25883";
+  public static final String s25884 = "25884";
+  public static final String s25885 = "25885";
+  public static final String s25886 = "25886";
+  public static final String s25887 = "25887";
+  public static final String s25888 = "25888";
+  public static final String s25889 = "25889";
+  public static final String s25890 = "25890";
+  public static final String s25891 = "25891";
+  public static final String s25892 = "25892";
+  public static final String s25893 = "25893";
+  public static final String s25894 = "25894";
+  public static final String s25895 = "25895";
+  public static final String s25896 = "25896";
+  public static final String s25897 = "25897";
+  public static final String s25898 = "25898";
+  public static final String s25899 = "25899";
+  public static final String s25900 = "25900";
+  public static final String s25901 = "25901";
+  public static final String s25902 = "25902";
+  public static final String s25903 = "25903";
+  public static final String s25904 = "25904";
+  public static final String s25905 = "25905";
+  public static final String s25906 = "25906";
+  public static final String s25907 = "25907";
+  public static final String s25908 = "25908";
+  public static final String s25909 = "25909";
+  public static final String s25910 = "25910";
+  public static final String s25911 = "25911";
+  public static final String s25912 = "25912";
+  public static final String s25913 = "25913";
+  public static final String s25914 = "25914";
+  public static final String s25915 = "25915";
+  public static final String s25916 = "25916";
+  public static final String s25917 = "25917";
+  public static final String s25918 = "25918";
+  public static final String s25919 = "25919";
+  public static final String s25920 = "25920";
+  public static final String s25921 = "25921";
+  public static final String s25922 = "25922";
+  public static final String s25923 = "25923";
+  public static final String s25924 = "25924";
+  public static final String s25925 = "25925";
+  public static final String s25926 = "25926";
+  public static final String s25927 = "25927";
+  public static final String s25928 = "25928";
+  public static final String s25929 = "25929";
+  public static final String s25930 = "25930";
+  public static final String s25931 = "25931";
+  public static final String s25932 = "25932";
+  public static final String s25933 = "25933";
+  public static final String s25934 = "25934";
+  public static final String s25935 = "25935";
+  public static final String s25936 = "25936";
+  public static final String s25937 = "25937";
+  public static final String s25938 = "25938";
+  public static final String s25939 = "25939";
+  public static final String s25940 = "25940";
+  public static final String s25941 = "25941";
+  public static final String s25942 = "25942";
+  public static final String s25943 = "25943";
+  public static final String s25944 = "25944";
+  public static final String s25945 = "25945";
+  public static final String s25946 = "25946";
+  public static final String s25947 = "25947";
+  public static final String s25948 = "25948";
+  public static final String s25949 = "25949";
+  public static final String s25950 = "25950";
+  public static final String s25951 = "25951";
+  public static final String s25952 = "25952";
+  public static final String s25953 = "25953";
+  public static final String s25954 = "25954";
+  public static final String s25955 = "25955";
+  public static final String s25956 = "25956";
+  public static final String s25957 = "25957";
+  public static final String s25958 = "25958";
+  public static final String s25959 = "25959";
+  public static final String s25960 = "25960";
+  public static final String s25961 = "25961";
+  public static final String s25962 = "25962";
+  public static final String s25963 = "25963";
+  public static final String s25964 = "25964";
+  public static final String s25965 = "25965";
+  public static final String s25966 = "25966";
+  public static final String s25967 = "25967";
+  public static final String s25968 = "25968";
+  public static final String s25969 = "25969";
+  public static final String s25970 = "25970";
+  public static final String s25971 = "25971";
+  public static final String s25972 = "25972";
+  public static final String s25973 = "25973";
+  public static final String s25974 = "25974";
+  public static final String s25975 = "25975";
+  public static final String s25976 = "25976";
+  public static final String s25977 = "25977";
+  public static final String s25978 = "25978";
+  public static final String s25979 = "25979";
+  public static final String s25980 = "25980";
+  public static final String s25981 = "25981";
+  public static final String s25982 = "25982";
+  public static final String s25983 = "25983";
+  public static final String s25984 = "25984";
+  public static final String s25985 = "25985";
+  public static final String s25986 = "25986";
+  public static final String s25987 = "25987";
+  public static final String s25988 = "25988";
+  public static final String s25989 = "25989";
+  public static final String s25990 = "25990";
+  public static final String s25991 = "25991";
+  public static final String s25992 = "25992";
+  public static final String s25993 = "25993";
+  public static final String s25994 = "25994";
+  public static final String s25995 = "25995";
+  public static final String s25996 = "25996";
+  public static final String s25997 = "25997";
+  public static final String s25998 = "25998";
+  public static final String s25999 = "25999";
+  public static final String s26000 = "26000";
+  public static final String s26001 = "26001";
+  public static final String s26002 = "26002";
+  public static final String s26003 = "26003";
+  public static final String s26004 = "26004";
+  public static final String s26005 = "26005";
+  public static final String s26006 = "26006";
+  public static final String s26007 = "26007";
+  public static final String s26008 = "26008";
+  public static final String s26009 = "26009";
+  public static final String s26010 = "26010";
+  public static final String s26011 = "26011";
+  public static final String s26012 = "26012";
+  public static final String s26013 = "26013";
+  public static final String s26014 = "26014";
+  public static final String s26015 = "26015";
+  public static final String s26016 = "26016";
+  public static final String s26017 = "26017";
+  public static final String s26018 = "26018";
+  public static final String s26019 = "26019";
+  public static final String s26020 = "26020";
+  public static final String s26021 = "26021";
+  public static final String s26022 = "26022";
+  public static final String s26023 = "26023";
+  public static final String s26024 = "26024";
+  public static final String s26025 = "26025";
+  public static final String s26026 = "26026";
+  public static final String s26027 = "26027";
+  public static final String s26028 = "26028";
+  public static final String s26029 = "26029";
+  public static final String s26030 = "26030";
+  public static final String s26031 = "26031";
+  public static final String s26032 = "26032";
+  public static final String s26033 = "26033";
+  public static final String s26034 = "26034";
+  public static final String s26035 = "26035";
+  public static final String s26036 = "26036";
+  public static final String s26037 = "26037";
+  public static final String s26038 = "26038";
+  public static final String s26039 = "26039";
+  public static final String s26040 = "26040";
+  public static final String s26041 = "26041";
+  public static final String s26042 = "26042";
+  public static final String s26043 = "26043";
+  public static final String s26044 = "26044";
+  public static final String s26045 = "26045";
+  public static final String s26046 = "26046";
+  public static final String s26047 = "26047";
+  public static final String s26048 = "26048";
+  public static final String s26049 = "26049";
+  public static final String s26050 = "26050";
+  public static final String s26051 = "26051";
+  public static final String s26052 = "26052";
+  public static final String s26053 = "26053";
+  public static final String s26054 = "26054";
+  public static final String s26055 = "26055";
+  public static final String s26056 = "26056";
+  public static final String s26057 = "26057";
+  public static final String s26058 = "26058";
+  public static final String s26059 = "26059";
+  public static final String s26060 = "26060";
+  public static final String s26061 = "26061";
+  public static final String s26062 = "26062";
+  public static final String s26063 = "26063";
+  public static final String s26064 = "26064";
+  public static final String s26065 = "26065";
+  public static final String s26066 = "26066";
+  public static final String s26067 = "26067";
+  public static final String s26068 = "26068";
+  public static final String s26069 = "26069";
+  public static final String s26070 = "26070";
+  public static final String s26071 = "26071";
+  public static final String s26072 = "26072";
+  public static final String s26073 = "26073";
+  public static final String s26074 = "26074";
+  public static final String s26075 = "26075";
+  public static final String s26076 = "26076";
+  public static final String s26077 = "26077";
+  public static final String s26078 = "26078";
+  public static final String s26079 = "26079";
+  public static final String s26080 = "26080";
+  public static final String s26081 = "26081";
+  public static final String s26082 = "26082";
+  public static final String s26083 = "26083";
+  public static final String s26084 = "26084";
+  public static final String s26085 = "26085";
+  public static final String s26086 = "26086";
+  public static final String s26087 = "26087";
+  public static final String s26088 = "26088";
+  public static final String s26089 = "26089";
+  public static final String s26090 = "26090";
+  public static final String s26091 = "26091";
+  public static final String s26092 = "26092";
+  public static final String s26093 = "26093";
+  public static final String s26094 = "26094";
+  public static final String s26095 = "26095";
+  public static final String s26096 = "26096";
+  public static final String s26097 = "26097";
+  public static final String s26098 = "26098";
+  public static final String s26099 = "26099";
+  public static final String s26100 = "26100";
+  public static final String s26101 = "26101";
+  public static final String s26102 = "26102";
+  public static final String s26103 = "26103";
+  public static final String s26104 = "26104";
+  public static final String s26105 = "26105";
+  public static final String s26106 = "26106";
+  public static final String s26107 = "26107";
+  public static final String s26108 = "26108";
+  public static final String s26109 = "26109";
+  public static final String s26110 = "26110";
+  public static final String s26111 = "26111";
+  public static final String s26112 = "26112";
+  public static final String s26113 = "26113";
+  public static final String s26114 = "26114";
+  public static final String s26115 = "26115";
+  public static final String s26116 = "26116";
+  public static final String s26117 = "26117";
+  public static final String s26118 = "26118";
+  public static final String s26119 = "26119";
+  public static final String s26120 = "26120";
+  public static final String s26121 = "26121";
+  public static final String s26122 = "26122";
+  public static final String s26123 = "26123";
+  public static final String s26124 = "26124";
+  public static final String s26125 = "26125";
+  public static final String s26126 = "26126";
+  public static final String s26127 = "26127";
+  public static final String s26128 = "26128";
+  public static final String s26129 = "26129";
+  public static final String s26130 = "26130";
+  public static final String s26131 = "26131";
+  public static final String s26132 = "26132";
+  public static final String s26133 = "26133";
+  public static final String s26134 = "26134";
+  public static final String s26135 = "26135";
+  public static final String s26136 = "26136";
+  public static final String s26137 = "26137";
+  public static final String s26138 = "26138";
+  public static final String s26139 = "26139";
+  public static final String s26140 = "26140";
+  public static final String s26141 = "26141";
+  public static final String s26142 = "26142";
+  public static final String s26143 = "26143";
+  public static final String s26144 = "26144";
+  public static final String s26145 = "26145";
+  public static final String s26146 = "26146";
+  public static final String s26147 = "26147";
+  public static final String s26148 = "26148";
+  public static final String s26149 = "26149";
+  public static final String s26150 = "26150";
+  public static final String s26151 = "26151";
+  public static final String s26152 = "26152";
+  public static final String s26153 = "26153";
+  public static final String s26154 = "26154";
+  public static final String s26155 = "26155";
+  public static final String s26156 = "26156";
+  public static final String s26157 = "26157";
+  public static final String s26158 = "26158";
+  public static final String s26159 = "26159";
+  public static final String s26160 = "26160";
+  public static final String s26161 = "26161";
+  public static final String s26162 = "26162";
+  public static final String s26163 = "26163";
+  public static final String s26164 = "26164";
+  public static final String s26165 = "26165";
+  public static final String s26166 = "26166";
+  public static final String s26167 = "26167";
+  public static final String s26168 = "26168";
+  public static final String s26169 = "26169";
+  public static final String s26170 = "26170";
+  public static final String s26171 = "26171";
+  public static final String s26172 = "26172";
+  public static final String s26173 = "26173";
+  public static final String s26174 = "26174";
+  public static final String s26175 = "26175";
+  public static final String s26176 = "26176";
+  public static final String s26177 = "26177";
+  public static final String s26178 = "26178";
+  public static final String s26179 = "26179";
+  public static final String s26180 = "26180";
+  public static final String s26181 = "26181";
+  public static final String s26182 = "26182";
+  public static final String s26183 = "26183";
+  public static final String s26184 = "26184";
+  public static final String s26185 = "26185";
+  public static final String s26186 = "26186";
+  public static final String s26187 = "26187";
+  public static final String s26188 = "26188";
+  public static final String s26189 = "26189";
+  public static final String s26190 = "26190";
+  public static final String s26191 = "26191";
+  public static final String s26192 = "26192";
+  public static final String s26193 = "26193";
+  public static final String s26194 = "26194";
+  public static final String s26195 = "26195";
+  public static final String s26196 = "26196";
+  public static final String s26197 = "26197";
+  public static final String s26198 = "26198";
+  public static final String s26199 = "26199";
+  public static final String s26200 = "26200";
+  public static final String s26201 = "26201";
+  public static final String s26202 = "26202";
+  public static final String s26203 = "26203";
+  public static final String s26204 = "26204";
+  public static final String s26205 = "26205";
+  public static final String s26206 = "26206";
+  public static final String s26207 = "26207";
+  public static final String s26208 = "26208";
+  public static final String s26209 = "26209";
+  public static final String s26210 = "26210";
+  public static final String s26211 = "26211";
+  public static final String s26212 = "26212";
+  public static final String s26213 = "26213";
+  public static final String s26214 = "26214";
+  public static final String s26215 = "26215";
+  public static final String s26216 = "26216";
+  public static final String s26217 = "26217";
+  public static final String s26218 = "26218";
+  public static final String s26219 = "26219";
+  public static final String s26220 = "26220";
+  public static final String s26221 = "26221";
+  public static final String s26222 = "26222";
+  public static final String s26223 = "26223";
+  public static final String s26224 = "26224";
+  public static final String s26225 = "26225";
+  public static final String s26226 = "26226";
+  public static final String s26227 = "26227";
+  public static final String s26228 = "26228";
+  public static final String s26229 = "26229";
+  public static final String s26230 = "26230";
+  public static final String s26231 = "26231";
+  public static final String s26232 = "26232";
+  public static final String s26233 = "26233";
+  public static final String s26234 = "26234";
+  public static final String s26235 = "26235";
+  public static final String s26236 = "26236";
+  public static final String s26237 = "26237";
+  public static final String s26238 = "26238";
+  public static final String s26239 = "26239";
+  public static final String s26240 = "26240";
+  public static final String s26241 = "26241";
+  public static final String s26242 = "26242";
+  public static final String s26243 = "26243";
+  public static final String s26244 = "26244";
+  public static final String s26245 = "26245";
+  public static final String s26246 = "26246";
+  public static final String s26247 = "26247";
+  public static final String s26248 = "26248";
+  public static final String s26249 = "26249";
+  public static final String s26250 = "26250";
+  public static final String s26251 = "26251";
+  public static final String s26252 = "26252";
+  public static final String s26253 = "26253";
+  public static final String s26254 = "26254";
+  public static final String s26255 = "26255";
+  public static final String s26256 = "26256";
+  public static final String s26257 = "26257";
+  public static final String s26258 = "26258";
+  public static final String s26259 = "26259";
+  public static final String s26260 = "26260";
+  public static final String s26261 = "26261";
+  public static final String s26262 = "26262";
+  public static final String s26263 = "26263";
+  public static final String s26264 = "26264";
+  public static final String s26265 = "26265";
+  public static final String s26266 = "26266";
+  public static final String s26267 = "26267";
+  public static final String s26268 = "26268";
+  public static final String s26269 = "26269";
+  public static final String s26270 = "26270";
+  public static final String s26271 = "26271";
+  public static final String s26272 = "26272";
+  public static final String s26273 = "26273";
+  public static final String s26274 = "26274";
+  public static final String s26275 = "26275";
+  public static final String s26276 = "26276";
+  public static final String s26277 = "26277";
+  public static final String s26278 = "26278";
+  public static final String s26279 = "26279";
+  public static final String s26280 = "26280";
+  public static final String s26281 = "26281";
+  public static final String s26282 = "26282";
+  public static final String s26283 = "26283";
+  public static final String s26284 = "26284";
+  public static final String s26285 = "26285";
+  public static final String s26286 = "26286";
+  public static final String s26287 = "26287";
+  public static final String s26288 = "26288";
+  public static final String s26289 = "26289";
+  public static final String s26290 = "26290";
+  public static final String s26291 = "26291";
+  public static final String s26292 = "26292";
+  public static final String s26293 = "26293";
+  public static final String s26294 = "26294";
+  public static final String s26295 = "26295";
+  public static final String s26296 = "26296";
+  public static final String s26297 = "26297";
+  public static final String s26298 = "26298";
+  public static final String s26299 = "26299";
+  public static final String s26300 = "26300";
+  public static final String s26301 = "26301";
+  public static final String s26302 = "26302";
+  public static final String s26303 = "26303";
+  public static final String s26304 = "26304";
+  public static final String s26305 = "26305";
+  public static final String s26306 = "26306";
+  public static final String s26307 = "26307";
+  public static final String s26308 = "26308";
+  public static final String s26309 = "26309";
+  public static final String s26310 = "26310";
+  public static final String s26311 = "26311";
+  public static final String s26312 = "26312";
+  public static final String s26313 = "26313";
+  public static final String s26314 = "26314";
+  public static final String s26315 = "26315";
+  public static final String s26316 = "26316";
+  public static final String s26317 = "26317";
+  public static final String s26318 = "26318";
+  public static final String s26319 = "26319";
+  public static final String s26320 = "26320";
+  public static final String s26321 = "26321";
+  public static final String s26322 = "26322";
+  public static final String s26323 = "26323";
+  public static final String s26324 = "26324";
+  public static final String s26325 = "26325";
+  public static final String s26326 = "26326";
+  public static final String s26327 = "26327";
+  public static final String s26328 = "26328";
+  public static final String s26329 = "26329";
+  public static final String s26330 = "26330";
+  public static final String s26331 = "26331";
+  public static final String s26332 = "26332";
+  public static final String s26333 = "26333";
+  public static final String s26334 = "26334";
+  public static final String s26335 = "26335";
+  public static final String s26336 = "26336";
+  public static final String s26337 = "26337";
+  public static final String s26338 = "26338";
+  public static final String s26339 = "26339";
+  public static final String s26340 = "26340";
+  public static final String s26341 = "26341";
+  public static final String s26342 = "26342";
+  public static final String s26343 = "26343";
+  public static final String s26344 = "26344";
+  public static final String s26345 = "26345";
+  public static final String s26346 = "26346";
+  public static final String s26347 = "26347";
+  public static final String s26348 = "26348";
+  public static final String s26349 = "26349";
+  public static final String s26350 = "26350";
+  public static final String s26351 = "26351";
+  public static final String s26352 = "26352";
+  public static final String s26353 = "26353";
+  public static final String s26354 = "26354";
+  public static final String s26355 = "26355";
+  public static final String s26356 = "26356";
+  public static final String s26357 = "26357";
+  public static final String s26358 = "26358";
+  public static final String s26359 = "26359";
+  public static final String s26360 = "26360";
+  public static final String s26361 = "26361";
+  public static final String s26362 = "26362";
+  public static final String s26363 = "26363";
+  public static final String s26364 = "26364";
+  public static final String s26365 = "26365";
+  public static final String s26366 = "26366";
+  public static final String s26367 = "26367";
+  public static final String s26368 = "26368";
+  public static final String s26369 = "26369";
+  public static final String s26370 = "26370";
+  public static final String s26371 = "26371";
+  public static final String s26372 = "26372";
+  public static final String s26373 = "26373";
+  public static final String s26374 = "26374";
+  public static final String s26375 = "26375";
+  public static final String s26376 = "26376";
+  public static final String s26377 = "26377";
+  public static final String s26378 = "26378";
+  public static final String s26379 = "26379";
+  public static final String s26380 = "26380";
+  public static final String s26381 = "26381";
+  public static final String s26382 = "26382";
+  public static final String s26383 = "26383";
+  public static final String s26384 = "26384";
+  public static final String s26385 = "26385";
+  public static final String s26386 = "26386";
+  public static final String s26387 = "26387";
+  public static final String s26388 = "26388";
+  public static final String s26389 = "26389";
+  public static final String s26390 = "26390";
+  public static final String s26391 = "26391";
+  public static final String s26392 = "26392";
+  public static final String s26393 = "26393";
+  public static final String s26394 = "26394";
+  public static final String s26395 = "26395";
+  public static final String s26396 = "26396";
+  public static final String s26397 = "26397";
+  public static final String s26398 = "26398";
+  public static final String s26399 = "26399";
+  public static final String s26400 = "26400";
+  public static final String s26401 = "26401";
+  public static final String s26402 = "26402";
+  public static final String s26403 = "26403";
+  public static final String s26404 = "26404";
+  public static final String s26405 = "26405";
+  public static final String s26406 = "26406";
+  public static final String s26407 = "26407";
+  public static final String s26408 = "26408";
+  public static final String s26409 = "26409";
+  public static final String s26410 = "26410";
+  public static final String s26411 = "26411";
+  public static final String s26412 = "26412";
+  public static final String s26413 = "26413";
+  public static final String s26414 = "26414";
+  public static final String s26415 = "26415";
+  public static final String s26416 = "26416";
+  public static final String s26417 = "26417";
+  public static final String s26418 = "26418";
+  public static final String s26419 = "26419";
+  public static final String s26420 = "26420";
+  public static final String s26421 = "26421";
+  public static final String s26422 = "26422";
+  public static final String s26423 = "26423";
+  public static final String s26424 = "26424";
+  public static final String s26425 = "26425";
+  public static final String s26426 = "26426";
+  public static final String s26427 = "26427";
+  public static final String s26428 = "26428";
+  public static final String s26429 = "26429";
+  public static final String s26430 = "26430";
+  public static final String s26431 = "26431";
+  public static final String s26432 = "26432";
+  public static final String s26433 = "26433";
+  public static final String s26434 = "26434";
+  public static final String s26435 = "26435";
+  public static final String s26436 = "26436";
+  public static final String s26437 = "26437";
+  public static final String s26438 = "26438";
+  public static final String s26439 = "26439";
+  public static final String s26440 = "26440";
+  public static final String s26441 = "26441";
+  public static final String s26442 = "26442";
+  public static final String s26443 = "26443";
+  public static final String s26444 = "26444";
+  public static final String s26445 = "26445";
+  public static final String s26446 = "26446";
+  public static final String s26447 = "26447";
+  public static final String s26448 = "26448";
+  public static final String s26449 = "26449";
+  public static final String s26450 = "26450";
+  public static final String s26451 = "26451";
+  public static final String s26452 = "26452";
+  public static final String s26453 = "26453";
+  public static final String s26454 = "26454";
+  public static final String s26455 = "26455";
+  public static final String s26456 = "26456";
+  public static final String s26457 = "26457";
+  public static final String s26458 = "26458";
+  public static final String s26459 = "26459";
+  public static final String s26460 = "26460";
+  public static final String s26461 = "26461";
+  public static final String s26462 = "26462";
+  public static final String s26463 = "26463";
+  public static final String s26464 = "26464";
+  public static final String s26465 = "26465";
+  public static final String s26466 = "26466";
+  public static final String s26467 = "26467";
+  public static final String s26468 = "26468";
+  public static final String s26469 = "26469";
+  public static final String s26470 = "26470";
+  public static final String s26471 = "26471";
+  public static final String s26472 = "26472";
+  public static final String s26473 = "26473";
+  public static final String s26474 = "26474";
+  public static final String s26475 = "26475";
+  public static final String s26476 = "26476";
+  public static final String s26477 = "26477";
+  public static final String s26478 = "26478";
+  public static final String s26479 = "26479";
+  public static final String s26480 = "26480";
+  public static final String s26481 = "26481";
+  public static final String s26482 = "26482";
+  public static final String s26483 = "26483";
+  public static final String s26484 = "26484";
+  public static final String s26485 = "26485";
+  public static final String s26486 = "26486";
+  public static final String s26487 = "26487";
+  public static final String s26488 = "26488";
+  public static final String s26489 = "26489";
+  public static final String s26490 = "26490";
+  public static final String s26491 = "26491";
+  public static final String s26492 = "26492";
+  public static final String s26493 = "26493";
+  public static final String s26494 = "26494";
+  public static final String s26495 = "26495";
+  public static final String s26496 = "26496";
+  public static final String s26497 = "26497";
+  public static final String s26498 = "26498";
+  public static final String s26499 = "26499";
+  public static final String s26500 = "26500";
+  public static final String s26501 = "26501";
+  public static final String s26502 = "26502";
+  public static final String s26503 = "26503";
+  public static final String s26504 = "26504";
+  public static final String s26505 = "26505";
+  public static final String s26506 = "26506";
+  public static final String s26507 = "26507";
+  public static final String s26508 = "26508";
+  public static final String s26509 = "26509";
+  public static final String s26510 = "26510";
+  public static final String s26511 = "26511";
+  public static final String s26512 = "26512";
+  public static final String s26513 = "26513";
+  public static final String s26514 = "26514";
+  public static final String s26515 = "26515";
+  public static final String s26516 = "26516";
+  public static final String s26517 = "26517";
+  public static final String s26518 = "26518";
+  public static final String s26519 = "26519";
+  public static final String s26520 = "26520";
+  public static final String s26521 = "26521";
+  public static final String s26522 = "26522";
+  public static final String s26523 = "26523";
+  public static final String s26524 = "26524";
+  public static final String s26525 = "26525";
+  public static final String s26526 = "26526";
+  public static final String s26527 = "26527";
+  public static final String s26528 = "26528";
+  public static final String s26529 = "26529";
+  public static final String s26530 = "26530";
+  public static final String s26531 = "26531";
+  public static final String s26532 = "26532";
+  public static final String s26533 = "26533";
+  public static final String s26534 = "26534";
+  public static final String s26535 = "26535";
+  public static final String s26536 = "26536";
+  public static final String s26537 = "26537";
+  public static final String s26538 = "26538";
+  public static final String s26539 = "26539";
+  public static final String s26540 = "26540";
+  public static final String s26541 = "26541";
+  public static final String s26542 = "26542";
+  public static final String s26543 = "26543";
+  public static final String s26544 = "26544";
+  public static final String s26545 = "26545";
+  public static final String s26546 = "26546";
+  public static final String s26547 = "26547";
+  public static final String s26548 = "26548";
+  public static final String s26549 = "26549";
+  public static final String s26550 = "26550";
+  public static final String s26551 = "26551";
+  public static final String s26552 = "26552";
+  public static final String s26553 = "26553";
+  public static final String s26554 = "26554";
+  public static final String s26555 = "26555";
+  public static final String s26556 = "26556";
+  public static final String s26557 = "26557";
+  public static final String s26558 = "26558";
+  public static final String s26559 = "26559";
+  public static final String s26560 = "26560";
+  public static final String s26561 = "26561";
+  public static final String s26562 = "26562";
+  public static final String s26563 = "26563";
+  public static final String s26564 = "26564";
+  public static final String s26565 = "26565";
+  public static final String s26566 = "26566";
+  public static final String s26567 = "26567";
+  public static final String s26568 = "26568";
+  public static final String s26569 = "26569";
+  public static final String s26570 = "26570";
+  public static final String s26571 = "26571";
+  public static final String s26572 = "26572";
+  public static final String s26573 = "26573";
+  public static final String s26574 = "26574";
+  public static final String s26575 = "26575";
+  public static final String s26576 = "26576";
+  public static final String s26577 = "26577";
+  public static final String s26578 = "26578";
+  public static final String s26579 = "26579";
+  public static final String s26580 = "26580";
+  public static final String s26581 = "26581";
+  public static final String s26582 = "26582";
+  public static final String s26583 = "26583";
+  public static final String s26584 = "26584";
+  public static final String s26585 = "26585";
+  public static final String s26586 = "26586";
+  public static final String s26587 = "26587";
+  public static final String s26588 = "26588";
+  public static final String s26589 = "26589";
+  public static final String s26590 = "26590";
+  public static final String s26591 = "26591";
+  public static final String s26592 = "26592";
+  public static final String s26593 = "26593";
+  public static final String s26594 = "26594";
+  public static final String s26595 = "26595";
+  public static final String s26596 = "26596";
+  public static final String s26597 = "26597";
+  public static final String s26598 = "26598";
+  public static final String s26599 = "26599";
+  public static final String s26600 = "26600";
+  public static final String s26601 = "26601";
+  public static final String s26602 = "26602";
+  public static final String s26603 = "26603";
+  public static final String s26604 = "26604";
+  public static final String s26605 = "26605";
+  public static final String s26606 = "26606";
+  public static final String s26607 = "26607";
+  public static final String s26608 = "26608";
+  public static final String s26609 = "26609";
+  public static final String s26610 = "26610";
+  public static final String s26611 = "26611";
+  public static final String s26612 = "26612";
+  public static final String s26613 = "26613";
+  public static final String s26614 = "26614";
+  public static final String s26615 = "26615";
+  public static final String s26616 = "26616";
+  public static final String s26617 = "26617";
+  public static final String s26618 = "26618";
+  public static final String s26619 = "26619";
+  public static final String s26620 = "26620";
+  public static final String s26621 = "26621";
+  public static final String s26622 = "26622";
+  public static final String s26623 = "26623";
+  public static final String s26624 = "26624";
+  public static final String s26625 = "26625";
+  public static final String s26626 = "26626";
+  public static final String s26627 = "26627";
+  public static final String s26628 = "26628";
+  public static final String s26629 = "26629";
+  public static final String s26630 = "26630";
+  public static final String s26631 = "26631";
+  public static final String s26632 = "26632";
+  public static final String s26633 = "26633";
+  public static final String s26634 = "26634";
+  public static final String s26635 = "26635";
+  public static final String s26636 = "26636";
+  public static final String s26637 = "26637";
+  public static final String s26638 = "26638";
+  public static final String s26639 = "26639";
+  public static final String s26640 = "26640";
+  public static final String s26641 = "26641";
+  public static final String s26642 = "26642";
+  public static final String s26643 = "26643";
+  public static final String s26644 = "26644";
+  public static final String s26645 = "26645";
+  public static final String s26646 = "26646";
+  public static final String s26647 = "26647";
+  public static final String s26648 = "26648";
+  public static final String s26649 = "26649";
+  public static final String s26650 = "26650";
+  public static final String s26651 = "26651";
+  public static final String s26652 = "26652";
+  public static final String s26653 = "26653";
+  public static final String s26654 = "26654";
+  public static final String s26655 = "26655";
+  public static final String s26656 = "26656";
+  public static final String s26657 = "26657";
+  public static final String s26658 = "26658";
+  public static final String s26659 = "26659";
+  public static final String s26660 = "26660";
+  public static final String s26661 = "26661";
+  public static final String s26662 = "26662";
+  public static final String s26663 = "26663";
+  public static final String s26664 = "26664";
+  public static final String s26665 = "26665";
+  public static final String s26666 = "26666";
+  public static final String s26667 = "26667";
+  public static final String s26668 = "26668";
+  public static final String s26669 = "26669";
+  public static final String s26670 = "26670";
+  public static final String s26671 = "26671";
+  public static final String s26672 = "26672";
+  public static final String s26673 = "26673";
+  public static final String s26674 = "26674";
+  public static final String s26675 = "26675";
+  public static final String s26676 = "26676";
+  public static final String s26677 = "26677";
+  public static final String s26678 = "26678";
+  public static final String s26679 = "26679";
+  public static final String s26680 = "26680";
+  public static final String s26681 = "26681";
+  public static final String s26682 = "26682";
+  public static final String s26683 = "26683";
+  public static final String s26684 = "26684";
+  public static final String s26685 = "26685";
+  public static final String s26686 = "26686";
+  public static final String s26687 = "26687";
+  public static final String s26688 = "26688";
+  public static final String s26689 = "26689";
+  public static final String s26690 = "26690";
+  public static final String s26691 = "26691";
+  public static final String s26692 = "26692";
+  public static final String s26693 = "26693";
+  public static final String s26694 = "26694";
+  public static final String s26695 = "26695";
+  public static final String s26696 = "26696";
+  public static final String s26697 = "26697";
+  public static final String s26698 = "26698";
+  public static final String s26699 = "26699";
+  public static final String s26700 = "26700";
+  public static final String s26701 = "26701";
+  public static final String s26702 = "26702";
+  public static final String s26703 = "26703";
+  public static final String s26704 = "26704";
+  public static final String s26705 = "26705";
+  public static final String s26706 = "26706";
+  public static final String s26707 = "26707";
+  public static final String s26708 = "26708";
+  public static final String s26709 = "26709";
+  public static final String s26710 = "26710";
+  public static final String s26711 = "26711";
+  public static final String s26712 = "26712";
+  public static final String s26713 = "26713";
+  public static final String s26714 = "26714";
+  public static final String s26715 = "26715";
+  public static final String s26716 = "26716";
+  public static final String s26717 = "26717";
+  public static final String s26718 = "26718";
+  public static final String s26719 = "26719";
+  public static final String s26720 = "26720";
+  public static final String s26721 = "26721";
+  public static final String s26722 = "26722";
+  public static final String s26723 = "26723";
+  public static final String s26724 = "26724";
+  public static final String s26725 = "26725";
+  public static final String s26726 = "26726";
+  public static final String s26727 = "26727";
+  public static final String s26728 = "26728";
+  public static final String s26729 = "26729";
+  public static final String s26730 = "26730";
+  public static final String s26731 = "26731";
+  public static final String s26732 = "26732";
+  public static final String s26733 = "26733";
+  public static final String s26734 = "26734";
+  public static final String s26735 = "26735";
+  public static final String s26736 = "26736";
+  public static final String s26737 = "26737";
+  public static final String s26738 = "26738";
+  public static final String s26739 = "26739";
+  public static final String s26740 = "26740";
+  public static final String s26741 = "26741";
+  public static final String s26742 = "26742";
+  public static final String s26743 = "26743";
+  public static final String s26744 = "26744";
+  public static final String s26745 = "26745";
+  public static final String s26746 = "26746";
+  public static final String s26747 = "26747";
+  public static final String s26748 = "26748";
+  public static final String s26749 = "26749";
+  public static final String s26750 = "26750";
+  public static final String s26751 = "26751";
+  public static final String s26752 = "26752";
+  public static final String s26753 = "26753";
+  public static final String s26754 = "26754";
+  public static final String s26755 = "26755";
+  public static final String s26756 = "26756";
+  public static final String s26757 = "26757";
+  public static final String s26758 = "26758";
+  public static final String s26759 = "26759";
+  public static final String s26760 = "26760";
+  public static final String s26761 = "26761";
+  public static final String s26762 = "26762";
+  public static final String s26763 = "26763";
+  public static final String s26764 = "26764";
+  public static final String s26765 = "26765";
+  public static final String s26766 = "26766";
+  public static final String s26767 = "26767";
+  public static final String s26768 = "26768";
+  public static final String s26769 = "26769";
+  public static final String s26770 = "26770";
+  public static final String s26771 = "26771";
+  public static final String s26772 = "26772";
+  public static final String s26773 = "26773";
+  public static final String s26774 = "26774";
+  public static final String s26775 = "26775";
+  public static final String s26776 = "26776";
+  public static final String s26777 = "26777";
+  public static final String s26778 = "26778";
+  public static final String s26779 = "26779";
+  public static final String s26780 = "26780";
+  public static final String s26781 = "26781";
+  public static final String s26782 = "26782";
+  public static final String s26783 = "26783";
+  public static final String s26784 = "26784";
+  public static final String s26785 = "26785";
+  public static final String s26786 = "26786";
+  public static final String s26787 = "26787";
+  public static final String s26788 = "26788";
+  public static final String s26789 = "26789";
+  public static final String s26790 = "26790";
+  public static final String s26791 = "26791";
+  public static final String s26792 = "26792";
+  public static final String s26793 = "26793";
+  public static final String s26794 = "26794";
+  public static final String s26795 = "26795";
+  public static final String s26796 = "26796";
+  public static final String s26797 = "26797";
+  public static final String s26798 = "26798";
+  public static final String s26799 = "26799";
+  public static final String s26800 = "26800";
+  public static final String s26801 = "26801";
+  public static final String s26802 = "26802";
+  public static final String s26803 = "26803";
+  public static final String s26804 = "26804";
+  public static final String s26805 = "26805";
+  public static final String s26806 = "26806";
+  public static final String s26807 = "26807";
+  public static final String s26808 = "26808";
+  public static final String s26809 = "26809";
+  public static final String s26810 = "26810";
+  public static final String s26811 = "26811";
+  public static final String s26812 = "26812";
+  public static final String s26813 = "26813";
+  public static final String s26814 = "26814";
+  public static final String s26815 = "26815";
+  public static final String s26816 = "26816";
+  public static final String s26817 = "26817";
+  public static final String s26818 = "26818";
+  public static final String s26819 = "26819";
+  public static final String s26820 = "26820";
+  public static final String s26821 = "26821";
+  public static final String s26822 = "26822";
+  public static final String s26823 = "26823";
+  public static final String s26824 = "26824";
+  public static final String s26825 = "26825";
+  public static final String s26826 = "26826";
+  public static final String s26827 = "26827";
+  public static final String s26828 = "26828";
+  public static final String s26829 = "26829";
+  public static final String s26830 = "26830";
+  public static final String s26831 = "26831";
+  public static final String s26832 = "26832";
+  public static final String s26833 = "26833";
+  public static final String s26834 = "26834";
+  public static final String s26835 = "26835";
+  public static final String s26836 = "26836";
+  public static final String s26837 = "26837";
+  public static final String s26838 = "26838";
+  public static final String s26839 = "26839";
+  public static final String s26840 = "26840";
+  public static final String s26841 = "26841";
+  public static final String s26842 = "26842";
+  public static final String s26843 = "26843";
+  public static final String s26844 = "26844";
+  public static final String s26845 = "26845";
+  public static final String s26846 = "26846";
+  public static final String s26847 = "26847";
+  public static final String s26848 = "26848";
+  public static final String s26849 = "26849";
+  public static final String s26850 = "26850";
+  public static final String s26851 = "26851";
+  public static final String s26852 = "26852";
+  public static final String s26853 = "26853";
+  public static final String s26854 = "26854";
+  public static final String s26855 = "26855";
+  public static final String s26856 = "26856";
+  public static final String s26857 = "26857";
+  public static final String s26858 = "26858";
+  public static final String s26859 = "26859";
+  public static final String s26860 = "26860";
+  public static final String s26861 = "26861";
+  public static final String s26862 = "26862";
+  public static final String s26863 = "26863";
+  public static final String s26864 = "26864";
+  public static final String s26865 = "26865";
+  public static final String s26866 = "26866";
+  public static final String s26867 = "26867";
+  public static final String s26868 = "26868";
+  public static final String s26869 = "26869";
+  public static final String s26870 = "26870";
+  public static final String s26871 = "26871";
+  public static final String s26872 = "26872";
+  public static final String s26873 = "26873";
+  public static final String s26874 = "26874";
+  public static final String s26875 = "26875";
+  public static final String s26876 = "26876";
+  public static final String s26877 = "26877";
+  public static final String s26878 = "26878";
+  public static final String s26879 = "26879";
+  public static final String s26880 = "26880";
+  public static final String s26881 = "26881";
+  public static final String s26882 = "26882";
+  public static final String s26883 = "26883";
+  public static final String s26884 = "26884";
+  public static final String s26885 = "26885";
+  public static final String s26886 = "26886";
+  public static final String s26887 = "26887";
+  public static final String s26888 = "26888";
+  public static final String s26889 = "26889";
+  public static final String s26890 = "26890";
+  public static final String s26891 = "26891";
+  public static final String s26892 = "26892";
+  public static final String s26893 = "26893";
+  public static final String s26894 = "26894";
+  public static final String s26895 = "26895";
+  public static final String s26896 = "26896";
+  public static final String s26897 = "26897";
+  public static final String s26898 = "26898";
+  public static final String s26899 = "26899";
+  public static final String s26900 = "26900";
+  public static final String s26901 = "26901";
+  public static final String s26902 = "26902";
+  public static final String s26903 = "26903";
+  public static final String s26904 = "26904";
+  public static final String s26905 = "26905";
+  public static final String s26906 = "26906";
+  public static final String s26907 = "26907";
+  public static final String s26908 = "26908";
+  public static final String s26909 = "26909";
+  public static final String s26910 = "26910";
+  public static final String s26911 = "26911";
+  public static final String s26912 = "26912";
+  public static final String s26913 = "26913";
+  public static final String s26914 = "26914";
+  public static final String s26915 = "26915";
+  public static final String s26916 = "26916";
+  public static final String s26917 = "26917";
+  public static final String s26918 = "26918";
+  public static final String s26919 = "26919";
+  public static final String s26920 = "26920";
+  public static final String s26921 = "26921";
+  public static final String s26922 = "26922";
+  public static final String s26923 = "26923";
+  public static final String s26924 = "26924";
+  public static final String s26925 = "26925";
+  public static final String s26926 = "26926";
+  public static final String s26927 = "26927";
+  public static final String s26928 = "26928";
+  public static final String s26929 = "26929";
+  public static final String s26930 = "26930";
+  public static final String s26931 = "26931";
+  public static final String s26932 = "26932";
+  public static final String s26933 = "26933";
+  public static final String s26934 = "26934";
+  public static final String s26935 = "26935";
+  public static final String s26936 = "26936";
+  public static final String s26937 = "26937";
+  public static final String s26938 = "26938";
+  public static final String s26939 = "26939";
+  public static final String s26940 = "26940";
+  public static final String s26941 = "26941";
+  public static final String s26942 = "26942";
+  public static final String s26943 = "26943";
+  public static final String s26944 = "26944";
+  public static final String s26945 = "26945";
+  public static final String s26946 = "26946";
+  public static final String s26947 = "26947";
+  public static final String s26948 = "26948";
+  public static final String s26949 = "26949";
+  public static final String s26950 = "26950";
+  public static final String s26951 = "26951";
+  public static final String s26952 = "26952";
+  public static final String s26953 = "26953";
+  public static final String s26954 = "26954";
+  public static final String s26955 = "26955";
+  public static final String s26956 = "26956";
+  public static final String s26957 = "26957";
+  public static final String s26958 = "26958";
+  public static final String s26959 = "26959";
+  public static final String s26960 = "26960";
+  public static final String s26961 = "26961";
+  public static final String s26962 = "26962";
+  public static final String s26963 = "26963";
+  public static final String s26964 = "26964";
+  public static final String s26965 = "26965";
+  public static final String s26966 = "26966";
+  public static final String s26967 = "26967";
+  public static final String s26968 = "26968";
+  public static final String s26969 = "26969";
+  public static final String s26970 = "26970";
+  public static final String s26971 = "26971";
+  public static final String s26972 = "26972";
+  public static final String s26973 = "26973";
+  public static final String s26974 = "26974";
+  public static final String s26975 = "26975";
+  public static final String s26976 = "26976";
+  public static final String s26977 = "26977";
+  public static final String s26978 = "26978";
+  public static final String s26979 = "26979";
+  public static final String s26980 = "26980";
+  public static final String s26981 = "26981";
+  public static final String s26982 = "26982";
+  public static final String s26983 = "26983";
+  public static final String s26984 = "26984";
+  public static final String s26985 = "26985";
+  public static final String s26986 = "26986";
+  public static final String s26987 = "26987";
+  public static final String s26988 = "26988";
+  public static final String s26989 = "26989";
+  public static final String s26990 = "26990";
+  public static final String s26991 = "26991";
+  public static final String s26992 = "26992";
+  public static final String s26993 = "26993";
+  public static final String s26994 = "26994";
+  public static final String s26995 = "26995";
+  public static final String s26996 = "26996";
+  public static final String s26997 = "26997";
+  public static final String s26998 = "26998";
+  public static final String s26999 = "26999";
+  public static final String s27000 = "27000";
+  public static final String s27001 = "27001";
+  public static final String s27002 = "27002";
+  public static final String s27003 = "27003";
+  public static final String s27004 = "27004";
+  public static final String s27005 = "27005";
+  public static final String s27006 = "27006";
+  public static final String s27007 = "27007";
+  public static final String s27008 = "27008";
+  public static final String s27009 = "27009";
+  public static final String s27010 = "27010";
+  public static final String s27011 = "27011";
+  public static final String s27012 = "27012";
+  public static final String s27013 = "27013";
+  public static final String s27014 = "27014";
+  public static final String s27015 = "27015";
+  public static final String s27016 = "27016";
+  public static final String s27017 = "27017";
+  public static final String s27018 = "27018";
+  public static final String s27019 = "27019";
+  public static final String s27020 = "27020";
+  public static final String s27021 = "27021";
+  public static final String s27022 = "27022";
+  public static final String s27023 = "27023";
+  public static final String s27024 = "27024";
+  public static final String s27025 = "27025";
+  public static final String s27026 = "27026";
+  public static final String s27027 = "27027";
+  public static final String s27028 = "27028";
+  public static final String s27029 = "27029";
+  public static final String s27030 = "27030";
+  public static final String s27031 = "27031";
+  public static final String s27032 = "27032";
+  public static final String s27033 = "27033";
+  public static final String s27034 = "27034";
+  public static final String s27035 = "27035";
+  public static final String s27036 = "27036";
+  public static final String s27037 = "27037";
+  public static final String s27038 = "27038";
+  public static final String s27039 = "27039";
+  public static final String s27040 = "27040";
+  public static final String s27041 = "27041";
+  public static final String s27042 = "27042";
+  public static final String s27043 = "27043";
+  public static final String s27044 = "27044";
+  public static final String s27045 = "27045";
+  public static final String s27046 = "27046";
+  public static final String s27047 = "27047";
+  public static final String s27048 = "27048";
+  public static final String s27049 = "27049";
+  public static final String s27050 = "27050";
+  public static final String s27051 = "27051";
+  public static final String s27052 = "27052";
+  public static final String s27053 = "27053";
+  public static final String s27054 = "27054";
+  public static final String s27055 = "27055";
+  public static final String s27056 = "27056";
+  public static final String s27057 = "27057";
+  public static final String s27058 = "27058";
+  public static final String s27059 = "27059";
+  public static final String s27060 = "27060";
+  public static final String s27061 = "27061";
+  public static final String s27062 = "27062";
+  public static final String s27063 = "27063";
+  public static final String s27064 = "27064";
+  public static final String s27065 = "27065";
+  public static final String s27066 = "27066";
+  public static final String s27067 = "27067";
+  public static final String s27068 = "27068";
+  public static final String s27069 = "27069";
+  public static final String s27070 = "27070";
+  public static final String s27071 = "27071";
+  public static final String s27072 = "27072";
+  public static final String s27073 = "27073";
+  public static final String s27074 = "27074";
+  public static final String s27075 = "27075";
+  public static final String s27076 = "27076";
+  public static final String s27077 = "27077";
+  public static final String s27078 = "27078";
+  public static final String s27079 = "27079";
+  public static final String s27080 = "27080";
+  public static final String s27081 = "27081";
+  public static final String s27082 = "27082";
+  public static final String s27083 = "27083";
+  public static final String s27084 = "27084";
+  public static final String s27085 = "27085";
+  public static final String s27086 = "27086";
+  public static final String s27087 = "27087";
+  public static final String s27088 = "27088";
+  public static final String s27089 = "27089";
+  public static final String s27090 = "27090";
+  public static final String s27091 = "27091";
+  public static final String s27092 = "27092";
+  public static final String s27093 = "27093";
+  public static final String s27094 = "27094";
+  public static final String s27095 = "27095";
+  public static final String s27096 = "27096";
+  public static final String s27097 = "27097";
+  public static final String s27098 = "27098";
+  public static final String s27099 = "27099";
+  public static final String s27100 = "27100";
+  public static final String s27101 = "27101";
+  public static final String s27102 = "27102";
+  public static final String s27103 = "27103";
+  public static final String s27104 = "27104";
+  public static final String s27105 = "27105";
+  public static final String s27106 = "27106";
+  public static final String s27107 = "27107";
+  public static final String s27108 = "27108";
+  public static final String s27109 = "27109";
+  public static final String s27110 = "27110";
+  public static final String s27111 = "27111";
+  public static final String s27112 = "27112";
+  public static final String s27113 = "27113";
+  public static final String s27114 = "27114";
+  public static final String s27115 = "27115";
+  public static final String s27116 = "27116";
+  public static final String s27117 = "27117";
+  public static final String s27118 = "27118";
+  public static final String s27119 = "27119";
+  public static final String s27120 = "27120";
+  public static final String s27121 = "27121";
+  public static final String s27122 = "27122";
+  public static final String s27123 = "27123";
+  public static final String s27124 = "27124";
+  public static final String s27125 = "27125";
+  public static final String s27126 = "27126";
+  public static final String s27127 = "27127";
+  public static final String s27128 = "27128";
+  public static final String s27129 = "27129";
+  public static final String s27130 = "27130";
+  public static final String s27131 = "27131";
+  public static final String s27132 = "27132";
+  public static final String s27133 = "27133";
+  public static final String s27134 = "27134";
+  public static final String s27135 = "27135";
+  public static final String s27136 = "27136";
+  public static final String s27137 = "27137";
+  public static final String s27138 = "27138";
+  public static final String s27139 = "27139";
+  public static final String s27140 = "27140";
+  public static final String s27141 = "27141";
+  public static final String s27142 = "27142";
+  public static final String s27143 = "27143";
+  public static final String s27144 = "27144";
+  public static final String s27145 = "27145";
+  public static final String s27146 = "27146";
+  public static final String s27147 = "27147";
+  public static final String s27148 = "27148";
+  public static final String s27149 = "27149";
+  public static final String s27150 = "27150";
+  public static final String s27151 = "27151";
+  public static final String s27152 = "27152";
+  public static final String s27153 = "27153";
+  public static final String s27154 = "27154";
+  public static final String s27155 = "27155";
+  public static final String s27156 = "27156";
+  public static final String s27157 = "27157";
+  public static final String s27158 = "27158";
+  public static final String s27159 = "27159";
+  public static final String s27160 = "27160";
+  public static final String s27161 = "27161";
+  public static final String s27162 = "27162";
+  public static final String s27163 = "27163";
+  public static final String s27164 = "27164";
+  public static final String s27165 = "27165";
+  public static final String s27166 = "27166";
+  public static final String s27167 = "27167";
+  public static final String s27168 = "27168";
+  public static final String s27169 = "27169";
+  public static final String s27170 = "27170";
+  public static final String s27171 = "27171";
+  public static final String s27172 = "27172";
+  public static final String s27173 = "27173";
+  public static final String s27174 = "27174";
+  public static final String s27175 = "27175";
+  public static final String s27176 = "27176";
+  public static final String s27177 = "27177";
+  public static final String s27178 = "27178";
+  public static final String s27179 = "27179";
+  public static final String s27180 = "27180";
+  public static final String s27181 = "27181";
+  public static final String s27182 = "27182";
+  public static final String s27183 = "27183";
+  public static final String s27184 = "27184";
+  public static final String s27185 = "27185";
+  public static final String s27186 = "27186";
+  public static final String s27187 = "27187";
+  public static final String s27188 = "27188";
+  public static final String s27189 = "27189";
+  public static final String s27190 = "27190";
+  public static final String s27191 = "27191";
+  public static final String s27192 = "27192";
+  public static final String s27193 = "27193";
+  public static final String s27194 = "27194";
+  public static final String s27195 = "27195";
+  public static final String s27196 = "27196";
+  public static final String s27197 = "27197";
+  public static final String s27198 = "27198";
+  public static final String s27199 = "27199";
+  public static final String s27200 = "27200";
+  public static final String s27201 = "27201";
+  public static final String s27202 = "27202";
+  public static final String s27203 = "27203";
+  public static final String s27204 = "27204";
+  public static final String s27205 = "27205";
+  public static final String s27206 = "27206";
+  public static final String s27207 = "27207";
+  public static final String s27208 = "27208";
+  public static final String s27209 = "27209";
+  public static final String s27210 = "27210";
+  public static final String s27211 = "27211";
+  public static final String s27212 = "27212";
+  public static final String s27213 = "27213";
+  public static final String s27214 = "27214";
+  public static final String s27215 = "27215";
+  public static final String s27216 = "27216";
+  public static final String s27217 = "27217";
+  public static final String s27218 = "27218";
+  public static final String s27219 = "27219";
+  public static final String s27220 = "27220";
+  public static final String s27221 = "27221";
+  public static final String s27222 = "27222";
+  public static final String s27223 = "27223";
+  public static final String s27224 = "27224";
+  public static final String s27225 = "27225";
+  public static final String s27226 = "27226";
+  public static final String s27227 = "27227";
+  public static final String s27228 = "27228";
+  public static final String s27229 = "27229";
+  public static final String s27230 = "27230";
+  public static final String s27231 = "27231";
+  public static final String s27232 = "27232";
+  public static final String s27233 = "27233";
+  public static final String s27234 = "27234";
+  public static final String s27235 = "27235";
+  public static final String s27236 = "27236";
+  public static final String s27237 = "27237";
+  public static final String s27238 = "27238";
+  public static final String s27239 = "27239";
+  public static final String s27240 = "27240";
+  public static final String s27241 = "27241";
+  public static final String s27242 = "27242";
+  public static final String s27243 = "27243";
+  public static final String s27244 = "27244";
+  public static final String s27245 = "27245";
+  public static final String s27246 = "27246";
+  public static final String s27247 = "27247";
+  public static final String s27248 = "27248";
+  public static final String s27249 = "27249";
+  public static final String s27250 = "27250";
+  public static final String s27251 = "27251";
+  public static final String s27252 = "27252";
+  public static final String s27253 = "27253";
+  public static final String s27254 = "27254";
+  public static final String s27255 = "27255";
+  public static final String s27256 = "27256";
+  public static final String s27257 = "27257";
+  public static final String s27258 = "27258";
+  public static final String s27259 = "27259";
+  public static final String s27260 = "27260";
+  public static final String s27261 = "27261";
+  public static final String s27262 = "27262";
+  public static final String s27263 = "27263";
+  public static final String s27264 = "27264";
+  public static final String s27265 = "27265";
+  public static final String s27266 = "27266";
+  public static final String s27267 = "27267";
+  public static final String s27268 = "27268";
+  public static final String s27269 = "27269";
+  public static final String s27270 = "27270";
+  public static final String s27271 = "27271";
+  public static final String s27272 = "27272";
+  public static final String s27273 = "27273";
+  public static final String s27274 = "27274";
+  public static final String s27275 = "27275";
+  public static final String s27276 = "27276";
+  public static final String s27277 = "27277";
+  public static final String s27278 = "27278";
+  public static final String s27279 = "27279";
+  public static final String s27280 = "27280";
+  public static final String s27281 = "27281";
+  public static final String s27282 = "27282";
+  public static final String s27283 = "27283";
+  public static final String s27284 = "27284";
+  public static final String s27285 = "27285";
+  public static final String s27286 = "27286";
+  public static final String s27287 = "27287";
+  public static final String s27288 = "27288";
+  public static final String s27289 = "27289";
+  public static final String s27290 = "27290";
+  public static final String s27291 = "27291";
+  public static final String s27292 = "27292";
+  public static final String s27293 = "27293";
+  public static final String s27294 = "27294";
+  public static final String s27295 = "27295";
+  public static final String s27296 = "27296";
+  public static final String s27297 = "27297";
+  public static final String s27298 = "27298";
+  public static final String s27299 = "27299";
+  public static final String s27300 = "27300";
+  public static final String s27301 = "27301";
+  public static final String s27302 = "27302";
+  public static final String s27303 = "27303";
+  public static final String s27304 = "27304";
+  public static final String s27305 = "27305";
+  public static final String s27306 = "27306";
+  public static final String s27307 = "27307";
+  public static final String s27308 = "27308";
+  public static final String s27309 = "27309";
+  public static final String s27310 = "27310";
+  public static final String s27311 = "27311";
+  public static final String s27312 = "27312";
+  public static final String s27313 = "27313";
+  public static final String s27314 = "27314";
+  public static final String s27315 = "27315";
+  public static final String s27316 = "27316";
+  public static final String s27317 = "27317";
+  public static final String s27318 = "27318";
+  public static final String s27319 = "27319";
+  public static final String s27320 = "27320";
+  public static final String s27321 = "27321";
+  public static final String s27322 = "27322";
+  public static final String s27323 = "27323";
+  public static final String s27324 = "27324";
+  public static final String s27325 = "27325";
+  public static final String s27326 = "27326";
+  public static final String s27327 = "27327";
+  public static final String s27328 = "27328";
+  public static final String s27329 = "27329";
+  public static final String s27330 = "27330";
+  public static final String s27331 = "27331";
+  public static final String s27332 = "27332";
+  public static final String s27333 = "27333";
+  public static final String s27334 = "27334";
+  public static final String s27335 = "27335";
+  public static final String s27336 = "27336";
+  public static final String s27337 = "27337";
+  public static final String s27338 = "27338";
+  public static final String s27339 = "27339";
+  public static final String s27340 = "27340";
+  public static final String s27341 = "27341";
+  public static final String s27342 = "27342";
+  public static final String s27343 = "27343";
+  public static final String s27344 = "27344";
+  public static final String s27345 = "27345";
+  public static final String s27346 = "27346";
+  public static final String s27347 = "27347";
+  public static final String s27348 = "27348";
+  public static final String s27349 = "27349";
+  public static final String s27350 = "27350";
+  public static final String s27351 = "27351";
+  public static final String s27352 = "27352";
+  public static final String s27353 = "27353";
+  public static final String s27354 = "27354";
+  public static final String s27355 = "27355";
+  public static final String s27356 = "27356";
+  public static final String s27357 = "27357";
+  public static final String s27358 = "27358";
+  public static final String s27359 = "27359";
+  public static final String s27360 = "27360";
+  public static final String s27361 = "27361";
+  public static final String s27362 = "27362";
+  public static final String s27363 = "27363";
+  public static final String s27364 = "27364";
+  public static final String s27365 = "27365";
+  public static final String s27366 = "27366";
+  public static final String s27367 = "27367";
+  public static final String s27368 = "27368";
+  public static final String s27369 = "27369";
+  public static final String s27370 = "27370";
+  public static final String s27371 = "27371";
+  public static final String s27372 = "27372";
+  public static final String s27373 = "27373";
+  public static final String s27374 = "27374";
+  public static final String s27375 = "27375";
+  public static final String s27376 = "27376";
+  public static final String s27377 = "27377";
+  public static final String s27378 = "27378";
+  public static final String s27379 = "27379";
+  public static final String s27380 = "27380";
+  public static final String s27381 = "27381";
+  public static final String s27382 = "27382";
+  public static final String s27383 = "27383";
+  public static final String s27384 = "27384";
+  public static final String s27385 = "27385";
+  public static final String s27386 = "27386";
+  public static final String s27387 = "27387";
+  public static final String s27388 = "27388";
+  public static final String s27389 = "27389";
+  public static final String s27390 = "27390";
+  public static final String s27391 = "27391";
+  public static final String s27392 = "27392";
+  public static final String s27393 = "27393";
+  public static final String s27394 = "27394";
+  public static final String s27395 = "27395";
+  public static final String s27396 = "27396";
+  public static final String s27397 = "27397";
+  public static final String s27398 = "27398";
+  public static final String s27399 = "27399";
+  public static final String s27400 = "27400";
+  public static final String s27401 = "27401";
+  public static final String s27402 = "27402";
+  public static final String s27403 = "27403";
+  public static final String s27404 = "27404";
+  public static final String s27405 = "27405";
+  public static final String s27406 = "27406";
+  public static final String s27407 = "27407";
+  public static final String s27408 = "27408";
+  public static final String s27409 = "27409";
+  public static final String s27410 = "27410";
+  public static final String s27411 = "27411";
+  public static final String s27412 = "27412";
+  public static final String s27413 = "27413";
+  public static final String s27414 = "27414";
+  public static final String s27415 = "27415";
+  public static final String s27416 = "27416";
+  public static final String s27417 = "27417";
+  public static final String s27418 = "27418";
+  public static final String s27419 = "27419";
+  public static final String s27420 = "27420";
+  public static final String s27421 = "27421";
+  public static final String s27422 = "27422";
+  public static final String s27423 = "27423";
+  public static final String s27424 = "27424";
+  public static final String s27425 = "27425";
+  public static final String s27426 = "27426";
+  public static final String s27427 = "27427";
+  public static final String s27428 = "27428";
+  public static final String s27429 = "27429";
+  public static final String s27430 = "27430";
+  public static final String s27431 = "27431";
+  public static final String s27432 = "27432";
+  public static final String s27433 = "27433";
+  public static final String s27434 = "27434";
+  public static final String s27435 = "27435";
+  public static final String s27436 = "27436";
+  public static final String s27437 = "27437";
+  public static final String s27438 = "27438";
+  public static final String s27439 = "27439";
+  public static final String s27440 = "27440";
+  public static final String s27441 = "27441";
+  public static final String s27442 = "27442";
+  public static final String s27443 = "27443";
+  public static final String s27444 = "27444";
+  public static final String s27445 = "27445";
+  public static final String s27446 = "27446";
+  public static final String s27447 = "27447";
+  public static final String s27448 = "27448";
+  public static final String s27449 = "27449";
+  public static final String s27450 = "27450";
+  public static final String s27451 = "27451";
+  public static final String s27452 = "27452";
+  public static final String s27453 = "27453";
+  public static final String s27454 = "27454";
+  public static final String s27455 = "27455";
+  public static final String s27456 = "27456";
+  public static final String s27457 = "27457";
+  public static final String s27458 = "27458";
+  public static final String s27459 = "27459";
+  public static final String s27460 = "27460";
+  public static final String s27461 = "27461";
+  public static final String s27462 = "27462";
+  public static final String s27463 = "27463";
+  public static final String s27464 = "27464";
+  public static final String s27465 = "27465";
+  public static final String s27466 = "27466";
+  public static final String s27467 = "27467";
+  public static final String s27468 = "27468";
+  public static final String s27469 = "27469";
+  public static final String s27470 = "27470";
+  public static final String s27471 = "27471";
+  public static final String s27472 = "27472";
+  public static final String s27473 = "27473";
+  public static final String s27474 = "27474";
+  public static final String s27475 = "27475";
+  public static final String s27476 = "27476";
+  public static final String s27477 = "27477";
+  public static final String s27478 = "27478";
+  public static final String s27479 = "27479";
+  public static final String s27480 = "27480";
+  public static final String s27481 = "27481";
+  public static final String s27482 = "27482";
+  public static final String s27483 = "27483";
+  public static final String s27484 = "27484";
+  public static final String s27485 = "27485";
+  public static final String s27486 = "27486";
+  public static final String s27487 = "27487";
+  public static final String s27488 = "27488";
+  public static final String s27489 = "27489";
+  public static final String s27490 = "27490";
+  public static final String s27491 = "27491";
+  public static final String s27492 = "27492";
+  public static final String s27493 = "27493";
+  public static final String s27494 = "27494";
+  public static final String s27495 = "27495";
+  public static final String s27496 = "27496";
+  public static final String s27497 = "27497";
+  public static final String s27498 = "27498";
+  public static final String s27499 = "27499";
+  public static final String s27500 = "27500";
+  public static final String s27501 = "27501";
+  public static final String s27502 = "27502";
+  public static final String s27503 = "27503";
+  public static final String s27504 = "27504";
+  public static final String s27505 = "27505";
+  public static final String s27506 = "27506";
+  public static final String s27507 = "27507";
+  public static final String s27508 = "27508";
+  public static final String s27509 = "27509";
+  public static final String s27510 = "27510";
+  public static final String s27511 = "27511";
+  public static final String s27512 = "27512";
+  public static final String s27513 = "27513";
+  public static final String s27514 = "27514";
+  public static final String s27515 = "27515";
+  public static final String s27516 = "27516";
+  public static final String s27517 = "27517";
+  public static final String s27518 = "27518";
+  public static final String s27519 = "27519";
+  public static final String s27520 = "27520";
+  public static final String s27521 = "27521";
+  public static final String s27522 = "27522";
+  public static final String s27523 = "27523";
+  public static final String s27524 = "27524";
+  public static final String s27525 = "27525";
+  public static final String s27526 = "27526";
+  public static final String s27527 = "27527";
+  public static final String s27528 = "27528";
+  public static final String s27529 = "27529";
+  public static final String s27530 = "27530";
+  public static final String s27531 = "27531";
+  public static final String s27532 = "27532";
+  public static final String s27533 = "27533";
+  public static final String s27534 = "27534";
+  public static final String s27535 = "27535";
+  public static final String s27536 = "27536";
+  public static final String s27537 = "27537";
+  public static final String s27538 = "27538";
+  public static final String s27539 = "27539";
+  public static final String s27540 = "27540";
+  public static final String s27541 = "27541";
+  public static final String s27542 = "27542";
+  public static final String s27543 = "27543";
+  public static final String s27544 = "27544";
+  public static final String s27545 = "27545";
+  public static final String s27546 = "27546";
+  public static final String s27547 = "27547";
+  public static final String s27548 = "27548";
+  public static final String s27549 = "27549";
+  public static final String s27550 = "27550";
+  public static final String s27551 = "27551";
+  public static final String s27552 = "27552";
+  public static final String s27553 = "27553";
+  public static final String s27554 = "27554";
+  public static final String s27555 = "27555";
+  public static final String s27556 = "27556";
+  public static final String s27557 = "27557";
+  public static final String s27558 = "27558";
+  public static final String s27559 = "27559";
+  public static final String s27560 = "27560";
+  public static final String s27561 = "27561";
+  public static final String s27562 = "27562";
+  public static final String s27563 = "27563";
+  public static final String s27564 = "27564";
+  public static final String s27565 = "27565";
+  public static final String s27566 = "27566";
+  public static final String s27567 = "27567";
+  public static final String s27568 = "27568";
+  public static final String s27569 = "27569";
+  public static final String s27570 = "27570";
+  public static final String s27571 = "27571";
+  public static final String s27572 = "27572";
+  public static final String s27573 = "27573";
+  public static final String s27574 = "27574";
+  public static final String s27575 = "27575";
+  public static final String s27576 = "27576";
+  public static final String s27577 = "27577";
+  public static final String s27578 = "27578";
+  public static final String s27579 = "27579";
+  public static final String s27580 = "27580";
+  public static final String s27581 = "27581";
+  public static final String s27582 = "27582";
+  public static final String s27583 = "27583";
+  public static final String s27584 = "27584";
+  public static final String s27585 = "27585";
+  public static final String s27586 = "27586";
+  public static final String s27587 = "27587";
+  public static final String s27588 = "27588";
+  public static final String s27589 = "27589";
+  public static final String s27590 = "27590";
+  public static final String s27591 = "27591";
+  public static final String s27592 = "27592";
+  public static final String s27593 = "27593";
+  public static final String s27594 = "27594";
+  public static final String s27595 = "27595";
+  public static final String s27596 = "27596";
+  public static final String s27597 = "27597";
+  public static final String s27598 = "27598";
+  public static final String s27599 = "27599";
+  public static final String s27600 = "27600";
+  public static final String s27601 = "27601";
+  public static final String s27602 = "27602";
+  public static final String s27603 = "27603";
+  public static final String s27604 = "27604";
+  public static final String s27605 = "27605";
+  public static final String s27606 = "27606";
+  public static final String s27607 = "27607";
+  public static final String s27608 = "27608";
+  public static final String s27609 = "27609";
+  public static final String s27610 = "27610";
+  public static final String s27611 = "27611";
+  public static final String s27612 = "27612";
+  public static final String s27613 = "27613";
+  public static final String s27614 = "27614";
+  public static final String s27615 = "27615";
+  public static final String s27616 = "27616";
+  public static final String s27617 = "27617";
+  public static final String s27618 = "27618";
+  public static final String s27619 = "27619";
+  public static final String s27620 = "27620";
+  public static final String s27621 = "27621";
+  public static final String s27622 = "27622";
+  public static final String s27623 = "27623";
+  public static final String s27624 = "27624";
+  public static final String s27625 = "27625";
+  public static final String s27626 = "27626";
+  public static final String s27627 = "27627";
+  public static final String s27628 = "27628";
+  public static final String s27629 = "27629";
+  public static final String s27630 = "27630";
+  public static final String s27631 = "27631";
+  public static final String s27632 = "27632";
+  public static final String s27633 = "27633";
+  public static final String s27634 = "27634";
+  public static final String s27635 = "27635";
+  public static final String s27636 = "27636";
+  public static final String s27637 = "27637";
+  public static final String s27638 = "27638";
+  public static final String s27639 = "27639";
+  public static final String s27640 = "27640";
+  public static final String s27641 = "27641";
+  public static final String s27642 = "27642";
+  public static final String s27643 = "27643";
+  public static final String s27644 = "27644";
+  public static final String s27645 = "27645";
+  public static final String s27646 = "27646";
+  public static final String s27647 = "27647";
+  public static final String s27648 = "27648";
+  public static final String s27649 = "27649";
+  public static final String s27650 = "27650";
+  public static final String s27651 = "27651";
+  public static final String s27652 = "27652";
+  public static final String s27653 = "27653";
+  public static final String s27654 = "27654";
+  public static final String s27655 = "27655";
+  public static final String s27656 = "27656";
+  public static final String s27657 = "27657";
+  public static final String s27658 = "27658";
+  public static final String s27659 = "27659";
+  public static final String s27660 = "27660";
+  public static final String s27661 = "27661";
+  public static final String s27662 = "27662";
+  public static final String s27663 = "27663";
+  public static final String s27664 = "27664";
+  public static final String s27665 = "27665";
+  public static final String s27666 = "27666";
+  public static final String s27667 = "27667";
+  public static final String s27668 = "27668";
+  public static final String s27669 = "27669";
+  public static final String s27670 = "27670";
+  public static final String s27671 = "27671";
+  public static final String s27672 = "27672";
+  public static final String s27673 = "27673";
+  public static final String s27674 = "27674";
+  public static final String s27675 = "27675";
+  public static final String s27676 = "27676";
+  public static final String s27677 = "27677";
+  public static final String s27678 = "27678";
+  public static final String s27679 = "27679";
+  public static final String s27680 = "27680";
+  public static final String s27681 = "27681";
+  public static final String s27682 = "27682";
+  public static final String s27683 = "27683";
+  public static final String s27684 = "27684";
+  public static final String s27685 = "27685";
+  public static final String s27686 = "27686";
+  public static final String s27687 = "27687";
+  public static final String s27688 = "27688";
+  public static final String s27689 = "27689";
+  public static final String s27690 = "27690";
+  public static final String s27691 = "27691";
+  public static final String s27692 = "27692";
+  public static final String s27693 = "27693";
+  public static final String s27694 = "27694";
+  public static final String s27695 = "27695";
+  public static final String s27696 = "27696";
+  public static final String s27697 = "27697";
+  public static final String s27698 = "27698";
+  public static final String s27699 = "27699";
+  public static final String s27700 = "27700";
+  public static final String s27701 = "27701";
+  public static final String s27702 = "27702";
+  public static final String s27703 = "27703";
+  public static final String s27704 = "27704";
+  public static final String s27705 = "27705";
+  public static final String s27706 = "27706";
+  public static final String s27707 = "27707";
+  public static final String s27708 = "27708";
+  public static final String s27709 = "27709";
+  public static final String s27710 = "27710";
+  public static final String s27711 = "27711";
+  public static final String s27712 = "27712";
+  public static final String s27713 = "27713";
+  public static final String s27714 = "27714";
+  public static final String s27715 = "27715";
+  public static final String s27716 = "27716";
+  public static final String s27717 = "27717";
+  public static final String s27718 = "27718";
+  public static final String s27719 = "27719";
+  public static final String s27720 = "27720";
+  public static final String s27721 = "27721";
+  public static final String s27722 = "27722";
+  public static final String s27723 = "27723";
+  public static final String s27724 = "27724";
+  public static final String s27725 = "27725";
+  public static final String s27726 = "27726";
+  public static final String s27727 = "27727";
+  public static final String s27728 = "27728";
+  public static final String s27729 = "27729";
+  public static final String s27730 = "27730";
+  public static final String s27731 = "27731";
+  public static final String s27732 = "27732";
+  public static final String s27733 = "27733";
+  public static final String s27734 = "27734";
+  public static final String s27735 = "27735";
+  public static final String s27736 = "27736";
+  public static final String s27737 = "27737";
+  public static final String s27738 = "27738";
+  public static final String s27739 = "27739";
+  public static final String s27740 = "27740";
+  public static final String s27741 = "27741";
+  public static final String s27742 = "27742";
+  public static final String s27743 = "27743";
+  public static final String s27744 = "27744";
+  public static final String s27745 = "27745";
+  public static final String s27746 = "27746";
+  public static final String s27747 = "27747";
+  public static final String s27748 = "27748";
+  public static final String s27749 = "27749";
+  public static final String s27750 = "27750";
+  public static final String s27751 = "27751";
+  public static final String s27752 = "27752";
+  public static final String s27753 = "27753";
+  public static final String s27754 = "27754";
+  public static final String s27755 = "27755";
+  public static final String s27756 = "27756";
+  public static final String s27757 = "27757";
+  public static final String s27758 = "27758";
+  public static final String s27759 = "27759";
+  public static final String s27760 = "27760";
+  public static final String s27761 = "27761";
+  public static final String s27762 = "27762";
+  public static final String s27763 = "27763";
+  public static final String s27764 = "27764";
+  public static final String s27765 = "27765";
+  public static final String s27766 = "27766";
+  public static final String s27767 = "27767";
+  public static final String s27768 = "27768";
+  public static final String s27769 = "27769";
+  public static final String s27770 = "27770";
+  public static final String s27771 = "27771";
+  public static final String s27772 = "27772";
+  public static final String s27773 = "27773";
+  public static final String s27774 = "27774";
+  public static final String s27775 = "27775";
+  public static final String s27776 = "27776";
+  public static final String s27777 = "27777";
+  public static final String s27778 = "27778";
+  public static final String s27779 = "27779";
+  public static final String s27780 = "27780";
+  public static final String s27781 = "27781";
+  public static final String s27782 = "27782";
+  public static final String s27783 = "27783";
+  public static final String s27784 = "27784";
+  public static final String s27785 = "27785";
+  public static final String s27786 = "27786";
+  public static final String s27787 = "27787";
+  public static final String s27788 = "27788";
+  public static final String s27789 = "27789";
+  public static final String s27790 = "27790";
+  public static final String s27791 = "27791";
+  public static final String s27792 = "27792";
+  public static final String s27793 = "27793";
+  public static final String s27794 = "27794";
+  public static final String s27795 = "27795";
+  public static final String s27796 = "27796";
+  public static final String s27797 = "27797";
+  public static final String s27798 = "27798";
+  public static final String s27799 = "27799";
+  public static final String s27800 = "27800";
+  public static final String s27801 = "27801";
+  public static final String s27802 = "27802";
+  public static final String s27803 = "27803";
+  public static final String s27804 = "27804";
+  public static final String s27805 = "27805";
+  public static final String s27806 = "27806";
+  public static final String s27807 = "27807";
+  public static final String s27808 = "27808";
+  public static final String s27809 = "27809";
+  public static final String s27810 = "27810";
+  public static final String s27811 = "27811";
+  public static final String s27812 = "27812";
+  public static final String s27813 = "27813";
+  public static final String s27814 = "27814";
+  public static final String s27815 = "27815";
+  public static final String s27816 = "27816";
+  public static final String s27817 = "27817";
+  public static final String s27818 = "27818";
+  public static final String s27819 = "27819";
+  public static final String s27820 = "27820";
+  public static final String s27821 = "27821";
+  public static final String s27822 = "27822";
+  public static final String s27823 = "27823";
+  public static final String s27824 = "27824";
+  public static final String s27825 = "27825";
+  public static final String s27826 = "27826";
+  public static final String s27827 = "27827";
+  public static final String s27828 = "27828";
+  public static final String s27829 = "27829";
+  public static final String s27830 = "27830";
+  public static final String s27831 = "27831";
+  public static final String s27832 = "27832";
+  public static final String s27833 = "27833";
+  public static final String s27834 = "27834";
+  public static final String s27835 = "27835";
+  public static final String s27836 = "27836";
+  public static final String s27837 = "27837";
+  public static final String s27838 = "27838";
+  public static final String s27839 = "27839";
+  public static final String s27840 = "27840";
+  public static final String s27841 = "27841";
+  public static final String s27842 = "27842";
+  public static final String s27843 = "27843";
+  public static final String s27844 = "27844";
+  public static final String s27845 = "27845";
+  public static final String s27846 = "27846";
+  public static final String s27847 = "27847";
+  public static final String s27848 = "27848";
+  public static final String s27849 = "27849";
+  public static final String s27850 = "27850";
+  public static final String s27851 = "27851";
+  public static final String s27852 = "27852";
+  public static final String s27853 = "27853";
+  public static final String s27854 = "27854";
+  public static final String s27855 = "27855";
+  public static final String s27856 = "27856";
+  public static final String s27857 = "27857";
+  public static final String s27858 = "27858";
+  public static final String s27859 = "27859";
+  public static final String s27860 = "27860";
+  public static final String s27861 = "27861";
+  public static final String s27862 = "27862";
+  public static final String s27863 = "27863";
+  public static final String s27864 = "27864";
+  public static final String s27865 = "27865";
+  public static final String s27866 = "27866";
+  public static final String s27867 = "27867";
+  public static final String s27868 = "27868";
+  public static final String s27869 = "27869";
+  public static final String s27870 = "27870";
+  public static final String s27871 = "27871";
+  public static final String s27872 = "27872";
+  public static final String s27873 = "27873";
+  public static final String s27874 = "27874";
+  public static final String s27875 = "27875";
+  public static final String s27876 = "27876";
+  public static final String s27877 = "27877";
+  public static final String s27878 = "27878";
+  public static final String s27879 = "27879";
+  public static final String s27880 = "27880";
+  public static final String s27881 = "27881";
+  public static final String s27882 = "27882";
+  public static final String s27883 = "27883";
+  public static final String s27884 = "27884";
+  public static final String s27885 = "27885";
+  public static final String s27886 = "27886";
+  public static final String s27887 = "27887";
+  public static final String s27888 = "27888";
+  public static final String s27889 = "27889";
+  public static final String s27890 = "27890";
+  public static final String s27891 = "27891";
+  public static final String s27892 = "27892";
+  public static final String s27893 = "27893";
+  public static final String s27894 = "27894";
+  public static final String s27895 = "27895";
+  public static final String s27896 = "27896";
+  public static final String s27897 = "27897";
+  public static final String s27898 = "27898";
+  public static final String s27899 = "27899";
+  public static final String s27900 = "27900";
+  public static final String s27901 = "27901";
+  public static final String s27902 = "27902";
+  public static final String s27903 = "27903";
+  public static final String s27904 = "27904";
+  public static final String s27905 = "27905";
+  public static final String s27906 = "27906";
+  public static final String s27907 = "27907";
+  public static final String s27908 = "27908";
+  public static final String s27909 = "27909";
+  public static final String s27910 = "27910";
+  public static final String s27911 = "27911";
+  public static final String s27912 = "27912";
+  public static final String s27913 = "27913";
+  public static final String s27914 = "27914";
+  public static final String s27915 = "27915";
+  public static final String s27916 = "27916";
+  public static final String s27917 = "27917";
+  public static final String s27918 = "27918";
+  public static final String s27919 = "27919";
+  public static final String s27920 = "27920";
+  public static final String s27921 = "27921";
+  public static final String s27922 = "27922";
+  public static final String s27923 = "27923";
+  public static final String s27924 = "27924";
+  public static final String s27925 = "27925";
+  public static final String s27926 = "27926";
+  public static final String s27927 = "27927";
+  public static final String s27928 = "27928";
+  public static final String s27929 = "27929";
+  public static final String s27930 = "27930";
+  public static final String s27931 = "27931";
+  public static final String s27932 = "27932";
+  public static final String s27933 = "27933";
+  public static final String s27934 = "27934";
+  public static final String s27935 = "27935";
+  public static final String s27936 = "27936";
+  public static final String s27937 = "27937";
+  public static final String s27938 = "27938";
+  public static final String s27939 = "27939";
+  public static final String s27940 = "27940";
+  public static final String s27941 = "27941";
+  public static final String s27942 = "27942";
+  public static final String s27943 = "27943";
+  public static final String s27944 = "27944";
+  public static final String s27945 = "27945";
+  public static final String s27946 = "27946";
+  public static final String s27947 = "27947";
+  public static final String s27948 = "27948";
+  public static final String s27949 = "27949";
+  public static final String s27950 = "27950";
+  public static final String s27951 = "27951";
+  public static final String s27952 = "27952";
+  public static final String s27953 = "27953";
+  public static final String s27954 = "27954";
+  public static final String s27955 = "27955";
+  public static final String s27956 = "27956";
+  public static final String s27957 = "27957";
+  public static final String s27958 = "27958";
+  public static final String s27959 = "27959";
+  public static final String s27960 = "27960";
+  public static final String s27961 = "27961";
+  public static final String s27962 = "27962";
+  public static final String s27963 = "27963";
+  public static final String s27964 = "27964";
+  public static final String s27965 = "27965";
+  public static final String s27966 = "27966";
+  public static final String s27967 = "27967";
+  public static final String s27968 = "27968";
+  public static final String s27969 = "27969";
+  public static final String s27970 = "27970";
+  public static final String s27971 = "27971";
+  public static final String s27972 = "27972";
+  public static final String s27973 = "27973";
+  public static final String s27974 = "27974";
+  public static final String s27975 = "27975";
+  public static final String s27976 = "27976";
+  public static final String s27977 = "27977";
+  public static final String s27978 = "27978";
+  public static final String s27979 = "27979";
+  public static final String s27980 = "27980";
+  public static final String s27981 = "27981";
+  public static final String s27982 = "27982";
+  public static final String s27983 = "27983";
+  public static final String s27984 = "27984";
+  public static final String s27985 = "27985";
+  public static final String s27986 = "27986";
+  public static final String s27987 = "27987";
+  public static final String s27988 = "27988";
+  public static final String s27989 = "27989";
+  public static final String s27990 = "27990";
+  public static final String s27991 = "27991";
+  public static final String s27992 = "27992";
+  public static final String s27993 = "27993";
+  public static final String s27994 = "27994";
+  public static final String s27995 = "27995";
+  public static final String s27996 = "27996";
+  public static final String s27997 = "27997";
+  public static final String s27998 = "27998";
+  public static final String s27999 = "27999";
+  public static final String s28000 = "28000";
+  public static final String s28001 = "28001";
+  public static final String s28002 = "28002";
+  public static final String s28003 = "28003";
+  public static final String s28004 = "28004";
+  public static final String s28005 = "28005";
+  public static final String s28006 = "28006";
+  public static final String s28007 = "28007";
+  public static final String s28008 = "28008";
+  public static final String s28009 = "28009";
+  public static final String s28010 = "28010";
+  public static final String s28011 = "28011";
+  public static final String s28012 = "28012";
+  public static final String s28013 = "28013";
+  public static final String s28014 = "28014";
+  public static final String s28015 = "28015";
+  public static final String s28016 = "28016";
+  public static final String s28017 = "28017";
+  public static final String s28018 = "28018";
+  public static final String s28019 = "28019";
+  public static final String s28020 = "28020";
+  public static final String s28021 = "28021";
+  public static final String s28022 = "28022";
+  public static final String s28023 = "28023";
+  public static final String s28024 = "28024";
+  public static final String s28025 = "28025";
+  public static final String s28026 = "28026";
+  public static final String s28027 = "28027";
+  public static final String s28028 = "28028";
+  public static final String s28029 = "28029";
+  public static final String s28030 = "28030";
+  public static final String s28031 = "28031";
+  public static final String s28032 = "28032";
+  public static final String s28033 = "28033";
+  public static final String s28034 = "28034";
+  public static final String s28035 = "28035";
+  public static final String s28036 = "28036";
+  public static final String s28037 = "28037";
+  public static final String s28038 = "28038";
+  public static final String s28039 = "28039";
+  public static final String s28040 = "28040";
+  public static final String s28041 = "28041";
+  public static final String s28042 = "28042";
+  public static final String s28043 = "28043";
+  public static final String s28044 = "28044";
+  public static final String s28045 = "28045";
+  public static final String s28046 = "28046";
+  public static final String s28047 = "28047";
+  public static final String s28048 = "28048";
+  public static final String s28049 = "28049";
+  public static final String s28050 = "28050";
+  public static final String s28051 = "28051";
+  public static final String s28052 = "28052";
+  public static final String s28053 = "28053";
+  public static final String s28054 = "28054";
+  public static final String s28055 = "28055";
+  public static final String s28056 = "28056";
+  public static final String s28057 = "28057";
+  public static final String s28058 = "28058";
+  public static final String s28059 = "28059";
+  public static final String s28060 = "28060";
+  public static final String s28061 = "28061";
+  public static final String s28062 = "28062";
+  public static final String s28063 = "28063";
+  public static final String s28064 = "28064";
+  public static final String s28065 = "28065";
+  public static final String s28066 = "28066";
+  public static final String s28067 = "28067";
+  public static final String s28068 = "28068";
+  public static final String s28069 = "28069";
+  public static final String s28070 = "28070";
+  public static final String s28071 = "28071";
+  public static final String s28072 = "28072";
+  public static final String s28073 = "28073";
+  public static final String s28074 = "28074";
+  public static final String s28075 = "28075";
+  public static final String s28076 = "28076";
+  public static final String s28077 = "28077";
+  public static final String s28078 = "28078";
+  public static final String s28079 = "28079";
+  public static final String s28080 = "28080";
+  public static final String s28081 = "28081";
+  public static final String s28082 = "28082";
+  public static final String s28083 = "28083";
+  public static final String s28084 = "28084";
+  public static final String s28085 = "28085";
+  public static final String s28086 = "28086";
+  public static final String s28087 = "28087";
+  public static final String s28088 = "28088";
+  public static final String s28089 = "28089";
+  public static final String s28090 = "28090";
+  public static final String s28091 = "28091";
+  public static final String s28092 = "28092";
+  public static final String s28093 = "28093";
+  public static final String s28094 = "28094";
+  public static final String s28095 = "28095";
+  public static final String s28096 = "28096";
+  public static final String s28097 = "28097";
+  public static final String s28098 = "28098";
+  public static final String s28099 = "28099";
+  public static final String s28100 = "28100";
+  public static final String s28101 = "28101";
+  public static final String s28102 = "28102";
+  public static final String s28103 = "28103";
+  public static final String s28104 = "28104";
+  public static final String s28105 = "28105";
+  public static final String s28106 = "28106";
+  public static final String s28107 = "28107";
+  public static final String s28108 = "28108";
+  public static final String s28109 = "28109";
+  public static final String s28110 = "28110";
+  public static final String s28111 = "28111";
+  public static final String s28112 = "28112";
+  public static final String s28113 = "28113";
+  public static final String s28114 = "28114";
+  public static final String s28115 = "28115";
+  public static final String s28116 = "28116";
+  public static final String s28117 = "28117";
+  public static final String s28118 = "28118";
+  public static final String s28119 = "28119";
+  public static final String s28120 = "28120";
+  public static final String s28121 = "28121";
+  public static final String s28122 = "28122";
+  public static final String s28123 = "28123";
+  public static final String s28124 = "28124";
+  public static final String s28125 = "28125";
+  public static final String s28126 = "28126";
+  public static final String s28127 = "28127";
+  public static final String s28128 = "28128";
+  public static final String s28129 = "28129";
+  public static final String s28130 = "28130";
+  public static final String s28131 = "28131";
+  public static final String s28132 = "28132";
+  public static final String s28133 = "28133";
+  public static final String s28134 = "28134";
+  public static final String s28135 = "28135";
+  public static final String s28136 = "28136";
+  public static final String s28137 = "28137";
+  public static final String s28138 = "28138";
+  public static final String s28139 = "28139";
+  public static final String s28140 = "28140";
+  public static final String s28141 = "28141";
+  public static final String s28142 = "28142";
+  public static final String s28143 = "28143";
+  public static final String s28144 = "28144";
+  public static final String s28145 = "28145";
+  public static final String s28146 = "28146";
+  public static final String s28147 = "28147";
+  public static final String s28148 = "28148";
+  public static final String s28149 = "28149";
+  public static final String s28150 = "28150";
+  public static final String s28151 = "28151";
+  public static final String s28152 = "28152";
+  public static final String s28153 = "28153";
+  public static final String s28154 = "28154";
+  public static final String s28155 = "28155";
+  public static final String s28156 = "28156";
+  public static final String s28157 = "28157";
+  public static final String s28158 = "28158";
+  public static final String s28159 = "28159";
+  public static final String s28160 = "28160";
+  public static final String s28161 = "28161";
+  public static final String s28162 = "28162";
+  public static final String s28163 = "28163";
+  public static final String s28164 = "28164";
+  public static final String s28165 = "28165";
+  public static final String s28166 = "28166";
+  public static final String s28167 = "28167";
+  public static final String s28168 = "28168";
+  public static final String s28169 = "28169";
+  public static final String s28170 = "28170";
+  public static final String s28171 = "28171";
+  public static final String s28172 = "28172";
+  public static final String s28173 = "28173";
+  public static final String s28174 = "28174";
+  public static final String s28175 = "28175";
+  public static final String s28176 = "28176";
+  public static final String s28177 = "28177";
+  public static final String s28178 = "28178";
+  public static final String s28179 = "28179";
+  public static final String s28180 = "28180";
+  public static final String s28181 = "28181";
+  public static final String s28182 = "28182";
+  public static final String s28183 = "28183";
+  public static final String s28184 = "28184";
+  public static final String s28185 = "28185";
+  public static final String s28186 = "28186";
+  public static final String s28187 = "28187";
+  public static final String s28188 = "28188";
+  public static final String s28189 = "28189";
+  public static final String s28190 = "28190";
+  public static final String s28191 = "28191";
+  public static final String s28192 = "28192";
+  public static final String s28193 = "28193";
+  public static final String s28194 = "28194";
+  public static final String s28195 = "28195";
+  public static final String s28196 = "28196";
+  public static final String s28197 = "28197";
+  public static final String s28198 = "28198";
+  public static final String s28199 = "28199";
+  public static final String s28200 = "28200";
+  public static final String s28201 = "28201";
+  public static final String s28202 = "28202";
+  public static final String s28203 = "28203";
+  public static final String s28204 = "28204";
+  public static final String s28205 = "28205";
+  public static final String s28206 = "28206";
+  public static final String s28207 = "28207";
+  public static final String s28208 = "28208";
+  public static final String s28209 = "28209";
+  public static final String s28210 = "28210";
+  public static final String s28211 = "28211";
+  public static final String s28212 = "28212";
+  public static final String s28213 = "28213";
+  public static final String s28214 = "28214";
+  public static final String s28215 = "28215";
+  public static final String s28216 = "28216";
+  public static final String s28217 = "28217";
+  public static final String s28218 = "28218";
+  public static final String s28219 = "28219";
+  public static final String s28220 = "28220";
+  public static final String s28221 = "28221";
+  public static final String s28222 = "28222";
+  public static final String s28223 = "28223";
+  public static final String s28224 = "28224";
+  public static final String s28225 = "28225";
+  public static final String s28226 = "28226";
+  public static final String s28227 = "28227";
+  public static final String s28228 = "28228";
+  public static final String s28229 = "28229";
+  public static final String s28230 = "28230";
+  public static final String s28231 = "28231";
+  public static final String s28232 = "28232";
+  public static final String s28233 = "28233";
+  public static final String s28234 = "28234";
+  public static final String s28235 = "28235";
+  public static final String s28236 = "28236";
+  public static final String s28237 = "28237";
+  public static final String s28238 = "28238";
+  public static final String s28239 = "28239";
+  public static final String s28240 = "28240";
+  public static final String s28241 = "28241";
+  public static final String s28242 = "28242";
+  public static final String s28243 = "28243";
+  public static final String s28244 = "28244";
+  public static final String s28245 = "28245";
+  public static final String s28246 = "28246";
+  public static final String s28247 = "28247";
+  public static final String s28248 = "28248";
+  public static final String s28249 = "28249";
+  public static final String s28250 = "28250";
+  public static final String s28251 = "28251";
+  public static final String s28252 = "28252";
+  public static final String s28253 = "28253";
+  public static final String s28254 = "28254";
+  public static final String s28255 = "28255";
+  public static final String s28256 = "28256";
+  public static final String s28257 = "28257";
+  public static final String s28258 = "28258";
+  public static final String s28259 = "28259";
+  public static final String s28260 = "28260";
+  public static final String s28261 = "28261";
+  public static final String s28262 = "28262";
+  public static final String s28263 = "28263";
+  public static final String s28264 = "28264";
+  public static final String s28265 = "28265";
+  public static final String s28266 = "28266";
+  public static final String s28267 = "28267";
+  public static final String s28268 = "28268";
+  public static final String s28269 = "28269";
+  public static final String s28270 = "28270";
+  public static final String s28271 = "28271";
+  public static final String s28272 = "28272";
+  public static final String s28273 = "28273";
+  public static final String s28274 = "28274";
+  public static final String s28275 = "28275";
+  public static final String s28276 = "28276";
+  public static final String s28277 = "28277";
+  public static final String s28278 = "28278";
+  public static final String s28279 = "28279";
+  public static final String s28280 = "28280";
+  public static final String s28281 = "28281";
+  public static final String s28282 = "28282";
+  public static final String s28283 = "28283";
+  public static final String s28284 = "28284";
+  public static final String s28285 = "28285";
+  public static final String s28286 = "28286";
+  public static final String s28287 = "28287";
+  public static final String s28288 = "28288";
+  public static final String s28289 = "28289";
+  public static final String s28290 = "28290";
+  public static final String s28291 = "28291";
+  public static final String s28292 = "28292";
+  public static final String s28293 = "28293";
+  public static final String s28294 = "28294";
+  public static final String s28295 = "28295";
+  public static final String s28296 = "28296";
+  public static final String s28297 = "28297";
+  public static final String s28298 = "28298";
+  public static final String s28299 = "28299";
+  public static final String s28300 = "28300";
+  public static final String s28301 = "28301";
+  public static final String s28302 = "28302";
+  public static final String s28303 = "28303";
+  public static final String s28304 = "28304";
+  public static final String s28305 = "28305";
+  public static final String s28306 = "28306";
+  public static final String s28307 = "28307";
+  public static final String s28308 = "28308";
+  public static final String s28309 = "28309";
+  public static final String s28310 = "28310";
+  public static final String s28311 = "28311";
+  public static final String s28312 = "28312";
+  public static final String s28313 = "28313";
+  public static final String s28314 = "28314";
+  public static final String s28315 = "28315";
+  public static final String s28316 = "28316";
+  public static final String s28317 = "28317";
+  public static final String s28318 = "28318";
+  public static final String s28319 = "28319";
+  public static final String s28320 = "28320";
+  public static final String s28321 = "28321";
+  public static final String s28322 = "28322";
+  public static final String s28323 = "28323";
+  public static final String s28324 = "28324";
+  public static final String s28325 = "28325";
+  public static final String s28326 = "28326";
+  public static final String s28327 = "28327";
+  public static final String s28328 = "28328";
+  public static final String s28329 = "28329";
+  public static final String s28330 = "28330";
+  public static final String s28331 = "28331";
+  public static final String s28332 = "28332";
+  public static final String s28333 = "28333";
+  public static final String s28334 = "28334";
+  public static final String s28335 = "28335";
+  public static final String s28336 = "28336";
+  public static final String s28337 = "28337";
+  public static final String s28338 = "28338";
+  public static final String s28339 = "28339";
+  public static final String s28340 = "28340";
+  public static final String s28341 = "28341";
+  public static final String s28342 = "28342";
+  public static final String s28343 = "28343";
+  public static final String s28344 = "28344";
+  public static final String s28345 = "28345";
+  public static final String s28346 = "28346";
+  public static final String s28347 = "28347";
+  public static final String s28348 = "28348";
+  public static final String s28349 = "28349";
+  public static final String s28350 = "28350";
+  public static final String s28351 = "28351";
+  public static final String s28352 = "28352";
+  public static final String s28353 = "28353";
+  public static final String s28354 = "28354";
+  public static final String s28355 = "28355";
+  public static final String s28356 = "28356";
+  public static final String s28357 = "28357";
+  public static final String s28358 = "28358";
+  public static final String s28359 = "28359";
+  public static final String s28360 = "28360";
+  public static final String s28361 = "28361";
+  public static final String s28362 = "28362";
+  public static final String s28363 = "28363";
+  public static final String s28364 = "28364";
+  public static final String s28365 = "28365";
+  public static final String s28366 = "28366";
+  public static final String s28367 = "28367";
+  public static final String s28368 = "28368";
+  public static final String s28369 = "28369";
+  public static final String s28370 = "28370";
+  public static final String s28371 = "28371";
+  public static final String s28372 = "28372";
+  public static final String s28373 = "28373";
+  public static final String s28374 = "28374";
+  public static final String s28375 = "28375";
+  public static final String s28376 = "28376";
+  public static final String s28377 = "28377";
+  public static final String s28378 = "28378";
+  public static final String s28379 = "28379";
+  public static final String s28380 = "28380";
+  public static final String s28381 = "28381";
+  public static final String s28382 = "28382";
+  public static final String s28383 = "28383";
+  public static final String s28384 = "28384";
+  public static final String s28385 = "28385";
+  public static final String s28386 = "28386";
+  public static final String s28387 = "28387";
+  public static final String s28388 = "28388";
+  public static final String s28389 = "28389";
+  public static final String s28390 = "28390";
+  public static final String s28391 = "28391";
+  public static final String s28392 = "28392";
+  public static final String s28393 = "28393";
+  public static final String s28394 = "28394";
+  public static final String s28395 = "28395";
+  public static final String s28396 = "28396";
+  public static final String s28397 = "28397";
+  public static final String s28398 = "28398";
+  public static final String s28399 = "28399";
+  public static final String s28400 = "28400";
+  public static final String s28401 = "28401";
+  public static final String s28402 = "28402";
+  public static final String s28403 = "28403";
+  public static final String s28404 = "28404";
+  public static final String s28405 = "28405";
+  public static final String s28406 = "28406";
+  public static final String s28407 = "28407";
+  public static final String s28408 = "28408";
+  public static final String s28409 = "28409";
+  public static final String s28410 = "28410";
+  public static final String s28411 = "28411";
+  public static final String s28412 = "28412";
+  public static final String s28413 = "28413";
+  public static final String s28414 = "28414";
+  public static final String s28415 = "28415";
+  public static final String s28416 = "28416";
+  public static final String s28417 = "28417";
+  public static final String s28418 = "28418";
+  public static final String s28419 = "28419";
+  public static final String s28420 = "28420";
+  public static final String s28421 = "28421";
+  public static final String s28422 = "28422";
+  public static final String s28423 = "28423";
+  public static final String s28424 = "28424";
+  public static final String s28425 = "28425";
+  public static final String s28426 = "28426";
+  public static final String s28427 = "28427";
+  public static final String s28428 = "28428";
+  public static final String s28429 = "28429";
+  public static final String s28430 = "28430";
+  public static final String s28431 = "28431";
+  public static final String s28432 = "28432";
+  public static final String s28433 = "28433";
+  public static final String s28434 = "28434";
+  public static final String s28435 = "28435";
+  public static final String s28436 = "28436";
+  public static final String s28437 = "28437";
+  public static final String s28438 = "28438";
+  public static final String s28439 = "28439";
+  public static final String s28440 = "28440";
+  public static final String s28441 = "28441";
+  public static final String s28442 = "28442";
+  public static final String s28443 = "28443";
+  public static final String s28444 = "28444";
+  public static final String s28445 = "28445";
+  public static final String s28446 = "28446";
+  public static final String s28447 = "28447";
+  public static final String s28448 = "28448";
+  public static final String s28449 = "28449";
+  public static final String s28450 = "28450";
+  public static final String s28451 = "28451";
+  public static final String s28452 = "28452";
+  public static final String s28453 = "28453";
+  public static final String s28454 = "28454";
+  public static final String s28455 = "28455";
+  public static final String s28456 = "28456";
+  public static final String s28457 = "28457";
+  public static final String s28458 = "28458";
+  public static final String s28459 = "28459";
+  public static final String s28460 = "28460";
+  public static final String s28461 = "28461";
+  public static final String s28462 = "28462";
+  public static final String s28463 = "28463";
+  public static final String s28464 = "28464";
+  public static final String s28465 = "28465";
+  public static final String s28466 = "28466";
+  public static final String s28467 = "28467";
+  public static final String s28468 = "28468";
+  public static final String s28469 = "28469";
+  public static final String s28470 = "28470";
+  public static final String s28471 = "28471";
+  public static final String s28472 = "28472";
+  public static final String s28473 = "28473";
+  public static final String s28474 = "28474";
+  public static final String s28475 = "28475";
+  public static final String s28476 = "28476";
+  public static final String s28477 = "28477";
+  public static final String s28478 = "28478";
+  public static final String s28479 = "28479";
+  public static final String s28480 = "28480";
+  public static final String s28481 = "28481";
+  public static final String s28482 = "28482";
+  public static final String s28483 = "28483";
+  public static final String s28484 = "28484";
+  public static final String s28485 = "28485";
+  public static final String s28486 = "28486";
+  public static final String s28487 = "28487";
+  public static final String s28488 = "28488";
+  public static final String s28489 = "28489";
+  public static final String s28490 = "28490";
+  public static final String s28491 = "28491";
+  public static final String s28492 = "28492";
+  public static final String s28493 = "28493";
+  public static final String s28494 = "28494";
+  public static final String s28495 = "28495";
+  public static final String s28496 = "28496";
+  public static final String s28497 = "28497";
+  public static final String s28498 = "28498";
+  public static final String s28499 = "28499";
+  public static final String s28500 = "28500";
+  public static final String s28501 = "28501";
+  public static final String s28502 = "28502";
+  public static final String s28503 = "28503";
+  public static final String s28504 = "28504";
+  public static final String s28505 = "28505";
+  public static final String s28506 = "28506";
+  public static final String s28507 = "28507";
+  public static final String s28508 = "28508";
+  public static final String s28509 = "28509";
+  public static final String s28510 = "28510";
+  public static final String s28511 = "28511";
+  public static final String s28512 = "28512";
+  public static final String s28513 = "28513";
+  public static final String s28514 = "28514";
+  public static final String s28515 = "28515";
+  public static final String s28516 = "28516";
+  public static final String s28517 = "28517";
+  public static final String s28518 = "28518";
+  public static final String s28519 = "28519";
+  public static final String s28520 = "28520";
+  public static final String s28521 = "28521";
+  public static final String s28522 = "28522";
+  public static final String s28523 = "28523";
+  public static final String s28524 = "28524";
+  public static final String s28525 = "28525";
+  public static final String s28526 = "28526";
+  public static final String s28527 = "28527";
+  public static final String s28528 = "28528";
+  public static final String s28529 = "28529";
+  public static final String s28530 = "28530";
+  public static final String s28531 = "28531";
+  public static final String s28532 = "28532";
+  public static final String s28533 = "28533";
+  public static final String s28534 = "28534";
+  public static final String s28535 = "28535";
+  public static final String s28536 = "28536";
+  public static final String s28537 = "28537";
+  public static final String s28538 = "28538";
+  public static final String s28539 = "28539";
+  public static final String s28540 = "28540";
+  public static final String s28541 = "28541";
+  public static final String s28542 = "28542";
+  public static final String s28543 = "28543";
+  public static final String s28544 = "28544";
+  public static final String s28545 = "28545";
+  public static final String s28546 = "28546";
+  public static final String s28547 = "28547";
+  public static final String s28548 = "28548";
+  public static final String s28549 = "28549";
+  public static final String s28550 = "28550";
+  public static final String s28551 = "28551";
+  public static final String s28552 = "28552";
+  public static final String s28553 = "28553";
+  public static final String s28554 = "28554";
+  public static final String s28555 = "28555";
+  public static final String s28556 = "28556";
+  public static final String s28557 = "28557";
+  public static final String s28558 = "28558";
+  public static final String s28559 = "28559";
+  public static final String s28560 = "28560";
+  public static final String s28561 = "28561";
+  public static final String s28562 = "28562";
+  public static final String s28563 = "28563";
+  public static final String s28564 = "28564";
+  public static final String s28565 = "28565";
+  public static final String s28566 = "28566";
+  public static final String s28567 = "28567";
+  public static final String s28568 = "28568";
+  public static final String s28569 = "28569";
+  public static final String s28570 = "28570";
+  public static final String s28571 = "28571";
+  public static final String s28572 = "28572";
+  public static final String s28573 = "28573";
+  public static final String s28574 = "28574";
+  public static final String s28575 = "28575";
+  public static final String s28576 = "28576";
+  public static final String s28577 = "28577";
+  public static final String s28578 = "28578";
+  public static final String s28579 = "28579";
+  public static final String s28580 = "28580";
+  public static final String s28581 = "28581";
+  public static final String s28582 = "28582";
+  public static final String s28583 = "28583";
+  public static final String s28584 = "28584";
+  public static final String s28585 = "28585";
+  public static final String s28586 = "28586";
+  public static final String s28587 = "28587";
+  public static final String s28588 = "28588";
+  public static final String s28589 = "28589";
+  public static final String s28590 = "28590";
+  public static final String s28591 = "28591";
+  public static final String s28592 = "28592";
+  public static final String s28593 = "28593";
+  public static final String s28594 = "28594";
+  public static final String s28595 = "28595";
+  public static final String s28596 = "28596";
+  public static final String s28597 = "28597";
+  public static final String s28598 = "28598";
+  public static final String s28599 = "28599";
+  public static final String s28600 = "28600";
+  public static final String s28601 = "28601";
+  public static final String s28602 = "28602";
+  public static final String s28603 = "28603";
+  public static final String s28604 = "28604";
+  public static final String s28605 = "28605";
+  public static final String s28606 = "28606";
+  public static final String s28607 = "28607";
+  public static final String s28608 = "28608";
+  public static final String s28609 = "28609";
+  public static final String s28610 = "28610";
+  public static final String s28611 = "28611";
+  public static final String s28612 = "28612";
+  public static final String s28613 = "28613";
+  public static final String s28614 = "28614";
+  public static final String s28615 = "28615";
+  public static final String s28616 = "28616";
+  public static final String s28617 = "28617";
+  public static final String s28618 = "28618";
+  public static final String s28619 = "28619";
+  public static final String s28620 = "28620";
+  public static final String s28621 = "28621";
+  public static final String s28622 = "28622";
+  public static final String s28623 = "28623";
+  public static final String s28624 = "28624";
+  public static final String s28625 = "28625";
+  public static final String s28626 = "28626";
+  public static final String s28627 = "28627";
+  public static final String s28628 = "28628";
+  public static final String s28629 = "28629";
+  public static final String s28630 = "28630";
+  public static final String s28631 = "28631";
+  public static final String s28632 = "28632";
+  public static final String s28633 = "28633";
+  public static final String s28634 = "28634";
+  public static final String s28635 = "28635";
+  public static final String s28636 = "28636";
+  public static final String s28637 = "28637";
+  public static final String s28638 = "28638";
+  public static final String s28639 = "28639";
+  public static final String s28640 = "28640";
+  public static final String s28641 = "28641";
+  public static final String s28642 = "28642";
+  public static final String s28643 = "28643";
+  public static final String s28644 = "28644";
+  public static final String s28645 = "28645";
+  public static final String s28646 = "28646";
+  public static final String s28647 = "28647";
+  public static final String s28648 = "28648";
+  public static final String s28649 = "28649";
+  public static final String s28650 = "28650";
+  public static final String s28651 = "28651";
+  public static final String s28652 = "28652";
+  public static final String s28653 = "28653";
+  public static final String s28654 = "28654";
+  public static final String s28655 = "28655";
+  public static final String s28656 = "28656";
+  public static final String s28657 = "28657";
+  public static final String s28658 = "28658";
+  public static final String s28659 = "28659";
+  public static final String s28660 = "28660";
+  public static final String s28661 = "28661";
+  public static final String s28662 = "28662";
+  public static final String s28663 = "28663";
+  public static final String s28664 = "28664";
+  public static final String s28665 = "28665";
+  public static final String s28666 = "28666";
+  public static final String s28667 = "28667";
+  public static final String s28668 = "28668";
+  public static final String s28669 = "28669";
+  public static final String s28670 = "28670";
+  public static final String s28671 = "28671";
+  public static final String s28672 = "28672";
+  public static final String s28673 = "28673";
+  public static final String s28674 = "28674";
+  public static final String s28675 = "28675";
+  public static final String s28676 = "28676";
+  public static final String s28677 = "28677";
+  public static final String s28678 = "28678";
+  public static final String s28679 = "28679";
+  public static final String s28680 = "28680";
+  public static final String s28681 = "28681";
+  public static final String s28682 = "28682";
+  public static final String s28683 = "28683";
+  public static final String s28684 = "28684";
+  public static final String s28685 = "28685";
+  public static final String s28686 = "28686";
+  public static final String s28687 = "28687";
+  public static final String s28688 = "28688";
+  public static final String s28689 = "28689";
+  public static final String s28690 = "28690";
+  public static final String s28691 = "28691";
+  public static final String s28692 = "28692";
+  public static final String s28693 = "28693";
+  public static final String s28694 = "28694";
+  public static final String s28695 = "28695";
+  public static final String s28696 = "28696";
+  public static final String s28697 = "28697";
+  public static final String s28698 = "28698";
+  public static final String s28699 = "28699";
+  public static final String s28700 = "28700";
+  public static final String s28701 = "28701";
+  public static final String s28702 = "28702";
+  public static final String s28703 = "28703";
+  public static final String s28704 = "28704";
+  public static final String s28705 = "28705";
+  public static final String s28706 = "28706";
+  public static final String s28707 = "28707";
+  public static final String s28708 = "28708";
+  public static final String s28709 = "28709";
+  public static final String s28710 = "28710";
+  public static final String s28711 = "28711";
+  public static final String s28712 = "28712";
+  public static final String s28713 = "28713";
+  public static final String s28714 = "28714";
+  public static final String s28715 = "28715";
+  public static final String s28716 = "28716";
+  public static final String s28717 = "28717";
+  public static final String s28718 = "28718";
+  public static final String s28719 = "28719";
+  public static final String s28720 = "28720";
+  public static final String s28721 = "28721";
+  public static final String s28722 = "28722";
+  public static final String s28723 = "28723";
+  public static final String s28724 = "28724";
+  public static final String s28725 = "28725";
+  public static final String s28726 = "28726";
+  public static final String s28727 = "28727";
+  public static final String s28728 = "28728";
+  public static final String s28729 = "28729";
+  public static final String s28730 = "28730";
+  public static final String s28731 = "28731";
+  public static final String s28732 = "28732";
+  public static final String s28733 = "28733";
+  public static final String s28734 = "28734";
+  public static final String s28735 = "28735";
+  public static final String s28736 = "28736";
+  public static final String s28737 = "28737";
+  public static final String s28738 = "28738";
+  public static final String s28739 = "28739";
+  public static final String s28740 = "28740";
+  public static final String s28741 = "28741";
+  public static final String s28742 = "28742";
+  public static final String s28743 = "28743";
+  public static final String s28744 = "28744";
+  public static final String s28745 = "28745";
+  public static final String s28746 = "28746";
+  public static final String s28747 = "28747";
+  public static final String s28748 = "28748";
+  public static final String s28749 = "28749";
+  public static final String s28750 = "28750";
+  public static final String s28751 = "28751";
+  public static final String s28752 = "28752";
+  public static final String s28753 = "28753";
+  public static final String s28754 = "28754";
+  public static final String s28755 = "28755";
+  public static final String s28756 = "28756";
+  public static final String s28757 = "28757";
+  public static final String s28758 = "28758";
+  public static final String s28759 = "28759";
+  public static final String s28760 = "28760";
+  public static final String s28761 = "28761";
+  public static final String s28762 = "28762";
+  public static final String s28763 = "28763";
+  public static final String s28764 = "28764";
+  public static final String s28765 = "28765";
+  public static final String s28766 = "28766";
+  public static final String s28767 = "28767";
+  public static final String s28768 = "28768";
+  public static final String s28769 = "28769";
+  public static final String s28770 = "28770";
+  public static final String s28771 = "28771";
+  public static final String s28772 = "28772";
+  public static final String s28773 = "28773";
+  public static final String s28774 = "28774";
+  public static final String s28775 = "28775";
+  public static final String s28776 = "28776";
+  public static final String s28777 = "28777";
+  public static final String s28778 = "28778";
+  public static final String s28779 = "28779";
+  public static final String s28780 = "28780";
+  public static final String s28781 = "28781";
+  public static final String s28782 = "28782";
+  public static final String s28783 = "28783";
+  public static final String s28784 = "28784";
+  public static final String s28785 = "28785";
+  public static final String s28786 = "28786";
+  public static final String s28787 = "28787";
+  public static final String s28788 = "28788";
+  public static final String s28789 = "28789";
+  public static final String s28790 = "28790";
+  public static final String s28791 = "28791";
+  public static final String s28792 = "28792";
+  public static final String s28793 = "28793";
+  public static final String s28794 = "28794";
+  public static final String s28795 = "28795";
+  public static final String s28796 = "28796";
+  public static final String s28797 = "28797";
+  public static final String s28798 = "28798";
+  public static final String s28799 = "28799";
+  public static final String s28800 = "28800";
+  public static final String s28801 = "28801";
+  public static final String s28802 = "28802";
+  public static final String s28803 = "28803";
+  public static final String s28804 = "28804";
+  public static final String s28805 = "28805";
+  public static final String s28806 = "28806";
+  public static final String s28807 = "28807";
+  public static final String s28808 = "28808";
+  public static final String s28809 = "28809";
+  public static final String s28810 = "28810";
+  public static final String s28811 = "28811";
+  public static final String s28812 = "28812";
+  public static final String s28813 = "28813";
+  public static final String s28814 = "28814";
+  public static final String s28815 = "28815";
+  public static final String s28816 = "28816";
+  public static final String s28817 = "28817";
+  public static final String s28818 = "28818";
+  public static final String s28819 = "28819";
+  public static final String s28820 = "28820";
+  public static final String s28821 = "28821";
+  public static final String s28822 = "28822";
+  public static final String s28823 = "28823";
+  public static final String s28824 = "28824";
+  public static final String s28825 = "28825";
+  public static final String s28826 = "28826";
+  public static final String s28827 = "28827";
+  public static final String s28828 = "28828";
+  public static final String s28829 = "28829";
+  public static final String s28830 = "28830";
+  public static final String s28831 = "28831";
+  public static final String s28832 = "28832";
+  public static final String s28833 = "28833";
+  public static final String s28834 = "28834";
+  public static final String s28835 = "28835";
+  public static final String s28836 = "28836";
+  public static final String s28837 = "28837";
+  public static final String s28838 = "28838";
+  public static final String s28839 = "28839";
+  public static final String s28840 = "28840";
+  public static final String s28841 = "28841";
+  public static final String s28842 = "28842";
+  public static final String s28843 = "28843";
+  public static final String s28844 = "28844";
+  public static final String s28845 = "28845";
+  public static final String s28846 = "28846";
+  public static final String s28847 = "28847";
+  public static final String s28848 = "28848";
+  public static final String s28849 = "28849";
+  public static final String s28850 = "28850";
+  public static final String s28851 = "28851";
+  public static final String s28852 = "28852";
+  public static final String s28853 = "28853";
+  public static final String s28854 = "28854";
+  public static final String s28855 = "28855";
+  public static final String s28856 = "28856";
+  public static final String s28857 = "28857";
+  public static final String s28858 = "28858";
+  public static final String s28859 = "28859";
+  public static final String s28860 = "28860";
+  public static final String s28861 = "28861";
+  public static final String s28862 = "28862";
+  public static final String s28863 = "28863";
+  public static final String s28864 = "28864";
+  public static final String s28865 = "28865";
+  public static final String s28866 = "28866";
+  public static final String s28867 = "28867";
+  public static final String s28868 = "28868";
+  public static final String s28869 = "28869";
+  public static final String s28870 = "28870";
+  public static final String s28871 = "28871";
+  public static final String s28872 = "28872";
+  public static final String s28873 = "28873";
+  public static final String s28874 = "28874";
+  public static final String s28875 = "28875";
+  public static final String s28876 = "28876";
+  public static final String s28877 = "28877";
+  public static final String s28878 = "28878";
+  public static final String s28879 = "28879";
+  public static final String s28880 = "28880";
+  public static final String s28881 = "28881";
+  public static final String s28882 = "28882";
+  public static final String s28883 = "28883";
+  public static final String s28884 = "28884";
+  public static final String s28885 = "28885";
+  public static final String s28886 = "28886";
+  public static final String s28887 = "28887";
+  public static final String s28888 = "28888";
+  public static final String s28889 = "28889";
+  public static final String s28890 = "28890";
+  public static final String s28891 = "28891";
+  public static final String s28892 = "28892";
+  public static final String s28893 = "28893";
+  public static final String s28894 = "28894";
+  public static final String s28895 = "28895";
+  public static final String s28896 = "28896";
+  public static final String s28897 = "28897";
+  public static final String s28898 = "28898";
+  public static final String s28899 = "28899";
+  public static final String s28900 = "28900";
+  public static final String s28901 = "28901";
+  public static final String s28902 = "28902";
+  public static final String s28903 = "28903";
+  public static final String s28904 = "28904";
+  public static final String s28905 = "28905";
+  public static final String s28906 = "28906";
+  public static final String s28907 = "28907";
+  public static final String s28908 = "28908";
+  public static final String s28909 = "28909";
+  public static final String s28910 = "28910";
+  public static final String s28911 = "28911";
+  public static final String s28912 = "28912";
+  public static final String s28913 = "28913";
+  public static final String s28914 = "28914";
+  public static final String s28915 = "28915";
+  public static final String s28916 = "28916";
+  public static final String s28917 = "28917";
+  public static final String s28918 = "28918";
+  public static final String s28919 = "28919";
+  public static final String s28920 = "28920";
+  public static final String s28921 = "28921";
+  public static final String s28922 = "28922";
+  public static final String s28923 = "28923";
+  public static final String s28924 = "28924";
+  public static final String s28925 = "28925";
+  public static final String s28926 = "28926";
+  public static final String s28927 = "28927";
+  public static final String s28928 = "28928";
+  public static final String s28929 = "28929";
+  public static final String s28930 = "28930";
+  public static final String s28931 = "28931";
+  public static final String s28932 = "28932";
+  public static final String s28933 = "28933";
+  public static final String s28934 = "28934";
+  public static final String s28935 = "28935";
+  public static final String s28936 = "28936";
+  public static final String s28937 = "28937";
+  public static final String s28938 = "28938";
+  public static final String s28939 = "28939";
+  public static final String s28940 = "28940";
+  public static final String s28941 = "28941";
+  public static final String s28942 = "28942";
+  public static final String s28943 = "28943";
+  public static final String s28944 = "28944";
+  public static final String s28945 = "28945";
+  public static final String s28946 = "28946";
+  public static final String s28947 = "28947";
+  public static final String s28948 = "28948";
+  public static final String s28949 = "28949";
+  public static final String s28950 = "28950";
+  public static final String s28951 = "28951";
+  public static final String s28952 = "28952";
+  public static final String s28953 = "28953";
+  public static final String s28954 = "28954";
+  public static final String s28955 = "28955";
+  public static final String s28956 = "28956";
+  public static final String s28957 = "28957";
+  public static final String s28958 = "28958";
+  public static final String s28959 = "28959";
+  public static final String s28960 = "28960";
+  public static final String s28961 = "28961";
+  public static final String s28962 = "28962";
+  public static final String s28963 = "28963";
+  public static final String s28964 = "28964";
+  public static final String s28965 = "28965";
+  public static final String s28966 = "28966";
+  public static final String s28967 = "28967";
+  public static final String s28968 = "28968";
+  public static final String s28969 = "28969";
+  public static final String s28970 = "28970";
+  public static final String s28971 = "28971";
+  public static final String s28972 = "28972";
+  public static final String s28973 = "28973";
+  public static final String s28974 = "28974";
+  public static final String s28975 = "28975";
+  public static final String s28976 = "28976";
+  public static final String s28977 = "28977";
+  public static final String s28978 = "28978";
+  public static final String s28979 = "28979";
+  public static final String s28980 = "28980";
+  public static final String s28981 = "28981";
+  public static final String s28982 = "28982";
+  public static final String s28983 = "28983";
+  public static final String s28984 = "28984";
+  public static final String s28985 = "28985";
+  public static final String s28986 = "28986";
+  public static final String s28987 = "28987";
+  public static final String s28988 = "28988";
+  public static final String s28989 = "28989";
+  public static final String s28990 = "28990";
+  public static final String s28991 = "28991";
+  public static final String s28992 = "28992";
+  public static final String s28993 = "28993";
+  public static final String s28994 = "28994";
+  public static final String s28995 = "28995";
+  public static final String s28996 = "28996";
+  public static final String s28997 = "28997";
+  public static final String s28998 = "28998";
+  public static final String s28999 = "28999";
+  public static final String s29000 = "29000";
+  public static final String s29001 = "29001";
+  public static final String s29002 = "29002";
+  public static final String s29003 = "29003";
+  public static final String s29004 = "29004";
+  public static final String s29005 = "29005";
+  public static final String s29006 = "29006";
+  public static final String s29007 = "29007";
+  public static final String s29008 = "29008";
+  public static final String s29009 = "29009";
+  public static final String s29010 = "29010";
+  public static final String s29011 = "29011";
+  public static final String s29012 = "29012";
+  public static final String s29013 = "29013";
+  public static final String s29014 = "29014";
+  public static final String s29015 = "29015";
+  public static final String s29016 = "29016";
+  public static final String s29017 = "29017";
+  public static final String s29018 = "29018";
+  public static final String s29019 = "29019";
+  public static final String s29020 = "29020";
+  public static final String s29021 = "29021";
+  public static final String s29022 = "29022";
+  public static final String s29023 = "29023";
+  public static final String s29024 = "29024";
+  public static final String s29025 = "29025";
+  public static final String s29026 = "29026";
+  public static final String s29027 = "29027";
+  public static final String s29028 = "29028";
+  public static final String s29029 = "29029";
+  public static final String s29030 = "29030";
+  public static final String s29031 = "29031";
+  public static final String s29032 = "29032";
+  public static final String s29033 = "29033";
+  public static final String s29034 = "29034";
+  public static final String s29035 = "29035";
+  public static final String s29036 = "29036";
+  public static final String s29037 = "29037";
+  public static final String s29038 = "29038";
+  public static final String s29039 = "29039";
+  public static final String s29040 = "29040";
+  public static final String s29041 = "29041";
+  public static final String s29042 = "29042";
+  public static final String s29043 = "29043";
+  public static final String s29044 = "29044";
+  public static final String s29045 = "29045";
+  public static final String s29046 = "29046";
+  public static final String s29047 = "29047";
+  public static final String s29048 = "29048";
+  public static final String s29049 = "29049";
+  public static final String s29050 = "29050";
+  public static final String s29051 = "29051";
+  public static final String s29052 = "29052";
+  public static final String s29053 = "29053";
+  public static final String s29054 = "29054";
+  public static final String s29055 = "29055";
+  public static final String s29056 = "29056";
+  public static final String s29057 = "29057";
+  public static final String s29058 = "29058";
+  public static final String s29059 = "29059";
+  public static final String s29060 = "29060";
+  public static final String s29061 = "29061";
+  public static final String s29062 = "29062";
+  public static final String s29063 = "29063";
+  public static final String s29064 = "29064";
+  public static final String s29065 = "29065";
+  public static final String s29066 = "29066";
+  public static final String s29067 = "29067";
+  public static final String s29068 = "29068";
+  public static final String s29069 = "29069";
+  public static final String s29070 = "29070";
+  public static final String s29071 = "29071";
+  public static final String s29072 = "29072";
+  public static final String s29073 = "29073";
+  public static final String s29074 = "29074";
+  public static final String s29075 = "29075";
+  public static final String s29076 = "29076";
+  public static final String s29077 = "29077";
+  public static final String s29078 = "29078";
+  public static final String s29079 = "29079";
+  public static final String s29080 = "29080";
+  public static final String s29081 = "29081";
+  public static final String s29082 = "29082";
+  public static final String s29083 = "29083";
+  public static final String s29084 = "29084";
+  public static final String s29085 = "29085";
+  public static final String s29086 = "29086";
+  public static final String s29087 = "29087";
+  public static final String s29088 = "29088";
+  public static final String s29089 = "29089";
+  public static final String s29090 = "29090";
+  public static final String s29091 = "29091";
+  public static final String s29092 = "29092";
+  public static final String s29093 = "29093";
+  public static final String s29094 = "29094";
+  public static final String s29095 = "29095";
+  public static final String s29096 = "29096";
+  public static final String s29097 = "29097";
+  public static final String s29098 = "29098";
+  public static final String s29099 = "29099";
+  public static final String s29100 = "29100";
+  public static final String s29101 = "29101";
+  public static final String s29102 = "29102";
+  public static final String s29103 = "29103";
+  public static final String s29104 = "29104";
+  public static final String s29105 = "29105";
+  public static final String s29106 = "29106";
+  public static final String s29107 = "29107";
+  public static final String s29108 = "29108";
+  public static final String s29109 = "29109";
+  public static final String s29110 = "29110";
+  public static final String s29111 = "29111";
+  public static final String s29112 = "29112";
+  public static final String s29113 = "29113";
+  public static final String s29114 = "29114";
+  public static final String s29115 = "29115";
+  public static final String s29116 = "29116";
+  public static final String s29117 = "29117";
+  public static final String s29118 = "29118";
+  public static final String s29119 = "29119";
+  public static final String s29120 = "29120";
+  public static final String s29121 = "29121";
+  public static final String s29122 = "29122";
+  public static final String s29123 = "29123";
+  public static final String s29124 = "29124";
+  public static final String s29125 = "29125";
+  public static final String s29126 = "29126";
+  public static final String s29127 = "29127";
+  public static final String s29128 = "29128";
+  public static final String s29129 = "29129";
+  public static final String s29130 = "29130";
+  public static final String s29131 = "29131";
+  public static final String s29132 = "29132";
+  public static final String s29133 = "29133";
+  public static final String s29134 = "29134";
+  public static final String s29135 = "29135";
+  public static final String s29136 = "29136";
+  public static final String s29137 = "29137";
+  public static final String s29138 = "29138";
+  public static final String s29139 = "29139";
+  public static final String s29140 = "29140";
+  public static final String s29141 = "29141";
+  public static final String s29142 = "29142";
+  public static final String s29143 = "29143";
+  public static final String s29144 = "29144";
+  public static final String s29145 = "29145";
+  public static final String s29146 = "29146";
+  public static final String s29147 = "29147";
+  public static final String s29148 = "29148";
+  public static final String s29149 = "29149";
+  public static final String s29150 = "29150";
+  public static final String s29151 = "29151";
+  public static final String s29152 = "29152";
+  public static final String s29153 = "29153";
+  public static final String s29154 = "29154";
+  public static final String s29155 = "29155";
+  public static final String s29156 = "29156";
+  public static final String s29157 = "29157";
+  public static final String s29158 = "29158";
+  public static final String s29159 = "29159";
+  public static final String s29160 = "29160";
+  public static final String s29161 = "29161";
+  public static final String s29162 = "29162";
+  public static final String s29163 = "29163";
+  public static final String s29164 = "29164";
+  public static final String s29165 = "29165";
+  public static final String s29166 = "29166";
+  public static final String s29167 = "29167";
+  public static final String s29168 = "29168";
+  public static final String s29169 = "29169";
+  public static final String s29170 = "29170";
+  public static final String s29171 = "29171";
+  public static final String s29172 = "29172";
+  public static final String s29173 = "29173";
+  public static final String s29174 = "29174";
+  public static final String s29175 = "29175";
+  public static final String s29176 = "29176";
+  public static final String s29177 = "29177";
+  public static final String s29178 = "29178";
+  public static final String s29179 = "29179";
+  public static final String s29180 = "29180";
+  public static final String s29181 = "29181";
+  public static final String s29182 = "29182";
+  public static final String s29183 = "29183";
+  public static final String s29184 = "29184";
+  public static final String s29185 = "29185";
+  public static final String s29186 = "29186";
+  public static final String s29187 = "29187";
+  public static final String s29188 = "29188";
+  public static final String s29189 = "29189";
+  public static final String s29190 = "29190";
+  public static final String s29191 = "29191";
+  public static final String s29192 = "29192";
+  public static final String s29193 = "29193";
+  public static final String s29194 = "29194";
+  public static final String s29195 = "29195";
+  public static final String s29196 = "29196";
+  public static final String s29197 = "29197";
+  public static final String s29198 = "29198";
+  public static final String s29199 = "29199";
+  public static final String s29200 = "29200";
+  public static final String s29201 = "29201";
+  public static final String s29202 = "29202";
+  public static final String s29203 = "29203";
+  public static final String s29204 = "29204";
+  public static final String s29205 = "29205";
+  public static final String s29206 = "29206";
+  public static final String s29207 = "29207";
+  public static final String s29208 = "29208";
+  public static final String s29209 = "29209";
+  public static final String s29210 = "29210";
+  public static final String s29211 = "29211";
+  public static final String s29212 = "29212";
+  public static final String s29213 = "29213";
+  public static final String s29214 = "29214";
+  public static final String s29215 = "29215";
+  public static final String s29216 = "29216";
+  public static final String s29217 = "29217";
+  public static final String s29218 = "29218";
+  public static final String s29219 = "29219";
+  public static final String s29220 = "29220";
+  public static final String s29221 = "29221";
+  public static final String s29222 = "29222";
+  public static final String s29223 = "29223";
+  public static final String s29224 = "29224";
+  public static final String s29225 = "29225";
+  public static final String s29226 = "29226";
+  public static final String s29227 = "29227";
+  public static final String s29228 = "29228";
+  public static final String s29229 = "29229";
+  public static final String s29230 = "29230";
+  public static final String s29231 = "29231";
+  public static final String s29232 = "29232";
+  public static final String s29233 = "29233";
+  public static final String s29234 = "29234";
+  public static final String s29235 = "29235";
+  public static final String s29236 = "29236";
+  public static final String s29237 = "29237";
+  public static final String s29238 = "29238";
+  public static final String s29239 = "29239";
+  public static final String s29240 = "29240";
+  public static final String s29241 = "29241";
+  public static final String s29242 = "29242";
+  public static final String s29243 = "29243";
+  public static final String s29244 = "29244";
+  public static final String s29245 = "29245";
+  public static final String s29246 = "29246";
+  public static final String s29247 = "29247";
+  public static final String s29248 = "29248";
+  public static final String s29249 = "29249";
+  public static final String s29250 = "29250";
+  public static final String s29251 = "29251";
+  public static final String s29252 = "29252";
+  public static final String s29253 = "29253";
+  public static final String s29254 = "29254";
+  public static final String s29255 = "29255";
+  public static final String s29256 = "29256";
+  public static final String s29257 = "29257";
+  public static final String s29258 = "29258";
+  public static final String s29259 = "29259";
+  public static final String s29260 = "29260";
+  public static final String s29261 = "29261";
+  public static final String s29262 = "29262";
+  public static final String s29263 = "29263";
+  public static final String s29264 = "29264";
+  public static final String s29265 = "29265";
+  public static final String s29266 = "29266";
+  public static final String s29267 = "29267";
+  public static final String s29268 = "29268";
+  public static final String s29269 = "29269";
+  public static final String s29270 = "29270";
+  public static final String s29271 = "29271";
+  public static final String s29272 = "29272";
+  public static final String s29273 = "29273";
+  public static final String s29274 = "29274";
+  public static final String s29275 = "29275";
+  public static final String s29276 = "29276";
+  public static final String s29277 = "29277";
+  public static final String s29278 = "29278";
+  public static final String s29279 = "29279";
+  public static final String s29280 = "29280";
+  public static final String s29281 = "29281";
+  public static final String s29282 = "29282";
+  public static final String s29283 = "29283";
+  public static final String s29284 = "29284";
+  public static final String s29285 = "29285";
+  public static final String s29286 = "29286";
+  public static final String s29287 = "29287";
+  public static final String s29288 = "29288";
+  public static final String s29289 = "29289";
+  public static final String s29290 = "29290";
+  public static final String s29291 = "29291";
+  public static final String s29292 = "29292";
+  public static final String s29293 = "29293";
+  public static final String s29294 = "29294";
+  public static final String s29295 = "29295";
+  public static final String s29296 = "29296";
+  public static final String s29297 = "29297";
+  public static final String s29298 = "29298";
+  public static final String s29299 = "29299";
+  public static final String s29300 = "29300";
+  public static final String s29301 = "29301";
+  public static final String s29302 = "29302";
+  public static final String s29303 = "29303";
+  public static final String s29304 = "29304";
+  public static final String s29305 = "29305";
+  public static final String s29306 = "29306";
+  public static final String s29307 = "29307";
+  public static final String s29308 = "29308";
+  public static final String s29309 = "29309";
+  public static final String s29310 = "29310";
+  public static final String s29311 = "29311";
+  public static final String s29312 = "29312";
+  public static final String s29313 = "29313";
+  public static final String s29314 = "29314";
+  public static final String s29315 = "29315";
+  public static final String s29316 = "29316";
+  public static final String s29317 = "29317";
+  public static final String s29318 = "29318";
+  public static final String s29319 = "29319";
+  public static final String s29320 = "29320";
+  public static final String s29321 = "29321";
+  public static final String s29322 = "29322";
+  public static final String s29323 = "29323";
+  public static final String s29324 = "29324";
+  public static final String s29325 = "29325";
+  public static final String s29326 = "29326";
+  public static final String s29327 = "29327";
+  public static final String s29328 = "29328";
+  public static final String s29329 = "29329";
+  public static final String s29330 = "29330";
+  public static final String s29331 = "29331";
+  public static final String s29332 = "29332";
+  public static final String s29333 = "29333";
+  public static final String s29334 = "29334";
+  public static final String s29335 = "29335";
+  public static final String s29336 = "29336";
+  public static final String s29337 = "29337";
+  public static final String s29338 = "29338";
+  public static final String s29339 = "29339";
+  public static final String s29340 = "29340";
+  public static final String s29341 = "29341";
+  public static final String s29342 = "29342";
+  public static final String s29343 = "29343";
+  public static final String s29344 = "29344";
+  public static final String s29345 = "29345";
+  public static final String s29346 = "29346";
+  public static final String s29347 = "29347";
+  public static final String s29348 = "29348";
+  public static final String s29349 = "29349";
+  public static final String s29350 = "29350";
+  public static final String s29351 = "29351";
+  public static final String s29352 = "29352";
+  public static final String s29353 = "29353";
+  public static final String s29354 = "29354";
+  public static final String s29355 = "29355";
+  public static final String s29356 = "29356";
+  public static final String s29357 = "29357";
+  public static final String s29358 = "29358";
+  public static final String s29359 = "29359";
+  public static final String s29360 = "29360";
+  public static final String s29361 = "29361";
+  public static final String s29362 = "29362";
+  public static final String s29363 = "29363";
+  public static final String s29364 = "29364";
+  public static final String s29365 = "29365";
+  public static final String s29366 = "29366";
+  public static final String s29367 = "29367";
+  public static final String s29368 = "29368";
+  public static final String s29369 = "29369";
+  public static final String s29370 = "29370";
+  public static final String s29371 = "29371";
+  public static final String s29372 = "29372";
+  public static final String s29373 = "29373";
+  public static final String s29374 = "29374";
+  public static final String s29375 = "29375";
+  public static final String s29376 = "29376";
+  public static final String s29377 = "29377";
+  public static final String s29378 = "29378";
+  public static final String s29379 = "29379";
+  public static final String s29380 = "29380";
+  public static final String s29381 = "29381";
+  public static final String s29382 = "29382";
+  public static final String s29383 = "29383";
+  public static final String s29384 = "29384";
+  public static final String s29385 = "29385";
+  public static final String s29386 = "29386";
+  public static final String s29387 = "29387";
+  public static final String s29388 = "29388";
+  public static final String s29389 = "29389";
+  public static final String s29390 = "29390";
+  public static final String s29391 = "29391";
+  public static final String s29392 = "29392";
+  public static final String s29393 = "29393";
+  public static final String s29394 = "29394";
+  public static final String s29395 = "29395";
+  public static final String s29396 = "29396";
+  public static final String s29397 = "29397";
+  public static final String s29398 = "29398";
+  public static final String s29399 = "29399";
+  public static final String s29400 = "29400";
+  public static final String s29401 = "29401";
+  public static final String s29402 = "29402";
+  public static final String s29403 = "29403";
+  public static final String s29404 = "29404";
+  public static final String s29405 = "29405";
+  public static final String s29406 = "29406";
+  public static final String s29407 = "29407";
+  public static final String s29408 = "29408";
+  public static final String s29409 = "29409";
+  public static final String s29410 = "29410";
+  public static final String s29411 = "29411";
+  public static final String s29412 = "29412";
+  public static final String s29413 = "29413";
+  public static final String s29414 = "29414";
+  public static final String s29415 = "29415";
+  public static final String s29416 = "29416";
+  public static final String s29417 = "29417";
+  public static final String s29418 = "29418";
+  public static final String s29419 = "29419";
+  public static final String s29420 = "29420";
+  public static final String s29421 = "29421";
+  public static final String s29422 = "29422";
+  public static final String s29423 = "29423";
+  public static final String s29424 = "29424";
+  public static final String s29425 = "29425";
+  public static final String s29426 = "29426";
+  public static final String s29427 = "29427";
+  public static final String s29428 = "29428";
+  public static final String s29429 = "29429";
+  public static final String s29430 = "29430";
+  public static final String s29431 = "29431";
+  public static final String s29432 = "29432";
+  public static final String s29433 = "29433";
+  public static final String s29434 = "29434";
+  public static final String s29435 = "29435";
+  public static final String s29436 = "29436";
+  public static final String s29437 = "29437";
+  public static final String s29438 = "29438";
+  public static final String s29439 = "29439";
+  public static final String s29440 = "29440";
+  public static final String s29441 = "29441";
+  public static final String s29442 = "29442";
+  public static final String s29443 = "29443";
+  public static final String s29444 = "29444";
+  public static final String s29445 = "29445";
+  public static final String s29446 = "29446";
+  public static final String s29447 = "29447";
+  public static final String s29448 = "29448";
+  public static final String s29449 = "29449";
+  public static final String s29450 = "29450";
+  public static final String s29451 = "29451";
+  public static final String s29452 = "29452";
+  public static final String s29453 = "29453";
+  public static final String s29454 = "29454";
+  public static final String s29455 = "29455";
+  public static final String s29456 = "29456";
+  public static final String s29457 = "29457";
+  public static final String s29458 = "29458";
+  public static final String s29459 = "29459";
+  public static final String s29460 = "29460";
+  public static final String s29461 = "29461";
+  public static final String s29462 = "29462";
+  public static final String s29463 = "29463";
+  public static final String s29464 = "29464";
+  public static final String s29465 = "29465";
+  public static final String s29466 = "29466";
+  public static final String s29467 = "29467";
+  public static final String s29468 = "29468";
+  public static final String s29469 = "29469";
+  public static final String s29470 = "29470";
+  public static final String s29471 = "29471";
+  public static final String s29472 = "29472";
+  public static final String s29473 = "29473";
+  public static final String s29474 = "29474";
+  public static final String s29475 = "29475";
+  public static final String s29476 = "29476";
+  public static final String s29477 = "29477";
+  public static final String s29478 = "29478";
+  public static final String s29479 = "29479";
+  public static final String s29480 = "29480";
+  public static final String s29481 = "29481";
+  public static final String s29482 = "29482";
+  public static final String s29483 = "29483";
+  public static final String s29484 = "29484";
+  public static final String s29485 = "29485";
+  public static final String s29486 = "29486";
+  public static final String s29487 = "29487";
+  public static final String s29488 = "29488";
+  public static final String s29489 = "29489";
+  public static final String s29490 = "29490";
+  public static final String s29491 = "29491";
+  public static final String s29492 = "29492";
+  public static final String s29493 = "29493";
+  public static final String s29494 = "29494";
+  public static final String s29495 = "29495";
+  public static final String s29496 = "29496";
+  public static final String s29497 = "29497";
+  public static final String s29498 = "29498";
+  public static final String s29499 = "29499";
+  public static final String s29500 = "29500";
+  public static final String s29501 = "29501";
+  public static final String s29502 = "29502";
+  public static final String s29503 = "29503";
+  public static final String s29504 = "29504";
+  public static final String s29505 = "29505";
+  public static final String s29506 = "29506";
+  public static final String s29507 = "29507";
+  public static final String s29508 = "29508";
+  public static final String s29509 = "29509";
+  public static final String s29510 = "29510";
+  public static final String s29511 = "29511";
+  public static final String s29512 = "29512";
+  public static final String s29513 = "29513";
+  public static final String s29514 = "29514";
+  public static final String s29515 = "29515";
+  public static final String s29516 = "29516";
+  public static final String s29517 = "29517";
+  public static final String s29518 = "29518";
+  public static final String s29519 = "29519";
+  public static final String s29520 = "29520";
+  public static final String s29521 = "29521";
+  public static final String s29522 = "29522";
+  public static final String s29523 = "29523";
+  public static final String s29524 = "29524";
+  public static final String s29525 = "29525";
+  public static final String s29526 = "29526";
+  public static final String s29527 = "29527";
+  public static final String s29528 = "29528";
+  public static final String s29529 = "29529";
+  public static final String s29530 = "29530";
+  public static final String s29531 = "29531";
+  public static final String s29532 = "29532";
+  public static final String s29533 = "29533";
+  public static final String s29534 = "29534";
+  public static final String s29535 = "29535";
+  public static final String s29536 = "29536";
+  public static final String s29537 = "29537";
+  public static final String s29538 = "29538";
+  public static final String s29539 = "29539";
+  public static final String s29540 = "29540";
+  public static final String s29541 = "29541";
+  public static final String s29542 = "29542";
+  public static final String s29543 = "29543";
+  public static final String s29544 = "29544";
+  public static final String s29545 = "29545";
+  public static final String s29546 = "29546";
+  public static final String s29547 = "29547";
+  public static final String s29548 = "29548";
+  public static final String s29549 = "29549";
+  public static final String s29550 = "29550";
+  public static final String s29551 = "29551";
+  public static final String s29552 = "29552";
+  public static final String s29553 = "29553";
+  public static final String s29554 = "29554";
+  public static final String s29555 = "29555";
+  public static final String s29556 = "29556";
+  public static final String s29557 = "29557";
+  public static final String s29558 = "29558";
+  public static final String s29559 = "29559";
+  public static final String s29560 = "29560";
+  public static final String s29561 = "29561";
+  public static final String s29562 = "29562";
+  public static final String s29563 = "29563";
+  public static final String s29564 = "29564";
+  public static final String s29565 = "29565";
+  public static final String s29566 = "29566";
+  public static final String s29567 = "29567";
+  public static final String s29568 = "29568";
+  public static final String s29569 = "29569";
+  public static final String s29570 = "29570";
+  public static final String s29571 = "29571";
+  public static final String s29572 = "29572";
+  public static final String s29573 = "29573";
+  public static final String s29574 = "29574";
+  public static final String s29575 = "29575";
+  public static final String s29576 = "29576";
+  public static final String s29577 = "29577";
+  public static final String s29578 = "29578";
+  public static final String s29579 = "29579";
+  public static final String s29580 = "29580";
+  public static final String s29581 = "29581";
+  public static final String s29582 = "29582";
+  public static final String s29583 = "29583";
+  public static final String s29584 = "29584";
+  public static final String s29585 = "29585";
+  public static final String s29586 = "29586";
+  public static final String s29587 = "29587";
+  public static final String s29588 = "29588";
+  public static final String s29589 = "29589";
+  public static final String s29590 = "29590";
+  public static final String s29591 = "29591";
+  public static final String s29592 = "29592";
+  public static final String s29593 = "29593";
+  public static final String s29594 = "29594";
+  public static final String s29595 = "29595";
+  public static final String s29596 = "29596";
+  public static final String s29597 = "29597";
+  public static final String s29598 = "29598";
+  public static final String s29599 = "29599";
+  public static final String s29600 = "29600";
+  public static final String s29601 = "29601";
+  public static final String s29602 = "29602";
+  public static final String s29603 = "29603";
+  public static final String s29604 = "29604";
+  public static final String s29605 = "29605";
+  public static final String s29606 = "29606";
+  public static final String s29607 = "29607";
+  public static final String s29608 = "29608";
+  public static final String s29609 = "29609";
+  public static final String s29610 = "29610";
+  public static final String s29611 = "29611";
+  public static final String s29612 = "29612";
+  public static final String s29613 = "29613";
+  public static final String s29614 = "29614";
+  public static final String s29615 = "29615";
+  public static final String s29616 = "29616";
+  public static final String s29617 = "29617";
+  public static final String s29618 = "29618";
+  public static final String s29619 = "29619";
+  public static final String s29620 = "29620";
+  public static final String s29621 = "29621";
+  public static final String s29622 = "29622";
+  public static final String s29623 = "29623";
+  public static final String s29624 = "29624";
+  public static final String s29625 = "29625";
+  public static final String s29626 = "29626";
+  public static final String s29627 = "29627";
+  public static final String s29628 = "29628";
+  public static final String s29629 = "29629";
+  public static final String s29630 = "29630";
+  public static final String s29631 = "29631";
+  public static final String s29632 = "29632";
+  public static final String s29633 = "29633";
+  public static final String s29634 = "29634";
+  public static final String s29635 = "29635";
+  public static final String s29636 = "29636";
+  public static final String s29637 = "29637";
+  public static final String s29638 = "29638";
+  public static final String s29639 = "29639";
+  public static final String s29640 = "29640";
+  public static final String s29641 = "29641";
+  public static final String s29642 = "29642";
+  public static final String s29643 = "29643";
+  public static final String s29644 = "29644";
+  public static final String s29645 = "29645";
+  public static final String s29646 = "29646";
+  public static final String s29647 = "29647";
+  public static final String s29648 = "29648";
+  public static final String s29649 = "29649";
+  public static final String s29650 = "29650";
+  public static final String s29651 = "29651";
+  public static final String s29652 = "29652";
+  public static final String s29653 = "29653";
+  public static final String s29654 = "29654";
+  public static final String s29655 = "29655";
+  public static final String s29656 = "29656";
+  public static final String s29657 = "29657";
+  public static final String s29658 = "29658";
+  public static final String s29659 = "29659";
+  public static final String s29660 = "29660";
+  public static final String s29661 = "29661";
+  public static final String s29662 = "29662";
+  public static final String s29663 = "29663";
+  public static final String s29664 = "29664";
+  public static final String s29665 = "29665";
+  public static final String s29666 = "29666";
+  public static final String s29667 = "29667";
+  public static final String s29668 = "29668";
+  public static final String s29669 = "29669";
+  public static final String s29670 = "29670";
+  public static final String s29671 = "29671";
+  public static final String s29672 = "29672";
+  public static final String s29673 = "29673";
+  public static final String s29674 = "29674";
+  public static final String s29675 = "29675";
+  public static final String s29676 = "29676";
+  public static final String s29677 = "29677";
+  public static final String s29678 = "29678";
+  public static final String s29679 = "29679";
+  public static final String s29680 = "29680";
+  public static final String s29681 = "29681";
+  public static final String s29682 = "29682";
+  public static final String s29683 = "29683";
+  public static final String s29684 = "29684";
+  public static final String s29685 = "29685";
+  public static final String s29686 = "29686";
+  public static final String s29687 = "29687";
+  public static final String s29688 = "29688";
+  public static final String s29689 = "29689";
+  public static final String s29690 = "29690";
+  public static final String s29691 = "29691";
+  public static final String s29692 = "29692";
+  public static final String s29693 = "29693";
+  public static final String s29694 = "29694";
+  public static final String s29695 = "29695";
+  public static final String s29696 = "29696";
+  public static final String s29697 = "29697";
+  public static final String s29698 = "29698";
+  public static final String s29699 = "29699";
+  public static final String s29700 = "29700";
+  public static final String s29701 = "29701";
+  public static final String s29702 = "29702";
+  public static final String s29703 = "29703";
+  public static final String s29704 = "29704";
+  public static final String s29705 = "29705";
+  public static final String s29706 = "29706";
+  public static final String s29707 = "29707";
+  public static final String s29708 = "29708";
+  public static final String s29709 = "29709";
+  public static final String s29710 = "29710";
+  public static final String s29711 = "29711";
+  public static final String s29712 = "29712";
+  public static final String s29713 = "29713";
+  public static final String s29714 = "29714";
+  public static final String s29715 = "29715";
+  public static final String s29716 = "29716";
+  public static final String s29717 = "29717";
+  public static final String s29718 = "29718";
+  public static final String s29719 = "29719";
+  public static final String s29720 = "29720";
+  public static final String s29721 = "29721";
+  public static final String s29722 = "29722";
+  public static final String s29723 = "29723";
+  public static final String s29724 = "29724";
+  public static final String s29725 = "29725";
+  public static final String s29726 = "29726";
+  public static final String s29727 = "29727";
+  public static final String s29728 = "29728";
+  public static final String s29729 = "29729";
+  public static final String s29730 = "29730";
+  public static final String s29731 = "29731";
+  public static final String s29732 = "29732";
+  public static final String s29733 = "29733";
+  public static final String s29734 = "29734";
+  public static final String s29735 = "29735";
+  public static final String s29736 = "29736";
+  public static final String s29737 = "29737";
+  public static final String s29738 = "29738";
+  public static final String s29739 = "29739";
+  public static final String s29740 = "29740";
+  public static final String s29741 = "29741";
+  public static final String s29742 = "29742";
+  public static final String s29743 = "29743";
+  public static final String s29744 = "29744";
+  public static final String s29745 = "29745";
+  public static final String s29746 = "29746";
+  public static final String s29747 = "29747";
+  public static final String s29748 = "29748";
+  public static final String s29749 = "29749";
+  public static final String s29750 = "29750";
+  public static final String s29751 = "29751";
+  public static final String s29752 = "29752";
+  public static final String s29753 = "29753";
+  public static final String s29754 = "29754";
+  public static final String s29755 = "29755";
+  public static final String s29756 = "29756";
+  public static final String s29757 = "29757";
+  public static final String s29758 = "29758";
+  public static final String s29759 = "29759";
+  public static final String s29760 = "29760";
+  public static final String s29761 = "29761";
+  public static final String s29762 = "29762";
+  public static final String s29763 = "29763";
+  public static final String s29764 = "29764";
+  public static final String s29765 = "29765";
+  public static final String s29766 = "29766";
+  public static final String s29767 = "29767";
+  public static final String s29768 = "29768";
+  public static final String s29769 = "29769";
+  public static final String s29770 = "29770";
+  public static final String s29771 = "29771";
+  public static final String s29772 = "29772";
+  public static final String s29773 = "29773";
+  public static final String s29774 = "29774";
+  public static final String s29775 = "29775";
+  public static final String s29776 = "29776";
+  public static final String s29777 = "29777";
+  public static final String s29778 = "29778";
+  public static final String s29779 = "29779";
+  public static final String s29780 = "29780";
+  public static final String s29781 = "29781";
+  public static final String s29782 = "29782";
+  public static final String s29783 = "29783";
+  public static final String s29784 = "29784";
+  public static final String s29785 = "29785";
+  public static final String s29786 = "29786";
+  public static final String s29787 = "29787";
+  public static final String s29788 = "29788";
+  public static final String s29789 = "29789";
+  public static final String s29790 = "29790";
+  public static final String s29791 = "29791";
+  public static final String s29792 = "29792";
+  public static final String s29793 = "29793";
+  public static final String s29794 = "29794";
+  public static final String s29795 = "29795";
+  public static final String s29796 = "29796";
+  public static final String s29797 = "29797";
+  public static final String s29798 = "29798";
+  public static final String s29799 = "29799";
+  public static final String s29800 = "29800";
+  public static final String s29801 = "29801";
+  public static final String s29802 = "29802";
+  public static final String s29803 = "29803";
+  public static final String s29804 = "29804";
+  public static final String s29805 = "29805";
+  public static final String s29806 = "29806";
+  public static final String s29807 = "29807";
+  public static final String s29808 = "29808";
+  public static final String s29809 = "29809";
+  public static final String s29810 = "29810";
+  public static final String s29811 = "29811";
+  public static final String s29812 = "29812";
+  public static final String s29813 = "29813";
+  public static final String s29814 = "29814";
+  public static final String s29815 = "29815";
+  public static final String s29816 = "29816";
+  public static final String s29817 = "29817";
+  public static final String s29818 = "29818";
+  public static final String s29819 = "29819";
+  public static final String s29820 = "29820";
+  public static final String s29821 = "29821";
+  public static final String s29822 = "29822";
+  public static final String s29823 = "29823";
+  public static final String s29824 = "29824";
+  public static final String s29825 = "29825";
+  public static final String s29826 = "29826";
+  public static final String s29827 = "29827";
+  public static final String s29828 = "29828";
+  public static final String s29829 = "29829";
+  public static final String s29830 = "29830";
+  public static final String s29831 = "29831";
+  public static final String s29832 = "29832";
+  public static final String s29833 = "29833";
+  public static final String s29834 = "29834";
+  public static final String s29835 = "29835";
+  public static final String s29836 = "29836";
+  public static final String s29837 = "29837";
+  public static final String s29838 = "29838";
+  public static final String s29839 = "29839";
+  public static final String s29840 = "29840";
+  public static final String s29841 = "29841";
+  public static final String s29842 = "29842";
+  public static final String s29843 = "29843";
+  public static final String s29844 = "29844";
+  public static final String s29845 = "29845";
+  public static final String s29846 = "29846";
+  public static final String s29847 = "29847";
+  public static final String s29848 = "29848";
+  public static final String s29849 = "29849";
+  public static final String s29850 = "29850";
+  public static final String s29851 = "29851";
+  public static final String s29852 = "29852";
+  public static final String s29853 = "29853";
+  public static final String s29854 = "29854";
+  public static final String s29855 = "29855";
+  public static final String s29856 = "29856";
+  public static final String s29857 = "29857";
+  public static final String s29858 = "29858";
+  public static final String s29859 = "29859";
+  public static final String s29860 = "29860";
+  public static final String s29861 = "29861";
+  public static final String s29862 = "29862";
+  public static final String s29863 = "29863";
+  public static final String s29864 = "29864";
+  public static final String s29865 = "29865";
+  public static final String s29866 = "29866";
+  public static final String s29867 = "29867";
+  public static final String s29868 = "29868";
+  public static final String s29869 = "29869";
+  public static final String s29870 = "29870";
+  public static final String s29871 = "29871";
+  public static final String s29872 = "29872";
+  public static final String s29873 = "29873";
+  public static final String s29874 = "29874";
+  public static final String s29875 = "29875";
+  public static final String s29876 = "29876";
+  public static final String s29877 = "29877";
+  public static final String s29878 = "29878";
+  public static final String s29879 = "29879";
+  public static final String s29880 = "29880";
+  public static final String s29881 = "29881";
+  public static final String s29882 = "29882";
+  public static final String s29883 = "29883";
+  public static final String s29884 = "29884";
+  public static final String s29885 = "29885";
+  public static final String s29886 = "29886";
+  public static final String s29887 = "29887";
+  public static final String s29888 = "29888";
+  public static final String s29889 = "29889";
+  public static final String s29890 = "29890";
+  public static final String s29891 = "29891";
+  public static final String s29892 = "29892";
+  public static final String s29893 = "29893";
+  public static final String s29894 = "29894";
+  public static final String s29895 = "29895";
+  public static final String s29896 = "29896";
+  public static final String s29897 = "29897";
+  public static final String s29898 = "29898";
+  public static final String s29899 = "29899";
+  public static final String s29900 = "29900";
+  public static final String s29901 = "29901";
+  public static final String s29902 = "29902";
+  public static final String s29903 = "29903";
+  public static final String s29904 = "29904";
+  public static final String s29905 = "29905";
+  public static final String s29906 = "29906";
+  public static final String s29907 = "29907";
+  public static final String s29908 = "29908";
+  public static final String s29909 = "29909";
+  public static final String s29910 = "29910";
+  public static final String s29911 = "29911";
+  public static final String s29912 = "29912";
+  public static final String s29913 = "29913";
+  public static final String s29914 = "29914";
+  public static final String s29915 = "29915";
+  public static final String s29916 = "29916";
+  public static final String s29917 = "29917";
+  public static final String s29918 = "29918";
+  public static final String s29919 = "29919";
+  public static final String s29920 = "29920";
+  public static final String s29921 = "29921";
+  public static final String s29922 = "29922";
+  public static final String s29923 = "29923";
+  public static final String s29924 = "29924";
+  public static final String s29925 = "29925";
+  public static final String s29926 = "29926";
+  public static final String s29927 = "29927";
+  public static final String s29928 = "29928";
+  public static final String s29929 = "29929";
+  public static final String s29930 = "29930";
+  public static final String s29931 = "29931";
+  public static final String s29932 = "29932";
+  public static final String s29933 = "29933";
+  public static final String s29934 = "29934";
+  public static final String s29935 = "29935";
+  public static final String s29936 = "29936";
+  public static final String s29937 = "29937";
+  public static final String s29938 = "29938";
+  public static final String s29939 = "29939";
+  public static final String s29940 = "29940";
+  public static final String s29941 = "29941";
+  public static final String s29942 = "29942";
+  public static final String s29943 = "29943";
+  public static final String s29944 = "29944";
+  public static final String s29945 = "29945";
+  public static final String s29946 = "29946";
+  public static final String s29947 = "29947";
+  public static final String s29948 = "29948";
+  public static final String s29949 = "29949";
+  public static final String s29950 = "29950";
+  public static final String s29951 = "29951";
+  public static final String s29952 = "29952";
+  public static final String s29953 = "29953";
+  public static final String s29954 = "29954";
+  public static final String s29955 = "29955";
+  public static final String s29956 = "29956";
+  public static final String s29957 = "29957";
+  public static final String s29958 = "29958";
+  public static final String s29959 = "29959";
+  public static final String s29960 = "29960";
+  public static final String s29961 = "29961";
+  public static final String s29962 = "29962";
+  public static final String s29963 = "29963";
+  public static final String s29964 = "29964";
+  public static final String s29965 = "29965";
+  public static final String s29966 = "29966";
+  public static final String s29967 = "29967";
+  public static final String s29968 = "29968";
+  public static final String s29969 = "29969";
+  public static final String s29970 = "29970";
+  public static final String s29971 = "29971";
+  public static final String s29972 = "29972";
+  public static final String s29973 = "29973";
+  public static final String s29974 = "29974";
+  public static final String s29975 = "29975";
+  public static final String s29976 = "29976";
+  public static final String s29977 = "29977";
+  public static final String s29978 = "29978";
+  public static final String s29979 = "29979";
+  public static final String s29980 = "29980";
+  public static final String s29981 = "29981";
+  public static final String s29982 = "29982";
+  public static final String s29983 = "29983";
+  public static final String s29984 = "29984";
+  public static final String s29985 = "29985";
+  public static final String s29986 = "29986";
+  public static final String s29987 = "29987";
+  public static final String s29988 = "29988";
+  public static final String s29989 = "29989";
+  public static final String s29990 = "29990";
+  public static final String s29991 = "29991";
+  public static final String s29992 = "29992";
+  public static final String s29993 = "29993";
+  public static final String s29994 = "29994";
+  public static final String s29995 = "29995";
+  public static final String s29996 = "29996";
+  public static final String s29997 = "29997";
+  public static final String s29998 = "29998";
+  public static final String s29999 = "29999";
+  public static final String s30000 = "30000";
+  public static final String s30001 = "30001";
+  public static final String s30002 = "30002";
+  public static final String s30003 = "30003";
+  public static final String s30004 = "30004";
+  public static final String s30005 = "30005";
+  public static final String s30006 = "30006";
+  public static final String s30007 = "30007";
+  public static final String s30008 = "30008";
+  public static final String s30009 = "30009";
+  public static final String s30010 = "30010";
+  public static final String s30011 = "30011";
+  public static final String s30012 = "30012";
+  public static final String s30013 = "30013";
+  public static final String s30014 = "30014";
+  public static final String s30015 = "30015";
+  public static final String s30016 = "30016";
+  public static final String s30017 = "30017";
+  public static final String s30018 = "30018";
+  public static final String s30019 = "30019";
+  public static final String s30020 = "30020";
+  public static final String s30021 = "30021";
+  public static final String s30022 = "30022";
+  public static final String s30023 = "30023";
+  public static final String s30024 = "30024";
+  public static final String s30025 = "30025";
+  public static final String s30026 = "30026";
+  public static final String s30027 = "30027";
+  public static final String s30028 = "30028";
+  public static final String s30029 = "30029";
+  public static final String s30030 = "30030";
+  public static final String s30031 = "30031";
+  public static final String s30032 = "30032";
+  public static final String s30033 = "30033";
+  public static final String s30034 = "30034";
+  public static final String s30035 = "30035";
+  public static final String s30036 = "30036";
+  public static final String s30037 = "30037";
+  public static final String s30038 = "30038";
+  public static final String s30039 = "30039";
+  public static final String s30040 = "30040";
+  public static final String s30041 = "30041";
+  public static final String s30042 = "30042";
+  public static final String s30043 = "30043";
+  public static final String s30044 = "30044";
+  public static final String s30045 = "30045";
+  public static final String s30046 = "30046";
+  public static final String s30047 = "30047";
+  public static final String s30048 = "30048";
+  public static final String s30049 = "30049";
+  public static final String s30050 = "30050";
+  public static final String s30051 = "30051";
+  public static final String s30052 = "30052";
+  public static final String s30053 = "30053";
+  public static final String s30054 = "30054";
+  public static final String s30055 = "30055";
+  public static final String s30056 = "30056";
+  public static final String s30057 = "30057";
+  public static final String s30058 = "30058";
+  public static final String s30059 = "30059";
+  public static final String s30060 = "30060";
+  public static final String s30061 = "30061";
+  public static final String s30062 = "30062";
+  public static final String s30063 = "30063";
+  public static final String s30064 = "30064";
+  public static final String s30065 = "30065";
+  public static final String s30066 = "30066";
+  public static final String s30067 = "30067";
+  public static final String s30068 = "30068";
+  public static final String s30069 = "30069";
+  public static final String s30070 = "30070";
+  public static final String s30071 = "30071";
+  public static final String s30072 = "30072";
+  public static final String s30073 = "30073";
+  public static final String s30074 = "30074";
+  public static final String s30075 = "30075";
+  public static final String s30076 = "30076";
+  public static final String s30077 = "30077";
+  public static final String s30078 = "30078";
+  public static final String s30079 = "30079";
+  public static final String s30080 = "30080";
+  public static final String s30081 = "30081";
+  public static final String s30082 = "30082";
+  public static final String s30083 = "30083";
+  public static final String s30084 = "30084";
+  public static final String s30085 = "30085";
+  public static final String s30086 = "30086";
+  public static final String s30087 = "30087";
+  public static final String s30088 = "30088";
+  public static final String s30089 = "30089";
+  public static final String s30090 = "30090";
+  public static final String s30091 = "30091";
+  public static final String s30092 = "30092";
+  public static final String s30093 = "30093";
+  public static final String s30094 = "30094";
+  public static final String s30095 = "30095";
+  public static final String s30096 = "30096";
+  public static final String s30097 = "30097";
+  public static final String s30098 = "30098";
+  public static final String s30099 = "30099";
+  public static final String s30100 = "30100";
+  public static final String s30101 = "30101";
+  public static final String s30102 = "30102";
+  public static final String s30103 = "30103";
+  public static final String s30104 = "30104";
+  public static final String s30105 = "30105";
+  public static final String s30106 = "30106";
+  public static final String s30107 = "30107";
+  public static final String s30108 = "30108";
+  public static final String s30109 = "30109";
+  public static final String s30110 = "30110";
+  public static final String s30111 = "30111";
+  public static final String s30112 = "30112";
+  public static final String s30113 = "30113";
+  public static final String s30114 = "30114";
+  public static final String s30115 = "30115";
+  public static final String s30116 = "30116";
+  public static final String s30117 = "30117";
+  public static final String s30118 = "30118";
+  public static final String s30119 = "30119";
+  public static final String s30120 = "30120";
+  public static final String s30121 = "30121";
+  public static final String s30122 = "30122";
+  public static final String s30123 = "30123";
+  public static final String s30124 = "30124";
+  public static final String s30125 = "30125";
+  public static final String s30126 = "30126";
+  public static final String s30127 = "30127";
+  public static final String s30128 = "30128";
+  public static final String s30129 = "30129";
+  public static final String s30130 = "30130";
+  public static final String s30131 = "30131";
+  public static final String s30132 = "30132";
+  public static final String s30133 = "30133";
+  public static final String s30134 = "30134";
+  public static final String s30135 = "30135";
+  public static final String s30136 = "30136";
+  public static final String s30137 = "30137";
+  public static final String s30138 = "30138";
+  public static final String s30139 = "30139";
+  public static final String s30140 = "30140";
+  public static final String s30141 = "30141";
+  public static final String s30142 = "30142";
+  public static final String s30143 = "30143";
+  public static final String s30144 = "30144";
+  public static final String s30145 = "30145";
+  public static final String s30146 = "30146";
+  public static final String s30147 = "30147";
+  public static final String s30148 = "30148";
+  public static final String s30149 = "30149";
+  public static final String s30150 = "30150";
+  public static final String s30151 = "30151";
+  public static final String s30152 = "30152";
+  public static final String s30153 = "30153";
+  public static final String s30154 = "30154";
+  public static final String s30155 = "30155";
+  public static final String s30156 = "30156";
+  public static final String s30157 = "30157";
+  public static final String s30158 = "30158";
+  public static final String s30159 = "30159";
+  public static final String s30160 = "30160";
+  public static final String s30161 = "30161";
+  public static final String s30162 = "30162";
+  public static final String s30163 = "30163";
+  public static final String s30164 = "30164";
+  public static final String s30165 = "30165";
+  public static final String s30166 = "30166";
+  public static final String s30167 = "30167";
+  public static final String s30168 = "30168";
+  public static final String s30169 = "30169";
+  public static final String s30170 = "30170";
+  public static final String s30171 = "30171";
+  public static final String s30172 = "30172";
+  public static final String s30173 = "30173";
+  public static final String s30174 = "30174";
+  public static final String s30175 = "30175";
+  public static final String s30176 = "30176";
+  public static final String s30177 = "30177";
+  public static final String s30178 = "30178";
+  public static final String s30179 = "30179";
+  public static final String s30180 = "30180";
+  public static final String s30181 = "30181";
+  public static final String s30182 = "30182";
+  public static final String s30183 = "30183";
+  public static final String s30184 = "30184";
+  public static final String s30185 = "30185";
+  public static final String s30186 = "30186";
+  public static final String s30187 = "30187";
+  public static final String s30188 = "30188";
+  public static final String s30189 = "30189";
+  public static final String s30190 = "30190";
+  public static final String s30191 = "30191";
+  public static final String s30192 = "30192";
+  public static final String s30193 = "30193";
+  public static final String s30194 = "30194";
+  public static final String s30195 = "30195";
+  public static final String s30196 = "30196";
+  public static final String s30197 = "30197";
+  public static final String s30198 = "30198";
+  public static final String s30199 = "30199";
+  public static final String s30200 = "30200";
+  public static final String s30201 = "30201";
+  public static final String s30202 = "30202";
+  public static final String s30203 = "30203";
+  public static final String s30204 = "30204";
+  public static final String s30205 = "30205";
+  public static final String s30206 = "30206";
+  public static final String s30207 = "30207";
+  public static final String s30208 = "30208";
+  public static final String s30209 = "30209";
+  public static final String s30210 = "30210";
+  public static final String s30211 = "30211";
+  public static final String s30212 = "30212";
+  public static final String s30213 = "30213";
+  public static final String s30214 = "30214";
+  public static final String s30215 = "30215";
+  public static final String s30216 = "30216";
+  public static final String s30217 = "30217";
+  public static final String s30218 = "30218";
+  public static final String s30219 = "30219";
+  public static final String s30220 = "30220";
+  public static final String s30221 = "30221";
+  public static final String s30222 = "30222";
+  public static final String s30223 = "30223";
+  public static final String s30224 = "30224";
+  public static final String s30225 = "30225";
+  public static final String s30226 = "30226";
+  public static final String s30227 = "30227";
+  public static final String s30228 = "30228";
+  public static final String s30229 = "30229";
+  public static final String s30230 = "30230";
+  public static final String s30231 = "30231";
+  public static final String s30232 = "30232";
+  public static final String s30233 = "30233";
+  public static final String s30234 = "30234";
+  public static final String s30235 = "30235";
+  public static final String s30236 = "30236";
+  public static final String s30237 = "30237";
+  public static final String s30238 = "30238";
+  public static final String s30239 = "30239";
+  public static final String s30240 = "30240";
+  public static final String s30241 = "30241";
+  public static final String s30242 = "30242";
+  public static final String s30243 = "30243";
+  public static final String s30244 = "30244";
+  public static final String s30245 = "30245";
+  public static final String s30246 = "30246";
+  public static final String s30247 = "30247";
+  public static final String s30248 = "30248";
+  public static final String s30249 = "30249";
+  public static final String s30250 = "30250";
+  public static final String s30251 = "30251";
+  public static final String s30252 = "30252";
+  public static final String s30253 = "30253";
+  public static final String s30254 = "30254";
+  public static final String s30255 = "30255";
+  public static final String s30256 = "30256";
+  public static final String s30257 = "30257";
+  public static final String s30258 = "30258";
+  public static final String s30259 = "30259";
+  public static final String s30260 = "30260";
+  public static final String s30261 = "30261";
+  public static final String s30262 = "30262";
+  public static final String s30263 = "30263";
+  public static final String s30264 = "30264";
+  public static final String s30265 = "30265";
+  public static final String s30266 = "30266";
+  public static final String s30267 = "30267";
+  public static final String s30268 = "30268";
+  public static final String s30269 = "30269";
+  public static final String s30270 = "30270";
+  public static final String s30271 = "30271";
+  public static final String s30272 = "30272";
+  public static final String s30273 = "30273";
+  public static final String s30274 = "30274";
+  public static final String s30275 = "30275";
+  public static final String s30276 = "30276";
+  public static final String s30277 = "30277";
+  public static final String s30278 = "30278";
+  public static final String s30279 = "30279";
+  public static final String s30280 = "30280";
+  public static final String s30281 = "30281";
+  public static final String s30282 = "30282";
+  public static final String s30283 = "30283";
+  public static final String s30284 = "30284";
+  public static final String s30285 = "30285";
+  public static final String s30286 = "30286";
+  public static final String s30287 = "30287";
+  public static final String s30288 = "30288";
+  public static final String s30289 = "30289";
+  public static final String s30290 = "30290";
+  public static final String s30291 = "30291";
+  public static final String s30292 = "30292";
+  public static final String s30293 = "30293";
+  public static final String s30294 = "30294";
+  public static final String s30295 = "30295";
+  public static final String s30296 = "30296";
+  public static final String s30297 = "30297";
+  public static final String s30298 = "30298";
+  public static final String s30299 = "30299";
+  public static final String s30300 = "30300";
+  public static final String s30301 = "30301";
+  public static final String s30302 = "30302";
+  public static final String s30303 = "30303";
+  public static final String s30304 = "30304";
+  public static final String s30305 = "30305";
+  public static final String s30306 = "30306";
+  public static final String s30307 = "30307";
+  public static final String s30308 = "30308";
+  public static final String s30309 = "30309";
+  public static final String s30310 = "30310";
+  public static final String s30311 = "30311";
+  public static final String s30312 = "30312";
+  public static final String s30313 = "30313";
+  public static final String s30314 = "30314";
+  public static final String s30315 = "30315";
+  public static final String s30316 = "30316";
+  public static final String s30317 = "30317";
+  public static final String s30318 = "30318";
+  public static final String s30319 = "30319";
+  public static final String s30320 = "30320";
+  public static final String s30321 = "30321";
+  public static final String s30322 = "30322";
+  public static final String s30323 = "30323";
+  public static final String s30324 = "30324";
+  public static final String s30325 = "30325";
+  public static final String s30326 = "30326";
+  public static final String s30327 = "30327";
+  public static final String s30328 = "30328";
+  public static final String s30329 = "30329";
+  public static final String s30330 = "30330";
+  public static final String s30331 = "30331";
+  public static final String s30332 = "30332";
+  public static final String s30333 = "30333";
+  public static final String s30334 = "30334";
+  public static final String s30335 = "30335";
+  public static final String s30336 = "30336";
+  public static final String s30337 = "30337";
+  public static final String s30338 = "30338";
+  public static final String s30339 = "30339";
+  public static final String s30340 = "30340";
+  public static final String s30341 = "30341";
+  public static final String s30342 = "30342";
+  public static final String s30343 = "30343";
+  public static final String s30344 = "30344";
+  public static final String s30345 = "30345";
+  public static final String s30346 = "30346";
+  public static final String s30347 = "30347";
+  public static final String s30348 = "30348";
+  public static final String s30349 = "30349";
+  public static final String s30350 = "30350";
+  public static final String s30351 = "30351";
+  public static final String s30352 = "30352";
+  public static final String s30353 = "30353";
+  public static final String s30354 = "30354";
+  public static final String s30355 = "30355";
+  public static final String s30356 = "30356";
+  public static final String s30357 = "30357";
+  public static final String s30358 = "30358";
+  public static final String s30359 = "30359";
+  public static final String s30360 = "30360";
+  public static final String s30361 = "30361";
+  public static final String s30362 = "30362";
+  public static final String s30363 = "30363";
+  public static final String s30364 = "30364";
+  public static final String s30365 = "30365";
+  public static final String s30366 = "30366";
+  public static final String s30367 = "30367";
+  public static final String s30368 = "30368";
+  public static final String s30369 = "30369";
+  public static final String s30370 = "30370";
+  public static final String s30371 = "30371";
+  public static final String s30372 = "30372";
+  public static final String s30373 = "30373";
+  public static final String s30374 = "30374";
+  public static final String s30375 = "30375";
+  public static final String s30376 = "30376";
+  public static final String s30377 = "30377";
+  public static final String s30378 = "30378";
+  public static final String s30379 = "30379";
+  public static final String s30380 = "30380";
+  public static final String s30381 = "30381";
+  public static final String s30382 = "30382";
+  public static final String s30383 = "30383";
+  public static final String s30384 = "30384";
+  public static final String s30385 = "30385";
+  public static final String s30386 = "30386";
+  public static final String s30387 = "30387";
+  public static final String s30388 = "30388";
+  public static final String s30389 = "30389";
+  public static final String s30390 = "30390";
+  public static final String s30391 = "30391";
+  public static final String s30392 = "30392";
+  public static final String s30393 = "30393";
+  public static final String s30394 = "30394";
+  public static final String s30395 = "30395";
+  public static final String s30396 = "30396";
+  public static final String s30397 = "30397";
+  public static final String s30398 = "30398";
+  public static final String s30399 = "30399";
+  public static final String s30400 = "30400";
+  public static final String s30401 = "30401";
+  public static final String s30402 = "30402";
+  public static final String s30403 = "30403";
+  public static final String s30404 = "30404";
+  public static final String s30405 = "30405";
+  public static final String s30406 = "30406";
+  public static final String s30407 = "30407";
+  public static final String s30408 = "30408";
+  public static final String s30409 = "30409";
+  public static final String s30410 = "30410";
+  public static final String s30411 = "30411";
+  public static final String s30412 = "30412";
+  public static final String s30413 = "30413";
+  public static final String s30414 = "30414";
+  public static final String s30415 = "30415";
+  public static final String s30416 = "30416";
+  public static final String s30417 = "30417";
+  public static final String s30418 = "30418";
+  public static final String s30419 = "30419";
+  public static final String s30420 = "30420";
+  public static final String s30421 = "30421";
+  public static final String s30422 = "30422";
+  public static final String s30423 = "30423";
+  public static final String s30424 = "30424";
+  public static final String s30425 = "30425";
+  public static final String s30426 = "30426";
+  public static final String s30427 = "30427";
+  public static final String s30428 = "30428";
+  public static final String s30429 = "30429";
+  public static final String s30430 = "30430";
+  public static final String s30431 = "30431";
+  public static final String s30432 = "30432";
+  public static final String s30433 = "30433";
+  public static final String s30434 = "30434";
+  public static final String s30435 = "30435";
+  public static final String s30436 = "30436";
+  public static final String s30437 = "30437";
+  public static final String s30438 = "30438";
+  public static final String s30439 = "30439";
+  public static final String s30440 = "30440";
+  public static final String s30441 = "30441";
+  public static final String s30442 = "30442";
+  public static final String s30443 = "30443";
+  public static final String s30444 = "30444";
+  public static final String s30445 = "30445";
+  public static final String s30446 = "30446";
+  public static final String s30447 = "30447";
+  public static final String s30448 = "30448";
+  public static final String s30449 = "30449";
+  public static final String s30450 = "30450";
+  public static final String s30451 = "30451";
+  public static final String s30452 = "30452";
+  public static final String s30453 = "30453";
+  public static final String s30454 = "30454";
+  public static final String s30455 = "30455";
+  public static final String s30456 = "30456";
+  public static final String s30457 = "30457";
+  public static final String s30458 = "30458";
+  public static final String s30459 = "30459";
+  public static final String s30460 = "30460";
+  public static final String s30461 = "30461";
+  public static final String s30462 = "30462";
+  public static final String s30463 = "30463";
+  public static final String s30464 = "30464";
+  public static final String s30465 = "30465";
+  public static final String s30466 = "30466";
+  public static final String s30467 = "30467";
+  public static final String s30468 = "30468";
+  public static final String s30469 = "30469";
+  public static final String s30470 = "30470";
+  public static final String s30471 = "30471";
+  public static final String s30472 = "30472";
+  public static final String s30473 = "30473";
+  public static final String s30474 = "30474";
+  public static final String s30475 = "30475";
+  public static final String s30476 = "30476";
+  public static final String s30477 = "30477";
+  public static final String s30478 = "30478";
+  public static final String s30479 = "30479";
+  public static final String s30480 = "30480";
+  public static final String s30481 = "30481";
+  public static final String s30482 = "30482";
+  public static final String s30483 = "30483";
+  public static final String s30484 = "30484";
+  public static final String s30485 = "30485";
+  public static final String s30486 = "30486";
+  public static final String s30487 = "30487";
+  public static final String s30488 = "30488";
+  public static final String s30489 = "30489";
+  public static final String s30490 = "30490";
+  public static final String s30491 = "30491";
+  public static final String s30492 = "30492";
+  public static final String s30493 = "30493";
+  public static final String s30494 = "30494";
+  public static final String s30495 = "30495";
+  public static final String s30496 = "30496";
+  public static final String s30497 = "30497";
+  public static final String s30498 = "30498";
+  public static final String s30499 = "30499";
+  public static final String s30500 = "30500";
+  public static final String s30501 = "30501";
+  public static final String s30502 = "30502";
+  public static final String s30503 = "30503";
+  public static final String s30504 = "30504";
+  public static final String s30505 = "30505";
+  public static final String s30506 = "30506";
+  public static final String s30507 = "30507";
+  public static final String s30508 = "30508";
+  public static final String s30509 = "30509";
+  public static final String s30510 = "30510";
+  public static final String s30511 = "30511";
+  public static final String s30512 = "30512";
+  public static final String s30513 = "30513";
+  public static final String s30514 = "30514";
+  public static final String s30515 = "30515";
+  public static final String s30516 = "30516";
+  public static final String s30517 = "30517";
+  public static final String s30518 = "30518";
+  public static final String s30519 = "30519";
+  public static final String s30520 = "30520";
+  public static final String s30521 = "30521";
+  public static final String s30522 = "30522";
+  public static final String s30523 = "30523";
+  public static final String s30524 = "30524";
+  public static final String s30525 = "30525";
+  public static final String s30526 = "30526";
+  public static final String s30527 = "30527";
+  public static final String s30528 = "30528";
+  public static final String s30529 = "30529";
+  public static final String s30530 = "30530";
+  public static final String s30531 = "30531";
+  public static final String s30532 = "30532";
+  public static final String s30533 = "30533";
+  public static final String s30534 = "30534";
+  public static final String s30535 = "30535";
+  public static final String s30536 = "30536";
+  public static final String s30537 = "30537";
+  public static final String s30538 = "30538";
+  public static final String s30539 = "30539";
+  public static final String s30540 = "30540";
+  public static final String s30541 = "30541";
+  public static final String s30542 = "30542";
+  public static final String s30543 = "30543";
+  public static final String s30544 = "30544";
+  public static final String s30545 = "30545";
+  public static final String s30546 = "30546";
+  public static final String s30547 = "30547";
+  public static final String s30548 = "30548";
+  public static final String s30549 = "30549";
+  public static final String s30550 = "30550";
+  public static final String s30551 = "30551";
+  public static final String s30552 = "30552";
+  public static final String s30553 = "30553";
+  public static final String s30554 = "30554";
+  public static final String s30555 = "30555";
+  public static final String s30556 = "30556";
+  public static final String s30557 = "30557";
+  public static final String s30558 = "30558";
+  public static final String s30559 = "30559";
+  public static final String s30560 = "30560";
+  public static final String s30561 = "30561";
+  public static final String s30562 = "30562";
+  public static final String s30563 = "30563";
+  public static final String s30564 = "30564";
+  public static final String s30565 = "30565";
+  public static final String s30566 = "30566";
+  public static final String s30567 = "30567";
+  public static final String s30568 = "30568";
+  public static final String s30569 = "30569";
+  public static final String s30570 = "30570";
+  public static final String s30571 = "30571";
+  public static final String s30572 = "30572";
+  public static final String s30573 = "30573";
+  public static final String s30574 = "30574";
+  public static final String s30575 = "30575";
+  public static final String s30576 = "30576";
+  public static final String s30577 = "30577";
+  public static final String s30578 = "30578";
+  public static final String s30579 = "30579";
+  public static final String s30580 = "30580";
+  public static final String s30581 = "30581";
+  public static final String s30582 = "30582";
+  public static final String s30583 = "30583";
+  public static final String s30584 = "30584";
+  public static final String s30585 = "30585";
+  public static final String s30586 = "30586";
+  public static final String s30587 = "30587";
+  public static final String s30588 = "30588";
+  public static final String s30589 = "30589";
+  public static final String s30590 = "30590";
+  public static final String s30591 = "30591";
+  public static final String s30592 = "30592";
+  public static final String s30593 = "30593";
+  public static final String s30594 = "30594";
+  public static final String s30595 = "30595";
+  public static final String s30596 = "30596";
+  public static final String s30597 = "30597";
+  public static final String s30598 = "30598";
+  public static final String s30599 = "30599";
+  public static final String s30600 = "30600";
+  public static final String s30601 = "30601";
+  public static final String s30602 = "30602";
+  public static final String s30603 = "30603";
+  public static final String s30604 = "30604";
+  public static final String s30605 = "30605";
+  public static final String s30606 = "30606";
+  public static final String s30607 = "30607";
+  public static final String s30608 = "30608";
+  public static final String s30609 = "30609";
+  public static final String s30610 = "30610";
+  public static final String s30611 = "30611";
+  public static final String s30612 = "30612";
+  public static final String s30613 = "30613";
+  public static final String s30614 = "30614";
+  public static final String s30615 = "30615";
+  public static final String s30616 = "30616";
+  public static final String s30617 = "30617";
+  public static final String s30618 = "30618";
+  public static final String s30619 = "30619";
+  public static final String s30620 = "30620";
+  public static final String s30621 = "30621";
+  public static final String s30622 = "30622";
+  public static final String s30623 = "30623";
+  public static final String s30624 = "30624";
+  public static final String s30625 = "30625";
+  public static final String s30626 = "30626";
+  public static final String s30627 = "30627";
+  public static final String s30628 = "30628";
+  public static final String s30629 = "30629";
+  public static final String s30630 = "30630";
+  public static final String s30631 = "30631";
+  public static final String s30632 = "30632";
+  public static final String s30633 = "30633";
+  public static final String s30634 = "30634";
+  public static final String s30635 = "30635";
+  public static final String s30636 = "30636";
+  public static final String s30637 = "30637";
+  public static final String s30638 = "30638";
+  public static final String s30639 = "30639";
+  public static final String s30640 = "30640";
+  public static final String s30641 = "30641";
+  public static final String s30642 = "30642";
+  public static final String s30643 = "30643";
+  public static final String s30644 = "30644";
+  public static final String s30645 = "30645";
+  public static final String s30646 = "30646";
+  public static final String s30647 = "30647";
+  public static final String s30648 = "30648";
+  public static final String s30649 = "30649";
+  public static final String s30650 = "30650";
+  public static final String s30651 = "30651";
+  public static final String s30652 = "30652";
+  public static final String s30653 = "30653";
+  public static final String s30654 = "30654";
+  public static final String s30655 = "30655";
+  public static final String s30656 = "30656";
+  public static final String s30657 = "30657";
+  public static final String s30658 = "30658";
+  public static final String s30659 = "30659";
+  public static final String s30660 = "30660";
+  public static final String s30661 = "30661";
+  public static final String s30662 = "30662";
+  public static final String s30663 = "30663";
+  public static final String s30664 = "30664";
+  public static final String s30665 = "30665";
+  public static final String s30666 = "30666";
+  public static final String s30667 = "30667";
+  public static final String s30668 = "30668";
+  public static final String s30669 = "30669";
+  public static final String s30670 = "30670";
+  public static final String s30671 = "30671";
+  public static final String s30672 = "30672";
+  public static final String s30673 = "30673";
+  public static final String s30674 = "30674";
+  public static final String s30675 = "30675";
+  public static final String s30676 = "30676";
+  public static final String s30677 = "30677";
+  public static final String s30678 = "30678";
+  public static final String s30679 = "30679";
+  public static final String s30680 = "30680";
+  public static final String s30681 = "30681";
+  public static final String s30682 = "30682";
+  public static final String s30683 = "30683";
+  public static final String s30684 = "30684";
+  public static final String s30685 = "30685";
+  public static final String s30686 = "30686";
+  public static final String s30687 = "30687";
+  public static final String s30688 = "30688";
+  public static final String s30689 = "30689";
+  public static final String s30690 = "30690";
+  public static final String s30691 = "30691";
+  public static final String s30692 = "30692";
+  public static final String s30693 = "30693";
+  public static final String s30694 = "30694";
+  public static final String s30695 = "30695";
+  public static final String s30696 = "30696";
+  public static final String s30697 = "30697";
+  public static final String s30698 = "30698";
+  public static final String s30699 = "30699";
+  public static final String s30700 = "30700";
+  public static final String s30701 = "30701";
+  public static final String s30702 = "30702";
+  public static final String s30703 = "30703";
+  public static final String s30704 = "30704";
+  public static final String s30705 = "30705";
+  public static final String s30706 = "30706";
+  public static final String s30707 = "30707";
+  public static final String s30708 = "30708";
+  public static final String s30709 = "30709";
+  public static final String s30710 = "30710";
+  public static final String s30711 = "30711";
+  public static final String s30712 = "30712";
+  public static final String s30713 = "30713";
+  public static final String s30714 = "30714";
+  public static final String s30715 = "30715";
+  public static final String s30716 = "30716";
+  public static final String s30717 = "30717";
+  public static final String s30718 = "30718";
+  public static final String s30719 = "30719";
+  public static final String s30720 = "30720";
+  public static final String s30721 = "30721";
+  public static final String s30722 = "30722";
+  public static final String s30723 = "30723";
+  public static final String s30724 = "30724";
+  public static final String s30725 = "30725";
+  public static final String s30726 = "30726";
+  public static final String s30727 = "30727";
+  public static final String s30728 = "30728";
+  public static final String s30729 = "30729";
+  public static final String s30730 = "30730";
+  public static final String s30731 = "30731";
+  public static final String s30732 = "30732";
+  public static final String s30733 = "30733";
+  public static final String s30734 = "30734";
+  public static final String s30735 = "30735";
+  public static final String s30736 = "30736";
+  public static final String s30737 = "30737";
+  public static final String s30738 = "30738";
+  public static final String s30739 = "30739";
+  public static final String s30740 = "30740";
+  public static final String s30741 = "30741";
+  public static final String s30742 = "30742";
+  public static final String s30743 = "30743";
+  public static final String s30744 = "30744";
+  public static final String s30745 = "30745";
+  public static final String s30746 = "30746";
+  public static final String s30747 = "30747";
+  public static final String s30748 = "30748";
+  public static final String s30749 = "30749";
+  public static final String s30750 = "30750";
+  public static final String s30751 = "30751";
+  public static final String s30752 = "30752";
+  public static final String s30753 = "30753";
+  public static final String s30754 = "30754";
+  public static final String s30755 = "30755";
+  public static final String s30756 = "30756";
+  public static final String s30757 = "30757";
+  public static final String s30758 = "30758";
+  public static final String s30759 = "30759";
+  public static final String s30760 = "30760";
+  public static final String s30761 = "30761";
+  public static final String s30762 = "30762";
+  public static final String s30763 = "30763";
+  public static final String s30764 = "30764";
+  public static final String s30765 = "30765";
+  public static final String s30766 = "30766";
+  public static final String s30767 = "30767";
+  public static final String s30768 = "30768";
+  public static final String s30769 = "30769";
+  public static final String s30770 = "30770";
+  public static final String s30771 = "30771";
+  public static final String s30772 = "30772";
+  public static final String s30773 = "30773";
+  public static final String s30774 = "30774";
+  public static final String s30775 = "30775";
+  public static final String s30776 = "30776";
+  public static final String s30777 = "30777";
+  public static final String s30778 = "30778";
+  public static final String s30779 = "30779";
+  public static final String s30780 = "30780";
+  public static final String s30781 = "30781";
+  public static final String s30782 = "30782";
+  public static final String s30783 = "30783";
+  public static final String s30784 = "30784";
+  public static final String s30785 = "30785";
+  public static final String s30786 = "30786";
+  public static final String s30787 = "30787";
+  public static final String s30788 = "30788";
+  public static final String s30789 = "30789";
+  public static final String s30790 = "30790";
+  public static final String s30791 = "30791";
+  public static final String s30792 = "30792";
+  public static final String s30793 = "30793";
+  public static final String s30794 = "30794";
+  public static final String s30795 = "30795";
+  public static final String s30796 = "30796";
+  public static final String s30797 = "30797";
+  public static final String s30798 = "30798";
+  public static final String s30799 = "30799";
+  public static final String s30800 = "30800";
+  public static final String s30801 = "30801";
+  public static final String s30802 = "30802";
+  public static final String s30803 = "30803";
+  public static final String s30804 = "30804";
+  public static final String s30805 = "30805";
+  public static final String s30806 = "30806";
+  public static final String s30807 = "30807";
+  public static final String s30808 = "30808";
+  public static final String s30809 = "30809";
+  public static final String s30810 = "30810";
+  public static final String s30811 = "30811";
+  public static final String s30812 = "30812";
+  public static final String s30813 = "30813";
+  public static final String s30814 = "30814";
+  public static final String s30815 = "30815";
+  public static final String s30816 = "30816";
+  public static final String s30817 = "30817";
+  public static final String s30818 = "30818";
+  public static final String s30819 = "30819";
+  public static final String s30820 = "30820";
+  public static final String s30821 = "30821";
+  public static final String s30822 = "30822";
+  public static final String s30823 = "30823";
+  public static final String s30824 = "30824";
+  public static final String s30825 = "30825";
+  public static final String s30826 = "30826";
+  public static final String s30827 = "30827";
+  public static final String s30828 = "30828";
+  public static final String s30829 = "30829";
+  public static final String s30830 = "30830";
+  public static final String s30831 = "30831";
+  public static final String s30832 = "30832";
+  public static final String s30833 = "30833";
+  public static final String s30834 = "30834";
+  public static final String s30835 = "30835";
+  public static final String s30836 = "30836";
+  public static final String s30837 = "30837";
+  public static final String s30838 = "30838";
+  public static final String s30839 = "30839";
+  public static final String s30840 = "30840";
+  public static final String s30841 = "30841";
+  public static final String s30842 = "30842";
+  public static final String s30843 = "30843";
+  public static final String s30844 = "30844";
+  public static final String s30845 = "30845";
+  public static final String s30846 = "30846";
+  public static final String s30847 = "30847";
+  public static final String s30848 = "30848";
+  public static final String s30849 = "30849";
+  public static final String s30850 = "30850";
+  public static final String s30851 = "30851";
+  public static final String s30852 = "30852";
+  public static final String s30853 = "30853";
+  public static final String s30854 = "30854";
+  public static final String s30855 = "30855";
+  public static final String s30856 = "30856";
+  public static final String s30857 = "30857";
+  public static final String s30858 = "30858";
+  public static final String s30859 = "30859";
+  public static final String s30860 = "30860";
+  public static final String s30861 = "30861";
+  public static final String s30862 = "30862";
+  public static final String s30863 = "30863";
+  public static final String s30864 = "30864";
+  public static final String s30865 = "30865";
+  public static final String s30866 = "30866";
+  public static final String s30867 = "30867";
+  public static final String s30868 = "30868";
+  public static final String s30869 = "30869";
+  public static final String s30870 = "30870";
+  public static final String s30871 = "30871";
+  public static final String s30872 = "30872";
+  public static final String s30873 = "30873";
+  public static final String s30874 = "30874";
+  public static final String s30875 = "30875";
+  public static final String s30876 = "30876";
+  public static final String s30877 = "30877";
+  public static final String s30878 = "30878";
+  public static final String s30879 = "30879";
+  public static final String s30880 = "30880";
+  public static final String s30881 = "30881";
+  public static final String s30882 = "30882";
+  public static final String s30883 = "30883";
+  public static final String s30884 = "30884";
+  public static final String s30885 = "30885";
+  public static final String s30886 = "30886";
+  public static final String s30887 = "30887";
+  public static final String s30888 = "30888";
+  public static final String s30889 = "30889";
+  public static final String s30890 = "30890";
+  public static final String s30891 = "30891";
+  public static final String s30892 = "30892";
+  public static final String s30893 = "30893";
+  public static final String s30894 = "30894";
+  public static final String s30895 = "30895";
+  public static final String s30896 = "30896";
+  public static final String s30897 = "30897";
+  public static final String s30898 = "30898";
+  public static final String s30899 = "30899";
+  public static final String s30900 = "30900";
+  public static final String s30901 = "30901";
+  public static final String s30902 = "30902";
+  public static final String s30903 = "30903";
+  public static final String s30904 = "30904";
+  public static final String s30905 = "30905";
+  public static final String s30906 = "30906";
+  public static final String s30907 = "30907";
+  public static final String s30908 = "30908";
+  public static final String s30909 = "30909";
+  public static final String s30910 = "30910";
+  public static final String s30911 = "30911";
+  public static final String s30912 = "30912";
+  public static final String s30913 = "30913";
+  public static final String s30914 = "30914";
+  public static final String s30915 = "30915";
+  public static final String s30916 = "30916";
+  public static final String s30917 = "30917";
+  public static final String s30918 = "30918";
+  public static final String s30919 = "30919";
+  public static final String s30920 = "30920";
+  public static final String s30921 = "30921";
+  public static final String s30922 = "30922";
+  public static final String s30923 = "30923";
+  public static final String s30924 = "30924";
+  public static final String s30925 = "30925";
+  public static final String s30926 = "30926";
+  public static final String s30927 = "30927";
+  public static final String s30928 = "30928";
+  public static final String s30929 = "30929";
+  public static final String s30930 = "30930";
+  public static final String s30931 = "30931";
+  public static final String s30932 = "30932";
+  public static final String s30933 = "30933";
+  public static final String s30934 = "30934";
+  public static final String s30935 = "30935";
+  public static final String s30936 = "30936";
+  public static final String s30937 = "30937";
+  public static final String s30938 = "30938";
+  public static final String s30939 = "30939";
+  public static final String s30940 = "30940";
+  public static final String s30941 = "30941";
+  public static final String s30942 = "30942";
+  public static final String s30943 = "30943";
+  public static final String s30944 = "30944";
+  public static final String s30945 = "30945";
+  public static final String s30946 = "30946";
+  public static final String s30947 = "30947";
+  public static final String s30948 = "30948";
+  public static final String s30949 = "30949";
+  public static final String s30950 = "30950";
+  public static final String s30951 = "30951";
+  public static final String s30952 = "30952";
+  public static final String s30953 = "30953";
+  public static final String s30954 = "30954";
+  public static final String s30955 = "30955";
+  public static final String s30956 = "30956";
+  public static final String s30957 = "30957";
+  public static final String s30958 = "30958";
+  public static final String s30959 = "30959";
+  public static final String s30960 = "30960";
+  public static final String s30961 = "30961";
+  public static final String s30962 = "30962";
+  public static final String s30963 = "30963";
+  public static final String s30964 = "30964";
+  public static final String s30965 = "30965";
+  public static final String s30966 = "30966";
+  public static final String s30967 = "30967";
+  public static final String s30968 = "30968";
+  public static final String s30969 = "30969";
+  public static final String s30970 = "30970";
+  public static final String s30971 = "30971";
+  public static final String s30972 = "30972";
+  public static final String s30973 = "30973";
+  public static final String s30974 = "30974";
+  public static final String s30975 = "30975";
+  public static final String s30976 = "30976";
+  public static final String s30977 = "30977";
+  public static final String s30978 = "30978";
+  public static final String s30979 = "30979";
+  public static final String s30980 = "30980";
+  public static final String s30981 = "30981";
+  public static final String s30982 = "30982";
+  public static final String s30983 = "30983";
+  public static final String s30984 = "30984";
+  public static final String s30985 = "30985";
+  public static final String s30986 = "30986";
+  public static final String s30987 = "30987";
+  public static final String s30988 = "30988";
+  public static final String s30989 = "30989";
+  public static final String s30990 = "30990";
+  public static final String s30991 = "30991";
+  public static final String s30992 = "30992";
+  public static final String s30993 = "30993";
+  public static final String s30994 = "30994";
+  public static final String s30995 = "30995";
+  public static final String s30996 = "30996";
+  public static final String s30997 = "30997";
+  public static final String s30998 = "30998";
+  public static final String s30999 = "30999";
+  public static final String s31000 = "31000";
+  public static final String s31001 = "31001";
+  public static final String s31002 = "31002";
+  public static final String s31003 = "31003";
+  public static final String s31004 = "31004";
+  public static final String s31005 = "31005";
+  public static final String s31006 = "31006";
+  public static final String s31007 = "31007";
+  public static final String s31008 = "31008";
+  public static final String s31009 = "31009";
+  public static final String s31010 = "31010";
+  public static final String s31011 = "31011";
+  public static final String s31012 = "31012";
+  public static final String s31013 = "31013";
+  public static final String s31014 = "31014";
+  public static final String s31015 = "31015";
+  public static final String s31016 = "31016";
+  public static final String s31017 = "31017";
+  public static final String s31018 = "31018";
+  public static final String s31019 = "31019";
+  public static final String s31020 = "31020";
+  public static final String s31021 = "31021";
+  public static final String s31022 = "31022";
+  public static final String s31023 = "31023";
+  public static final String s31024 = "31024";
+  public static final String s31025 = "31025";
+  public static final String s31026 = "31026";
+  public static final String s31027 = "31027";
+  public static final String s31028 = "31028";
+  public static final String s31029 = "31029";
+  public static final String s31030 = "31030";
+  public static final String s31031 = "31031";
+  public static final String s31032 = "31032";
+  public static final String s31033 = "31033";
+  public static final String s31034 = "31034";
+  public static final String s31035 = "31035";
+  public static final String s31036 = "31036";
+  public static final String s31037 = "31037";
+  public static final String s31038 = "31038";
+  public static final String s31039 = "31039";
+  public static final String s31040 = "31040";
+  public static final String s31041 = "31041";
+  public static final String s31042 = "31042";
+  public static final String s31043 = "31043";
+  public static final String s31044 = "31044";
+  public static final String s31045 = "31045";
+  public static final String s31046 = "31046";
+  public static final String s31047 = "31047";
+  public static final String s31048 = "31048";
+  public static final String s31049 = "31049";
+  public static final String s31050 = "31050";
+  public static final String s31051 = "31051";
+  public static final String s31052 = "31052";
+  public static final String s31053 = "31053";
+  public static final String s31054 = "31054";
+  public static final String s31055 = "31055";
+  public static final String s31056 = "31056";
+  public static final String s31057 = "31057";
+  public static final String s31058 = "31058";
+  public static final String s31059 = "31059";
+  public static final String s31060 = "31060";
+  public static final String s31061 = "31061";
+  public static final String s31062 = "31062";
+  public static final String s31063 = "31063";
+  public static final String s31064 = "31064";
+  public static final String s31065 = "31065";
+  public static final String s31066 = "31066";
+  public static final String s31067 = "31067";
+  public static final String s31068 = "31068";
+  public static final String s31069 = "31069";
+  public static final String s31070 = "31070";
+  public static final String s31071 = "31071";
+  public static final String s31072 = "31072";
+  public static final String s31073 = "31073";
+  public static final String s31074 = "31074";
+  public static final String s31075 = "31075";
+  public static final String s31076 = "31076";
+  public static final String s31077 = "31077";
+  public static final String s31078 = "31078";
+  public static final String s31079 = "31079";
+  public static final String s31080 = "31080";
+  public static final String s31081 = "31081";
+  public static final String s31082 = "31082";
+  public static final String s31083 = "31083";
+  public static final String s31084 = "31084";
+  public static final String s31085 = "31085";
+  public static final String s31086 = "31086";
+  public static final String s31087 = "31087";
+  public static final String s31088 = "31088";
+  public static final String s31089 = "31089";
+  public static final String s31090 = "31090";
+  public static final String s31091 = "31091";
+  public static final String s31092 = "31092";
+  public static final String s31093 = "31093";
+  public static final String s31094 = "31094";
+  public static final String s31095 = "31095";
+  public static final String s31096 = "31096";
+  public static final String s31097 = "31097";
+  public static final String s31098 = "31098";
+  public static final String s31099 = "31099";
+  public static final String s31100 = "31100";
+  public static final String s31101 = "31101";
+  public static final String s31102 = "31102";
+  public static final String s31103 = "31103";
+  public static final String s31104 = "31104";
+  public static final String s31105 = "31105";
+  public static final String s31106 = "31106";
+  public static final String s31107 = "31107";
+  public static final String s31108 = "31108";
+  public static final String s31109 = "31109";
+  public static final String s31110 = "31110";
+  public static final String s31111 = "31111";
+  public static final String s31112 = "31112";
+  public static final String s31113 = "31113";
+  public static final String s31114 = "31114";
+  public static final String s31115 = "31115";
+  public static final String s31116 = "31116";
+  public static final String s31117 = "31117";
+  public static final String s31118 = "31118";
+  public static final String s31119 = "31119";
+  public static final String s31120 = "31120";
+  public static final String s31121 = "31121";
+  public static final String s31122 = "31122";
+  public static final String s31123 = "31123";
+  public static final String s31124 = "31124";
+  public static final String s31125 = "31125";
+  public static final String s31126 = "31126";
+  public static final String s31127 = "31127";
+  public static final String s31128 = "31128";
+  public static final String s31129 = "31129";
+  public static final String s31130 = "31130";
+  public static final String s31131 = "31131";
+  public static final String s31132 = "31132";
+  public static final String s31133 = "31133";
+  public static final String s31134 = "31134";
+  public static final String s31135 = "31135";
+  public static final String s31136 = "31136";
+  public static final String s31137 = "31137";
+  public static final String s31138 = "31138";
+  public static final String s31139 = "31139";
+  public static final String s31140 = "31140";
+  public static final String s31141 = "31141";
+  public static final String s31142 = "31142";
+  public static final String s31143 = "31143";
+  public static final String s31144 = "31144";
+  public static final String s31145 = "31145";
+  public static final String s31146 = "31146";
+  public static final String s31147 = "31147";
+  public static final String s31148 = "31148";
+  public static final String s31149 = "31149";
+  public static final String s31150 = "31150";
+  public static final String s31151 = "31151";
+  public static final String s31152 = "31152";
+  public static final String s31153 = "31153";
+  public static final String s31154 = "31154";
+  public static final String s31155 = "31155";
+  public static final String s31156 = "31156";
+  public static final String s31157 = "31157";
+  public static final String s31158 = "31158";
+  public static final String s31159 = "31159";
+  public static final String s31160 = "31160";
+  public static final String s31161 = "31161";
+  public static final String s31162 = "31162";
+  public static final String s31163 = "31163";
+  public static final String s31164 = "31164";
+  public static final String s31165 = "31165";
+  public static final String s31166 = "31166";
+  public static final String s31167 = "31167";
+  public static final String s31168 = "31168";
+  public static final String s31169 = "31169";
+  public static final String s31170 = "31170";
+  public static final String s31171 = "31171";
+  public static final String s31172 = "31172";
+  public static final String s31173 = "31173";
+  public static final String s31174 = "31174";
+  public static final String s31175 = "31175";
+  public static final String s31176 = "31176";
+  public static final String s31177 = "31177";
+  public static final String s31178 = "31178";
+  public static final String s31179 = "31179";
+  public static final String s31180 = "31180";
+  public static final String s31181 = "31181";
+  public static final String s31182 = "31182";
+  public static final String s31183 = "31183";
+  public static final String s31184 = "31184";
+  public static final String s31185 = "31185";
+  public static final String s31186 = "31186";
+  public static final String s31187 = "31187";
+  public static final String s31188 = "31188";
+  public static final String s31189 = "31189";
+  public static final String s31190 = "31190";
+  public static final String s31191 = "31191";
+  public static final String s31192 = "31192";
+  public static final String s31193 = "31193";
+  public static final String s31194 = "31194";
+  public static final String s31195 = "31195";
+  public static final String s31196 = "31196";
+  public static final String s31197 = "31197";
+  public static final String s31198 = "31198";
+  public static final String s31199 = "31199";
+  public static final String s31200 = "31200";
+  public static final String s31201 = "31201";
+  public static final String s31202 = "31202";
+  public static final String s31203 = "31203";
+  public static final String s31204 = "31204";
+  public static final String s31205 = "31205";
+  public static final String s31206 = "31206";
+  public static final String s31207 = "31207";
+  public static final String s31208 = "31208";
+  public static final String s31209 = "31209";
+  public static final String s31210 = "31210";
+  public static final String s31211 = "31211";
+  public static final String s31212 = "31212";
+  public static final String s31213 = "31213";
+  public static final String s31214 = "31214";
+  public static final String s31215 = "31215";
+  public static final String s31216 = "31216";
+  public static final String s31217 = "31217";
+  public static final String s31218 = "31218";
+  public static final String s31219 = "31219";
+  public static final String s31220 = "31220";
+  public static final String s31221 = "31221";
+  public static final String s31222 = "31222";
+  public static final String s31223 = "31223";
+  public static final String s31224 = "31224";
+  public static final String s31225 = "31225";
+  public static final String s31226 = "31226";
+  public static final String s31227 = "31227";
+  public static final String s31228 = "31228";
+  public static final String s31229 = "31229";
+  public static final String s31230 = "31230";
+  public static final String s31231 = "31231";
+  public static final String s31232 = "31232";
+  public static final String s31233 = "31233";
+  public static final String s31234 = "31234";
+  public static final String s31235 = "31235";
+  public static final String s31236 = "31236";
+  public static final String s31237 = "31237";
+  public static final String s31238 = "31238";
+  public static final String s31239 = "31239";
+  public static final String s31240 = "31240";
+  public static final String s31241 = "31241";
+  public static final String s31242 = "31242";
+  public static final String s31243 = "31243";
+  public static final String s31244 = "31244";
+  public static final String s31245 = "31245";
+  public static final String s31246 = "31246";
+  public static final String s31247 = "31247";
+  public static final String s31248 = "31248";
+  public static final String s31249 = "31249";
+  public static final String s31250 = "31250";
+  public static final String s31251 = "31251";
+  public static final String s31252 = "31252";
+  public static final String s31253 = "31253";
+  public static final String s31254 = "31254";
+  public static final String s31255 = "31255";
+  public static final String s31256 = "31256";
+  public static final String s31257 = "31257";
+  public static final String s31258 = "31258";
+  public static final String s31259 = "31259";
+  public static final String s31260 = "31260";
+  public static final String s31261 = "31261";
+  public static final String s31262 = "31262";
+  public static final String s31263 = "31263";
+  public static final String s31264 = "31264";
+  public static final String s31265 = "31265";
+  public static final String s31266 = "31266";
+  public static final String s31267 = "31267";
+  public static final String s31268 = "31268";
+  public static final String s31269 = "31269";
+  public static final String s31270 = "31270";
+  public static final String s31271 = "31271";
+  public static final String s31272 = "31272";
+  public static final String s31273 = "31273";
+  public static final String s31274 = "31274";
+  public static final String s31275 = "31275";
+  public static final String s31276 = "31276";
+  public static final String s31277 = "31277";
+  public static final String s31278 = "31278";
+  public static final String s31279 = "31279";
+  public static final String s31280 = "31280";
+  public static final String s31281 = "31281";
+  public static final String s31282 = "31282";
+  public static final String s31283 = "31283";
+  public static final String s31284 = "31284";
+  public static final String s31285 = "31285";
+  public static final String s31286 = "31286";
+  public static final String s31287 = "31287";
+  public static final String s31288 = "31288";
+  public static final String s31289 = "31289";
+  public static final String s31290 = "31290";
+  public static final String s31291 = "31291";
+  public static final String s31292 = "31292";
+  public static final String s31293 = "31293";
+  public static final String s31294 = "31294";
+  public static final String s31295 = "31295";
+  public static final String s31296 = "31296";
+  public static final String s31297 = "31297";
+  public static final String s31298 = "31298";
+  public static final String s31299 = "31299";
+  public static final String s31300 = "31300";
+  public static final String s31301 = "31301";
+  public static final String s31302 = "31302";
+  public static final String s31303 = "31303";
+  public static final String s31304 = "31304";
+  public static final String s31305 = "31305";
+  public static final String s31306 = "31306";
+  public static final String s31307 = "31307";
+  public static final String s31308 = "31308";
+  public static final String s31309 = "31309";
+  public static final String s31310 = "31310";
+  public static final String s31311 = "31311";
+  public static final String s31312 = "31312";
+  public static final String s31313 = "31313";
+  public static final String s31314 = "31314";
+  public static final String s31315 = "31315";
+  public static final String s31316 = "31316";
+  public static final String s31317 = "31317";
+  public static final String s31318 = "31318";
+  public static final String s31319 = "31319";
+  public static final String s31320 = "31320";
+  public static final String s31321 = "31321";
+  public static final String s31322 = "31322";
+  public static final String s31323 = "31323";
+  public static final String s31324 = "31324";
+  public static final String s31325 = "31325";
+  public static final String s31326 = "31326";
+  public static final String s31327 = "31327";
+  public static final String s31328 = "31328";
+  public static final String s31329 = "31329";
+  public static final String s31330 = "31330";
+  public static final String s31331 = "31331";
+  public static final String s31332 = "31332";
+  public static final String s31333 = "31333";
+  public static final String s31334 = "31334";
+  public static final String s31335 = "31335";
+  public static final String s31336 = "31336";
+  public static final String s31337 = "31337";
+  public static final String s31338 = "31338";
+  public static final String s31339 = "31339";
+  public static final String s31340 = "31340";
+  public static final String s31341 = "31341";
+  public static final String s31342 = "31342";
+  public static final String s31343 = "31343";
+  public static final String s31344 = "31344";
+  public static final String s31345 = "31345";
+  public static final String s31346 = "31346";
+  public static final String s31347 = "31347";
+  public static final String s31348 = "31348";
+  public static final String s31349 = "31349";
+  public static final String s31350 = "31350";
+  public static final String s31351 = "31351";
+  public static final String s31352 = "31352";
+  public static final String s31353 = "31353";
+  public static final String s31354 = "31354";
+  public static final String s31355 = "31355";
+  public static final String s31356 = "31356";
+  public static final String s31357 = "31357";
+  public static final String s31358 = "31358";
+  public static final String s31359 = "31359";
+  public static final String s31360 = "31360";
+  public static final String s31361 = "31361";
+  public static final String s31362 = "31362";
+  public static final String s31363 = "31363";
+  public static final String s31364 = "31364";
+  public static final String s31365 = "31365";
+  public static final String s31366 = "31366";
+  public static final String s31367 = "31367";
+  public static final String s31368 = "31368";
+  public static final String s31369 = "31369";
+  public static final String s31370 = "31370";
+  public static final String s31371 = "31371";
+  public static final String s31372 = "31372";
+  public static final String s31373 = "31373";
+  public static final String s31374 = "31374";
+  public static final String s31375 = "31375";
+  public static final String s31376 = "31376";
+  public static final String s31377 = "31377";
+  public static final String s31378 = "31378";
+  public static final String s31379 = "31379";
+  public static final String s31380 = "31380";
+  public static final String s31381 = "31381";
+  public static final String s31382 = "31382";
+  public static final String s31383 = "31383";
+  public static final String s31384 = "31384";
+  public static final String s31385 = "31385";
+  public static final String s31386 = "31386";
+  public static final String s31387 = "31387";
+  public static final String s31388 = "31388";
+  public static final String s31389 = "31389";
+  public static final String s31390 = "31390";
+  public static final String s31391 = "31391";
+  public static final String s31392 = "31392";
+  public static final String s31393 = "31393";
+  public static final String s31394 = "31394";
+  public static final String s31395 = "31395";
+  public static final String s31396 = "31396";
+  public static final String s31397 = "31397";
+  public static final String s31398 = "31398";
+  public static final String s31399 = "31399";
+  public static final String s31400 = "31400";
+  public static final String s31401 = "31401";
+  public static final String s31402 = "31402";
+  public static final String s31403 = "31403";
+  public static final String s31404 = "31404";
+  public static final String s31405 = "31405";
+  public static final String s31406 = "31406";
+  public static final String s31407 = "31407";
+  public static final String s31408 = "31408";
+  public static final String s31409 = "31409";
+  public static final String s31410 = "31410";
+  public static final String s31411 = "31411";
+  public static final String s31412 = "31412";
+  public static final String s31413 = "31413";
+  public static final String s31414 = "31414";
+  public static final String s31415 = "31415";
+  public static final String s31416 = "31416";
+  public static final String s31417 = "31417";
+  public static final String s31418 = "31418";
+  public static final String s31419 = "31419";
+  public static final String s31420 = "31420";
+  public static final String s31421 = "31421";
+  public static final String s31422 = "31422";
+  public static final String s31423 = "31423";
+  public static final String s31424 = "31424";
+  public static final String s31425 = "31425";
+  public static final String s31426 = "31426";
+  public static final String s31427 = "31427";
+  public static final String s31428 = "31428";
+  public static final String s31429 = "31429";
+  public static final String s31430 = "31430";
+  public static final String s31431 = "31431";
+  public static final String s31432 = "31432";
+  public static final String s31433 = "31433";
+  public static final String s31434 = "31434";
+  public static final String s31435 = "31435";
+  public static final String s31436 = "31436";
+  public static final String s31437 = "31437";
+  public static final String s31438 = "31438";
+  public static final String s31439 = "31439";
+  public static final String s31440 = "31440";
+  public static final String s31441 = "31441";
+  public static final String s31442 = "31442";
+  public static final String s31443 = "31443";
+  public static final String s31444 = "31444";
+  public static final String s31445 = "31445";
+  public static final String s31446 = "31446";
+  public static final String s31447 = "31447";
+  public static final String s31448 = "31448";
+  public static final String s31449 = "31449";
+  public static final String s31450 = "31450";
+  public static final String s31451 = "31451";
+  public static final String s31452 = "31452";
+  public static final String s31453 = "31453";
+  public static final String s31454 = "31454";
+  public static final String s31455 = "31455";
+  public static final String s31456 = "31456";
+  public static final String s31457 = "31457";
+  public static final String s31458 = "31458";
+  public static final String s31459 = "31459";
+  public static final String s31460 = "31460";
+  public static final String s31461 = "31461";
+  public static final String s31462 = "31462";
+  public static final String s31463 = "31463";
+  public static final String s31464 = "31464";
+  public static final String s31465 = "31465";
+  public static final String s31466 = "31466";
+  public static final String s31467 = "31467";
+  public static final String s31468 = "31468";
+  public static final String s31469 = "31469";
+  public static final String s31470 = "31470";
+  public static final String s31471 = "31471";
+  public static final String s31472 = "31472";
+  public static final String s31473 = "31473";
+  public static final String s31474 = "31474";
+  public static final String s31475 = "31475";
+  public static final String s31476 = "31476";
+  public static final String s31477 = "31477";
+  public static final String s31478 = "31478";
+  public static final String s31479 = "31479";
+  public static final String s31480 = "31480";
+  public static final String s31481 = "31481";
+  public static final String s31482 = "31482";
+  public static final String s31483 = "31483";
+  public static final String s31484 = "31484";
+  public static final String s31485 = "31485";
+  public static final String s31486 = "31486";
+  public static final String s31487 = "31487";
+  public static final String s31488 = "31488";
+  public static final String s31489 = "31489";
+  public static final String s31490 = "31490";
+  public static final String s31491 = "31491";
+  public static final String s31492 = "31492";
+  public static final String s31493 = "31493";
+  public static final String s31494 = "31494";
+  public static final String s31495 = "31495";
+  public static final String s31496 = "31496";
+  public static final String s31497 = "31497";
+  public static final String s31498 = "31498";
+  public static final String s31499 = "31499";
+  public static final String s31500 = "31500";
+  public static final String s31501 = "31501";
+  public static final String s31502 = "31502";
+  public static final String s31503 = "31503";
+  public static final String s31504 = "31504";
+  public static final String s31505 = "31505";
+  public static final String s31506 = "31506";
+  public static final String s31507 = "31507";
+  public static final String s31508 = "31508";
+  public static final String s31509 = "31509";
+  public static final String s31510 = "31510";
+  public static final String s31511 = "31511";
+  public static final String s31512 = "31512";
+  public static final String s31513 = "31513";
+  public static final String s31514 = "31514";
+  public static final String s31515 = "31515";
+  public static final String s31516 = "31516";
+  public static final String s31517 = "31517";
+  public static final String s31518 = "31518";
+  public static final String s31519 = "31519";
+  public static final String s31520 = "31520";
+  public static final String s31521 = "31521";
+  public static final String s31522 = "31522";
+  public static final String s31523 = "31523";
+  public static final String s31524 = "31524";
+  public static final String s31525 = "31525";
+  public static final String s31526 = "31526";
+  public static final String s31527 = "31527";
+  public static final String s31528 = "31528";
+  public static final String s31529 = "31529";
+  public static final String s31530 = "31530";
+  public static final String s31531 = "31531";
+  public static final String s31532 = "31532";
+  public static final String s31533 = "31533";
+  public static final String s31534 = "31534";
+  public static final String s31535 = "31535";
+  public static final String s31536 = "31536";
+  public static final String s31537 = "31537";
+  public static final String s31538 = "31538";
+  public static final String s31539 = "31539";
+  public static final String s31540 = "31540";
+  public static final String s31541 = "31541";
+  public static final String s31542 = "31542";
+  public static final String s31543 = "31543";
+  public static final String s31544 = "31544";
+  public static final String s31545 = "31545";
+  public static final String s31546 = "31546";
+  public static final String s31547 = "31547";
+  public static final String s31548 = "31548";
+  public static final String s31549 = "31549";
+  public static final String s31550 = "31550";
+  public static final String s31551 = "31551";
+  public static final String s31552 = "31552";
+  public static final String s31553 = "31553";
+  public static final String s31554 = "31554";
+  public static final String s31555 = "31555";
+  public static final String s31556 = "31556";
+  public static final String s31557 = "31557";
+  public static final String s31558 = "31558";
+  public static final String s31559 = "31559";
+  public static final String s31560 = "31560";
+  public static final String s31561 = "31561";
+  public static final String s31562 = "31562";
+  public static final String s31563 = "31563";
+  public static final String s31564 = "31564";
+  public static final String s31565 = "31565";
+  public static final String s31566 = "31566";
+  public static final String s31567 = "31567";
+  public static final String s31568 = "31568";
+  public static final String s31569 = "31569";
+  public static final String s31570 = "31570";
+  public static final String s31571 = "31571";
+  public static final String s31572 = "31572";
+  public static final String s31573 = "31573";
+  public static final String s31574 = "31574";
+  public static final String s31575 = "31575";
+  public static final String s31576 = "31576";
+  public static final String s31577 = "31577";
+  public static final String s31578 = "31578";
+  public static final String s31579 = "31579";
+  public static final String s31580 = "31580";
+  public static final String s31581 = "31581";
+  public static final String s31582 = "31582";
+  public static final String s31583 = "31583";
+  public static final String s31584 = "31584";
+  public static final String s31585 = "31585";
+  public static final String s31586 = "31586";
+  public static final String s31587 = "31587";
+  public static final String s31588 = "31588";
+  public static final String s31589 = "31589";
+  public static final String s31590 = "31590";
+  public static final String s31591 = "31591";
+  public static final String s31592 = "31592";
+  public static final String s31593 = "31593";
+  public static final String s31594 = "31594";
+  public static final String s31595 = "31595";
+  public static final String s31596 = "31596";
+  public static final String s31597 = "31597";
+  public static final String s31598 = "31598";
+  public static final String s31599 = "31599";
+  public static final String s31600 = "31600";
+  public static final String s31601 = "31601";
+  public static final String s31602 = "31602";
+  public static final String s31603 = "31603";
+  public static final String s31604 = "31604";
+  public static final String s31605 = "31605";
+  public static final String s31606 = "31606";
+  public static final String s31607 = "31607";
+  public static final String s31608 = "31608";
+  public static final String s31609 = "31609";
+  public static final String s31610 = "31610";
+  public static final String s31611 = "31611";
+  public static final String s31612 = "31612";
+  public static final String s31613 = "31613";
+  public static final String s31614 = "31614";
+  public static final String s31615 = "31615";
+  public static final String s31616 = "31616";
+  public static final String s31617 = "31617";
+  public static final String s31618 = "31618";
+  public static final String s31619 = "31619";
+  public static final String s31620 = "31620";
+  public static final String s31621 = "31621";
+  public static final String s31622 = "31622";
+  public static final String s31623 = "31623";
+  public static final String s31624 = "31624";
+  public static final String s31625 = "31625";
+  public static final String s31626 = "31626";
+  public static final String s31627 = "31627";
+  public static final String s31628 = "31628";
+  public static final String s31629 = "31629";
+  public static final String s31630 = "31630";
+  public static final String s31631 = "31631";
+  public static final String s31632 = "31632";
+  public static final String s31633 = "31633";
+  public static final String s31634 = "31634";
+  public static final String s31635 = "31635";
+  public static final String s31636 = "31636";
+  public static final String s31637 = "31637";
+  public static final String s31638 = "31638";
+  public static final String s31639 = "31639";
+  public static final String s31640 = "31640";
+  public static final String s31641 = "31641";
+  public static final String s31642 = "31642";
+  public static final String s31643 = "31643";
+  public static final String s31644 = "31644";
+  public static final String s31645 = "31645";
+  public static final String s31646 = "31646";
+  public static final String s31647 = "31647";
+  public static final String s31648 = "31648";
+  public static final String s31649 = "31649";
+  public static final String s31650 = "31650";
+  public static final String s31651 = "31651";
+  public static final String s31652 = "31652";
+  public static final String s31653 = "31653";
+  public static final String s31654 = "31654";
+  public static final String s31655 = "31655";
+  public static final String s31656 = "31656";
+  public static final String s31657 = "31657";
+  public static final String s31658 = "31658";
+  public static final String s31659 = "31659";
+  public static final String s31660 = "31660";
+  public static final String s31661 = "31661";
+  public static final String s31662 = "31662";
+  public static final String s31663 = "31663";
+  public static final String s31664 = "31664";
+  public static final String s31665 = "31665";
+  public static final String s31666 = "31666";
+  public static final String s31667 = "31667";
+  public static final String s31668 = "31668";
+  public static final String s31669 = "31669";
+  public static final String s31670 = "31670";
+  public static final String s31671 = "31671";
+  public static final String s31672 = "31672";
+  public static final String s31673 = "31673";
+  public static final String s31674 = "31674";
+  public static final String s31675 = "31675";
+  public static final String s31676 = "31676";
+  public static final String s31677 = "31677";
+  public static final String s31678 = "31678";
+  public static final String s31679 = "31679";
+  public static final String s31680 = "31680";
+  public static final String s31681 = "31681";
+  public static final String s31682 = "31682";
+  public static final String s31683 = "31683";
+  public static final String s31684 = "31684";
+  public static final String s31685 = "31685";
+  public static final String s31686 = "31686";
+  public static final String s31687 = "31687";
+  public static final String s31688 = "31688";
+  public static final String s31689 = "31689";
+  public static final String s31690 = "31690";
+  public static final String s31691 = "31691";
+  public static final String s31692 = "31692";
+  public static final String s31693 = "31693";
+  public static final String s31694 = "31694";
+  public static final String s31695 = "31695";
+  public static final String s31696 = "31696";
+  public static final String s31697 = "31697";
+  public static final String s31698 = "31698";
+  public static final String s31699 = "31699";
+  public static final String s31700 = "31700";
+  public static final String s31701 = "31701";
+  public static final String s31702 = "31702";
+  public static final String s31703 = "31703";
+  public static final String s31704 = "31704";
+  public static final String s31705 = "31705";
+  public static final String s31706 = "31706";
+  public static final String s31707 = "31707";
+  public static final String s31708 = "31708";
+  public static final String s31709 = "31709";
+  public static final String s31710 = "31710";
+  public static final String s31711 = "31711";
+  public static final String s31712 = "31712";
+  public static final String s31713 = "31713";
+  public static final String s31714 = "31714";
+  public static final String s31715 = "31715";
+  public static final String s31716 = "31716";
+  public static final String s31717 = "31717";
+  public static final String s31718 = "31718";
+  public static final String s31719 = "31719";
+  public static final String s31720 = "31720";
+  public static final String s31721 = "31721";
+  public static final String s31722 = "31722";
+  public static final String s31723 = "31723";
+  public static final String s31724 = "31724";
+  public static final String s31725 = "31725";
+  public static final String s31726 = "31726";
+  public static final String s31727 = "31727";
+  public static final String s31728 = "31728";
+  public static final String s31729 = "31729";
+  public static final String s31730 = "31730";
+  public static final String s31731 = "31731";
+  public static final String s31732 = "31732";
+  public static final String s31733 = "31733";
+  public static final String s31734 = "31734";
+  public static final String s31735 = "31735";
+  public static final String s31736 = "31736";
+  public static final String s31737 = "31737";
+  public static final String s31738 = "31738";
+  public static final String s31739 = "31739";
+  public static final String s31740 = "31740";
+  public static final String s31741 = "31741";
+  public static final String s31742 = "31742";
+  public static final String s31743 = "31743";
+  public static final String s31744 = "31744";
+  public static final String s31745 = "31745";
+  public static final String s31746 = "31746";
+  public static final String s31747 = "31747";
+  public static final String s31748 = "31748";
+  public static final String s31749 = "31749";
+  public static final String s31750 = "31750";
+  public static final String s31751 = "31751";
+  public static final String s31752 = "31752";
+  public static final String s31753 = "31753";
+  public static final String s31754 = "31754";
+  public static final String s31755 = "31755";
+  public static final String s31756 = "31756";
+  public static final String s31757 = "31757";
+  public static final String s31758 = "31758";
+  public static final String s31759 = "31759";
+  public static final String s31760 = "31760";
+  public static final String s31761 = "31761";
+  public static final String s31762 = "31762";
+  public static final String s31763 = "31763";
+  public static final String s31764 = "31764";
+  public static final String s31765 = "31765";
+  public static final String s31766 = "31766";
+  public static final String s31767 = "31767";
+  public static final String s31768 = "31768";
+  public static final String s31769 = "31769";
+  public static final String s31770 = "31770";
+  public static final String s31771 = "31771";
+  public static final String s31772 = "31772";
+  public static final String s31773 = "31773";
+  public static final String s31774 = "31774";
+  public static final String s31775 = "31775";
+  public static final String s31776 = "31776";
+  public static final String s31777 = "31777";
+  public static final String s31778 = "31778";
+  public static final String s31779 = "31779";
+  public static final String s31780 = "31780";
+  public static final String s31781 = "31781";
+  public static final String s31782 = "31782";
+  public static final String s31783 = "31783";
+  public static final String s31784 = "31784";
+  public static final String s31785 = "31785";
+  public static final String s31786 = "31786";
+  public static final String s31787 = "31787";
+  public static final String s31788 = "31788";
+  public static final String s31789 = "31789";
+  public static final String s31790 = "31790";
+  public static final String s31791 = "31791";
+  public static final String s31792 = "31792";
+  public static final String s31793 = "31793";
+  public static final String s31794 = "31794";
+  public static final String s31795 = "31795";
+  public static final String s31796 = "31796";
+  public static final String s31797 = "31797";
+  public static final String s31798 = "31798";
+  public static final String s31799 = "31799";
+  public static final String s31800 = "31800";
+  public static final String s31801 = "31801";
+  public static final String s31802 = "31802";
+  public static final String s31803 = "31803";
+  public static final String s31804 = "31804";
+  public static final String s31805 = "31805";
+  public static final String s31806 = "31806";
+  public static final String s31807 = "31807";
+  public static final String s31808 = "31808";
+  public static final String s31809 = "31809";
+  public static final String s31810 = "31810";
+  public static final String s31811 = "31811";
+  public static final String s31812 = "31812";
+  public static final String s31813 = "31813";
+  public static final String s31814 = "31814";
+  public static final String s31815 = "31815";
+  public static final String s31816 = "31816";
+  public static final String s31817 = "31817";
+  public static final String s31818 = "31818";
+  public static final String s31819 = "31819";
+  public static final String s31820 = "31820";
+  public static final String s31821 = "31821";
+  public static final String s31822 = "31822";
+  public static final String s31823 = "31823";
+  public static final String s31824 = "31824";
+  public static final String s31825 = "31825";
+  public static final String s31826 = "31826";
+  public static final String s31827 = "31827";
+  public static final String s31828 = "31828";
+  public static final String s31829 = "31829";
+  public static final String s31830 = "31830";
+  public static final String s31831 = "31831";
+  public static final String s31832 = "31832";
+  public static final String s31833 = "31833";
+  public static final String s31834 = "31834";
+  public static final String s31835 = "31835";
+  public static final String s31836 = "31836";
+  public static final String s31837 = "31837";
+  public static final String s31838 = "31838";
+  public static final String s31839 = "31839";
+  public static final String s31840 = "31840";
+  public static final String s31841 = "31841";
+  public static final String s31842 = "31842";
+  public static final String s31843 = "31843";
+  public static final String s31844 = "31844";
+  public static final String s31845 = "31845";
+  public static final String s31846 = "31846";
+  public static final String s31847 = "31847";
+  public static final String s31848 = "31848";
+  public static final String s31849 = "31849";
+  public static final String s31850 = "31850";
+  public static final String s31851 = "31851";
+  public static final String s31852 = "31852";
+  public static final String s31853 = "31853";
+  public static final String s31854 = "31854";
+  public static final String s31855 = "31855";
+  public static final String s31856 = "31856";
+  public static final String s31857 = "31857";
+  public static final String s31858 = "31858";
+  public static final String s31859 = "31859";
+  public static final String s31860 = "31860";
+  public static final String s31861 = "31861";
+  public static final String s31862 = "31862";
+  public static final String s31863 = "31863";
+  public static final String s31864 = "31864";
+  public static final String s31865 = "31865";
+  public static final String s31866 = "31866";
+  public static final String s31867 = "31867";
+  public static final String s31868 = "31868";
+  public static final String s31869 = "31869";
+  public static final String s31870 = "31870";
+  public static final String s31871 = "31871";
+  public static final String s31872 = "31872";
+  public static final String s31873 = "31873";
+  public static final String s31874 = "31874";
+  public static final String s31875 = "31875";
+  public static final String s31876 = "31876";
+  public static final String s31877 = "31877";
+  public static final String s31878 = "31878";
+  public static final String s31879 = "31879";
+  public static final String s31880 = "31880";
+  public static final String s31881 = "31881";
+  public static final String s31882 = "31882";
+  public static final String s31883 = "31883";
+  public static final String s31884 = "31884";
+  public static final String s31885 = "31885";
+  public static final String s31886 = "31886";
+  public static final String s31887 = "31887";
+  public static final String s31888 = "31888";
+  public static final String s31889 = "31889";
+  public static final String s31890 = "31890";
+  public static final String s31891 = "31891";
+  public static final String s31892 = "31892";
+  public static final String s31893 = "31893";
+  public static final String s31894 = "31894";
+  public static final String s31895 = "31895";
+  public static final String s31896 = "31896";
+  public static final String s31897 = "31897";
+  public static final String s31898 = "31898";
+  public static final String s31899 = "31899";
+  public static final String s31900 = "31900";
+  public static final String s31901 = "31901";
+  public static final String s31902 = "31902";
+  public static final String s31903 = "31903";
+  public static final String s31904 = "31904";
+  public static final String s31905 = "31905";
+  public static final String s31906 = "31906";
+  public static final String s31907 = "31907";
+  public static final String s31908 = "31908";
+  public static final String s31909 = "31909";
+  public static final String s31910 = "31910";
+  public static final String s31911 = "31911";
+  public static final String s31912 = "31912";
+  public static final String s31913 = "31913";
+  public static final String s31914 = "31914";
+  public static final String s31915 = "31915";
+  public static final String s31916 = "31916";
+  public static final String s31917 = "31917";
+  public static final String s31918 = "31918";
+  public static final String s31919 = "31919";
+  public static final String s31920 = "31920";
+  public static final String s31921 = "31921";
+  public static final String s31922 = "31922";
+  public static final String s31923 = "31923";
+  public static final String s31924 = "31924";
+  public static final String s31925 = "31925";
+  public static final String s31926 = "31926";
+  public static final String s31927 = "31927";
+  public static final String s31928 = "31928";
+  public static final String s31929 = "31929";
+  public static final String s31930 = "31930";
+  public static final String s31931 = "31931";
+  public static final String s31932 = "31932";
+  public static final String s31933 = "31933";
+  public static final String s31934 = "31934";
+  public static final String s31935 = "31935";
+  public static final String s31936 = "31936";
+  public static final String s31937 = "31937";
+  public static final String s31938 = "31938";
+  public static final String s31939 = "31939";
+  public static final String s31940 = "31940";
+  public static final String s31941 = "31941";
+  public static final String s31942 = "31942";
+  public static final String s31943 = "31943";
+  public static final String s31944 = "31944";
+  public static final String s31945 = "31945";
+  public static final String s31946 = "31946";
+  public static final String s31947 = "31947";
+  public static final String s31948 = "31948";
+  public static final String s31949 = "31949";
+  public static final String s31950 = "31950";
+  public static final String s31951 = "31951";
+  public static final String s31952 = "31952";
+  public static final String s31953 = "31953";
+  public static final String s31954 = "31954";
+  public static final String s31955 = "31955";
+  public static final String s31956 = "31956";
+  public static final String s31957 = "31957";
+  public static final String s31958 = "31958";
+  public static final String s31959 = "31959";
+  public static final String s31960 = "31960";
+  public static final String s31961 = "31961";
+  public static final String s31962 = "31962";
+  public static final String s31963 = "31963";
+  public static final String s31964 = "31964";
+  public static final String s31965 = "31965";
+  public static final String s31966 = "31966";
+  public static final String s31967 = "31967";
+  public static final String s31968 = "31968";
+  public static final String s31969 = "31969";
+  public static final String s31970 = "31970";
+  public static final String s31971 = "31971";
+  public static final String s31972 = "31972";
+  public static final String s31973 = "31973";
+  public static final String s31974 = "31974";
+  public static final String s31975 = "31975";
+  public static final String s31976 = "31976";
+  public static final String s31977 = "31977";
+  public static final String s31978 = "31978";
+  public static final String s31979 = "31979";
+  public static final String s31980 = "31980";
+  public static final String s31981 = "31981";
+  public static final String s31982 = "31982";
+  public static final String s31983 = "31983";
+  public static final String s31984 = "31984";
+  public static final String s31985 = "31985";
+  public static final String s31986 = "31986";
+  public static final String s31987 = "31987";
+  public static final String s31988 = "31988";
+  public static final String s31989 = "31989";
+  public static final String s31990 = "31990";
+  public static final String s31991 = "31991";
+  public static final String s31992 = "31992";
+  public static final String s31993 = "31993";
+  public static final String s31994 = "31994";
+  public static final String s31995 = "31995";
+  public static final String s31996 = "31996";
+  public static final String s31997 = "31997";
+  public static final String s31998 = "31998";
+  public static final String s31999 = "31999";
+  public static final String s32000 = "32000";
+  public static final String s32001 = "32001";
+  public static final String s32002 = "32002";
+  public static final String s32003 = "32003";
+  public static final String s32004 = "32004";
+  public static final String s32005 = "32005";
+  public static final String s32006 = "32006";
+  public static final String s32007 = "32007";
+  public static final String s32008 = "32008";
+  public static final String s32009 = "32009";
+  public static final String s32010 = "32010";
+  public static final String s32011 = "32011";
+  public static final String s32012 = "32012";
+  public static final String s32013 = "32013";
+  public static final String s32014 = "32014";
+  public static final String s32015 = "32015";
+  public static final String s32016 = "32016";
+  public static final String s32017 = "32017";
+  public static final String s32018 = "32018";
+  public static final String s32019 = "32019";
+  public static final String s32020 = "32020";
+  public static final String s32021 = "32021";
+  public static final String s32022 = "32022";
+  public static final String s32023 = "32023";
+  public static final String s32024 = "32024";
+  public static final String s32025 = "32025";
+  public static final String s32026 = "32026";
+  public static final String s32027 = "32027";
+  public static final String s32028 = "32028";
+  public static final String s32029 = "32029";
+  public static final String s32030 = "32030";
+  public static final String s32031 = "32031";
+  public static final String s32032 = "32032";
+  public static final String s32033 = "32033";
+  public static final String s32034 = "32034";
+  public static final String s32035 = "32035";
+  public static final String s32036 = "32036";
+  public static final String s32037 = "32037";
+  public static final String s32038 = "32038";
+  public static final String s32039 = "32039";
+  public static final String s32040 = "32040";
+  public static final String s32041 = "32041";
+  public static final String s32042 = "32042";
+  public static final String s32043 = "32043";
+  public static final String s32044 = "32044";
+  public static final String s32045 = "32045";
+  public static final String s32046 = "32046";
+  public static final String s32047 = "32047";
+  public static final String s32048 = "32048";
+  public static final String s32049 = "32049";
+  public static final String s32050 = "32050";
+  public static final String s32051 = "32051";
+  public static final String s32052 = "32052";
+  public static final String s32053 = "32053";
+  public static final String s32054 = "32054";
+  public static final String s32055 = "32055";
+  public static final String s32056 = "32056";
+  public static final String s32057 = "32057";
+  public static final String s32058 = "32058";
+  public static final String s32059 = "32059";
+  public static final String s32060 = "32060";
+  public static final String s32061 = "32061";
+  public static final String s32062 = "32062";
+  public static final String s32063 = "32063";
+  public static final String s32064 = "32064";
+  public static final String s32065 = "32065";
+  public static final String s32066 = "32066";
+  public static final String s32067 = "32067";
+  public static final String s32068 = "32068";
+  public static final String s32069 = "32069";
+  public static final String s32070 = "32070";
+  public static final String s32071 = "32071";
+  public static final String s32072 = "32072";
+  public static final String s32073 = "32073";
+  public static final String s32074 = "32074";
+  public static final String s32075 = "32075";
+  public static final String s32076 = "32076";
+  public static final String s32077 = "32077";
+  public static final String s32078 = "32078";
+  public static final String s32079 = "32079";
+  public static final String s32080 = "32080";
+  public static final String s32081 = "32081";
+  public static final String s32082 = "32082";
+  public static final String s32083 = "32083";
+  public static final String s32084 = "32084";
+  public static final String s32085 = "32085";
+  public static final String s32086 = "32086";
+  public static final String s32087 = "32087";
+  public static final String s32088 = "32088";
+  public static final String s32089 = "32089";
+  public static final String s32090 = "32090";
+  public static final String s32091 = "32091";
+  public static final String s32092 = "32092";
+  public static final String s32093 = "32093";
+  public static final String s32094 = "32094";
+  public static final String s32095 = "32095";
+  public static final String s32096 = "32096";
+  public static final String s32097 = "32097";
+  public static final String s32098 = "32098";
+  public static final String s32099 = "32099";
+  public static final String s32100 = "32100";
+  public static final String s32101 = "32101";
+  public static final String s32102 = "32102";
+  public static final String s32103 = "32103";
+  public static final String s32104 = "32104";
+  public static final String s32105 = "32105";
+  public static final String s32106 = "32106";
+  public static final String s32107 = "32107";
+  public static final String s32108 = "32108";
+  public static final String s32109 = "32109";
+  public static final String s32110 = "32110";
+  public static final String s32111 = "32111";
+  public static final String s32112 = "32112";
+  public static final String s32113 = "32113";
+  public static final String s32114 = "32114";
+  public static final String s32115 = "32115";
+  public static final String s32116 = "32116";
+  public static final String s32117 = "32117";
+  public static final String s32118 = "32118";
+  public static final String s32119 = "32119";
+  public static final String s32120 = "32120";
+  public static final String s32121 = "32121";
+  public static final String s32122 = "32122";
+  public static final String s32123 = "32123";
+  public static final String s32124 = "32124";
+  public static final String s32125 = "32125";
+  public static final String s32126 = "32126";
+  public static final String s32127 = "32127";
+  public static final String s32128 = "32128";
+  public static final String s32129 = "32129";
+  public static final String s32130 = "32130";
+  public static final String s32131 = "32131";
+  public static final String s32132 = "32132";
+  public static final String s32133 = "32133";
+  public static final String s32134 = "32134";
+  public static final String s32135 = "32135";
+  public static final String s32136 = "32136";
+  public static final String s32137 = "32137";
+  public static final String s32138 = "32138";
+  public static final String s32139 = "32139";
+  public static final String s32140 = "32140";
+  public static final String s32141 = "32141";
+  public static final String s32142 = "32142";
+  public static final String s32143 = "32143";
+  public static final String s32144 = "32144";
+  public static final String s32145 = "32145";
+  public static final String s32146 = "32146";
+  public static final String s32147 = "32147";
+  public static final String s32148 = "32148";
+  public static final String s32149 = "32149";
+  public static final String s32150 = "32150";
+  public static final String s32151 = "32151";
+  public static final String s32152 = "32152";
+  public static final String s32153 = "32153";
+  public static final String s32154 = "32154";
+  public static final String s32155 = "32155";
+  public static final String s32156 = "32156";
+  public static final String s32157 = "32157";
+  public static final String s32158 = "32158";
+  public static final String s32159 = "32159";
+  public static final String s32160 = "32160";
+  public static final String s32161 = "32161";
+  public static final String s32162 = "32162";
+  public static final String s32163 = "32163";
+  public static final String s32164 = "32164";
+  public static final String s32165 = "32165";
+  public static final String s32166 = "32166";
+  public static final String s32167 = "32167";
+  public static final String s32168 = "32168";
+  public static final String s32169 = "32169";
+  public static final String s32170 = "32170";
+  public static final String s32171 = "32171";
+  public static final String s32172 = "32172";
+  public static final String s32173 = "32173";
+  public static final String s32174 = "32174";
+  public static final String s32175 = "32175";
+  public static final String s32176 = "32176";
+  public static final String s32177 = "32177";
+  public static final String s32178 = "32178";
+  public static final String s32179 = "32179";
+  public static final String s32180 = "32180";
+  public static final String s32181 = "32181";
+  public static final String s32182 = "32182";
+  public static final String s32183 = "32183";
+  public static final String s32184 = "32184";
+  public static final String s32185 = "32185";
+  public static final String s32186 = "32186";
+  public static final String s32187 = "32187";
+  public static final String s32188 = "32188";
+  public static final String s32189 = "32189";
+  public static final String s32190 = "32190";
+  public static final String s32191 = "32191";
+  public static final String s32192 = "32192";
+  public static final String s32193 = "32193";
+  public static final String s32194 = "32194";
+  public static final String s32195 = "32195";
+  public static final String s32196 = "32196";
+  public static final String s32197 = "32197";
+  public static final String s32198 = "32198";
+  public static final String s32199 = "32199";
+  public static final String s32200 = "32200";
+  public static final String s32201 = "32201";
+  public static final String s32202 = "32202";
+  public static final String s32203 = "32203";
+  public static final String s32204 = "32204";
+  public static final String s32205 = "32205";
+  public static final String s32206 = "32206";
+  public static final String s32207 = "32207";
+  public static final String s32208 = "32208";
+  public static final String s32209 = "32209";
+  public static final String s32210 = "32210";
+  public static final String s32211 = "32211";
+  public static final String s32212 = "32212";
+  public static final String s32213 = "32213";
+  public static final String s32214 = "32214";
+  public static final String s32215 = "32215";
+  public static final String s32216 = "32216";
+  public static final String s32217 = "32217";
+  public static final String s32218 = "32218";
+  public static final String s32219 = "32219";
+  public static final String s32220 = "32220";
+  public static final String s32221 = "32221";
+  public static final String s32222 = "32222";
+  public static final String s32223 = "32223";
+  public static final String s32224 = "32224";
+  public static final String s32225 = "32225";
+  public static final String s32226 = "32226";
+  public static final String s32227 = "32227";
+  public static final String s32228 = "32228";
+  public static final String s32229 = "32229";
+  public static final String s32230 = "32230";
+  public static final String s32231 = "32231";
+  public static final String s32232 = "32232";
+  public static final String s32233 = "32233";
+  public static final String s32234 = "32234";
+  public static final String s32235 = "32235";
+  public static final String s32236 = "32236";
+  public static final String s32237 = "32237";
+  public static final String s32238 = "32238";
+  public static final String s32239 = "32239";
+  public static final String s32240 = "32240";
+  public static final String s32241 = "32241";
+  public static final String s32242 = "32242";
+  public static final String s32243 = "32243";
+  public static final String s32244 = "32244";
+  public static final String s32245 = "32245";
+  public static final String s32246 = "32246";
+  public static final String s32247 = "32247";
+  public static final String s32248 = "32248";
+  public static final String s32249 = "32249";
+  public static final String s32250 = "32250";
+  public static final String s32251 = "32251";
+  public static final String s32252 = "32252";
+  public static final String s32253 = "32253";
+  public static final String s32254 = "32254";
+  public static final String s32255 = "32255";
+  public static final String s32256 = "32256";
+  public static final String s32257 = "32257";
+  public static final String s32258 = "32258";
+  public static final String s32259 = "32259";
+  public static final String s32260 = "32260";
+  public static final String s32261 = "32261";
+  public static final String s32262 = "32262";
+  public static final String s32263 = "32263";
+  public static final String s32264 = "32264";
+  public static final String s32265 = "32265";
+  public static final String s32266 = "32266";
+  public static final String s32267 = "32267";
+  public static final String s32268 = "32268";
+  public static final String s32269 = "32269";
+  public static final String s32270 = "32270";
+  public static final String s32271 = "32271";
+  public static final String s32272 = "32272";
+  public static final String s32273 = "32273";
+  public static final String s32274 = "32274";
+  public static final String s32275 = "32275";
+  public static final String s32276 = "32276";
+  public static final String s32277 = "32277";
+  public static final String s32278 = "32278";
+  public static final String s32279 = "32279";
+  public static final String s32280 = "32280";
+  public static final String s32281 = "32281";
+  public static final String s32282 = "32282";
+  public static final String s32283 = "32283";
+  public static final String s32284 = "32284";
+  public static final String s32285 = "32285";
+  public static final String s32286 = "32286";
+  public static final String s32287 = "32287";
+  public static final String s32288 = "32288";
+  public static final String s32289 = "32289";
+  public static final String s32290 = "32290";
+  public static final String s32291 = "32291";
+  public static final String s32292 = "32292";
+  public static final String s32293 = "32293";
+  public static final String s32294 = "32294";
+  public static final String s32295 = "32295";
+  public static final String s32296 = "32296";
+  public static final String s32297 = "32297";
+  public static final String s32298 = "32298";
+  public static final String s32299 = "32299";
+  public static final String s32300 = "32300";
+  public static final String s32301 = "32301";
+  public static final String s32302 = "32302";
+  public static final String s32303 = "32303";
+  public static final String s32304 = "32304";
+  public static final String s32305 = "32305";
+  public static final String s32306 = "32306";
+  public static final String s32307 = "32307";
+  public static final String s32308 = "32308";
+  public static final String s32309 = "32309";
+  public static final String s32310 = "32310";
+  public static final String s32311 = "32311";
+  public static final String s32312 = "32312";
+  public static final String s32313 = "32313";
+  public static final String s32314 = "32314";
+  public static final String s32315 = "32315";
+  public static final String s32316 = "32316";
+  public static final String s32317 = "32317";
+  public static final String s32318 = "32318";
+  public static final String s32319 = "32319";
+  public static final String s32320 = "32320";
+  public static final String s32321 = "32321";
+  public static final String s32322 = "32322";
+  public static final String s32323 = "32323";
+  public static final String s32324 = "32324";
+  public static final String s32325 = "32325";
+  public static final String s32326 = "32326";
+  public static final String s32327 = "32327";
+  public static final String s32328 = "32328";
+  public static final String s32329 = "32329";
+  public static final String s32330 = "32330";
+  public static final String s32331 = "32331";
+  public static final String s32332 = "32332";
+  public static final String s32333 = "32333";
+  public static final String s32334 = "32334";
+  public static final String s32335 = "32335";
+  public static final String s32336 = "32336";
+  public static final String s32337 = "32337";
+  public static final String s32338 = "32338";
+  public static final String s32339 = "32339";
+  public static final String s32340 = "32340";
+  public static final String s32341 = "32341";
+  public static final String s32342 = "32342";
+  public static final String s32343 = "32343";
+  public static final String s32344 = "32344";
+  public static final String s32345 = "32345";
+  public static final String s32346 = "32346";
+  public static final String s32347 = "32347";
+  public static final String s32348 = "32348";
+  public static final String s32349 = "32349";
+  public static final String s32350 = "32350";
+  public static final String s32351 = "32351";
+  public static final String s32352 = "32352";
+  public static final String s32353 = "32353";
+  public static final String s32354 = "32354";
+  public static final String s32355 = "32355";
+  public static final String s32356 = "32356";
+  public static final String s32357 = "32357";
+  public static final String s32358 = "32358";
+  public static final String s32359 = "32359";
+  public static final String s32360 = "32360";
+  public static final String s32361 = "32361";
+  public static final String s32362 = "32362";
+  public static final String s32363 = "32363";
+  public static final String s32364 = "32364";
+  public static final String s32365 = "32365";
+  public static final String s32366 = "32366";
+  public static final String s32367 = "32367";
+  public static final String s32368 = "32368";
+  public static final String s32369 = "32369";
+  public static final String s32370 = "32370";
+  public static final String s32371 = "32371";
+  public static final String s32372 = "32372";
+  public static final String s32373 = "32373";
+  public static final String s32374 = "32374";
+  public static final String s32375 = "32375";
+  public static final String s32376 = "32376";
+  public static final String s32377 = "32377";
+  public static final String s32378 = "32378";
+  public static final String s32379 = "32379";
+  public static final String s32380 = "32380";
+  public static final String s32381 = "32381";
+  public static final String s32382 = "32382";
+  public static final String s32383 = "32383";
+  public static final String s32384 = "32384";
+  public static final String s32385 = "32385";
+  public static final String s32386 = "32386";
+  public static final String s32387 = "32387";
+  public static final String s32388 = "32388";
+  public static final String s32389 = "32389";
+  public static final String s32390 = "32390";
+  public static final String s32391 = "32391";
+  public static final String s32392 = "32392";
+  public static final String s32393 = "32393";
+  public static final String s32394 = "32394";
+  public static final String s32395 = "32395";
+  public static final String s32396 = "32396";
+  public static final String s32397 = "32397";
+  public static final String s32398 = "32398";
+  public static final String s32399 = "32399";
+  public static final String s32400 = "32400";
+  public static final String s32401 = "32401";
+  public static final String s32402 = "32402";
+  public static final String s32403 = "32403";
+  public static final String s32404 = "32404";
+  public static final String s32405 = "32405";
+  public static final String s32406 = "32406";
+  public static final String s32407 = "32407";
+  public static final String s32408 = "32408";
+  public static final String s32409 = "32409";
+  public static final String s32410 = "32410";
+  public static final String s32411 = "32411";
+  public static final String s32412 = "32412";
+  public static final String s32413 = "32413";
+  public static final String s32414 = "32414";
+  public static final String s32415 = "32415";
+  public static final String s32416 = "32416";
+  public static final String s32417 = "32417";
+  public static final String s32418 = "32418";
+  public static final String s32419 = "32419";
+  public static final String s32420 = "32420";
+  public static final String s32421 = "32421";
+  public static final String s32422 = "32422";
+  public static final String s32423 = "32423";
+  public static final String s32424 = "32424";
+  public static final String s32425 = "32425";
+  public static final String s32426 = "32426";
+  public static final String s32427 = "32427";
+  public static final String s32428 = "32428";
+  public static final String s32429 = "32429";
+  public static final String s32430 = "32430";
+  public static final String s32431 = "32431";
+  public static final String s32432 = "32432";
+  public static final String s32433 = "32433";
+  public static final String s32434 = "32434";
+  public static final String s32435 = "32435";
+  public static final String s32436 = "32436";
+  public static final String s32437 = "32437";
+  public static final String s32438 = "32438";
+  public static final String s32439 = "32439";
+  public static final String s32440 = "32440";
+  public static final String s32441 = "32441";
+  public static final String s32442 = "32442";
+  public static final String s32443 = "32443";
+  public static final String s32444 = "32444";
+  public static final String s32445 = "32445";
+  public static final String s32446 = "32446";
+  public static final String s32447 = "32447";
+  public static final String s32448 = "32448";
+  public static final String s32449 = "32449";
+  public static final String s32450 = "32450";
+  public static final String s32451 = "32451";
+  public static final String s32452 = "32452";
+  public static final String s32453 = "32453";
+  public static final String s32454 = "32454";
+  public static final String s32455 = "32455";
+  public static final String s32456 = "32456";
+  public static final String s32457 = "32457";
+  public static final String s32458 = "32458";
+  public static final String s32459 = "32459";
+  public static final String s32460 = "32460";
+  public static final String s32461 = "32461";
+  public static final String s32462 = "32462";
+  public static final String s32463 = "32463";
+  public static final String s32464 = "32464";
+  public static final String s32465 = "32465";
+  public static final String s32466 = "32466";
+  public static final String s32467 = "32467";
+  public static final String s32468 = "32468";
+  public static final String s32469 = "32469";
+  public static final String s32470 = "32470";
+  public static final String s32471 = "32471";
+  public static final String s32472 = "32472";
+  public static final String s32473 = "32473";
+  public static final String s32474 = "32474";
+  public static final String s32475 = "32475";
+  public static final String s32476 = "32476";
+  public static final String s32477 = "32477";
+  public static final String s32478 = "32478";
+  public static final String s32479 = "32479";
+  public static final String s32480 = "32480";
+  public static final String s32481 = "32481";
+  public static final String s32482 = "32482";
+  public static final String s32483 = "32483";
+  public static final String s32484 = "32484";
+  public static final String s32485 = "32485";
+  public static final String s32486 = "32486";
+  public static final String s32487 = "32487";
+  public static final String s32488 = "32488";
+  public static final String s32489 = "32489";
+  public static final String s32490 = "32490";
+  public static final String s32491 = "32491";
+  public static final String s32492 = "32492";
+  public static final String s32493 = "32493";
+  public static final String s32494 = "32494";
+  public static final String s32495 = "32495";
+  public static final String s32496 = "32496";
+  public static final String s32497 = "32497";
+  public static final String s32498 = "32498";
+  public static final String s32499 = "32499";
+  public static final String s32500 = "32500";
+  public static final String s32501 = "32501";
+  public static final String s32502 = "32502";
+  public static final String s32503 = "32503";
+  public static final String s32504 = "32504";
+  public static final String s32505 = "32505";
+  public static final String s32506 = "32506";
+  public static final String s32507 = "32507";
+  public static final String s32508 = "32508";
+  public static final String s32509 = "32509";
+  public static final String s32510 = "32510";
+  public static final String s32511 = "32511";
+  public static final String s32512 = "32512";
+  public static final String s32513 = "32513";
+  public static final String s32514 = "32514";
+  public static final String s32515 = "32515";
+  public static final String s32516 = "32516";
+  public static final String s32517 = "32517";
+  public static final String s32518 = "32518";
+  public static final String s32519 = "32519";
+  public static final String s32520 = "32520";
+  public static final String s32521 = "32521";
+  public static final String s32522 = "32522";
+  public static final String s32523 = "32523";
+  public static final String s32524 = "32524";
+  public static final String s32525 = "32525";
+  public static final String s32526 = "32526";
+  public static final String s32527 = "32527";
+  public static final String s32528 = "32528";
+  public static final String s32529 = "32529";
+  public static final String s32530 = "32530";
+  public static final String s32531 = "32531";
+  public static final String s32532 = "32532";
+  public static final String s32533 = "32533";
+  public static final String s32534 = "32534";
+  public static final String s32535 = "32535";
+  public static final String s32536 = "32536";
+  public static final String s32537 = "32537";
+  public static final String s32538 = "32538";
+  public static final String s32539 = "32539";
+  public static final String s32540 = "32540";
+  public static final String s32541 = "32541";
+  public static final String s32542 = "32542";
+  public static final String s32543 = "32543";
+  public static final String s32544 = "32544";
+  public static final String s32545 = "32545";
+  public static final String s32546 = "32546";
+  public static final String s32547 = "32547";
+  public static final String s32548 = "32548";
+  public static final String s32549 = "32549";
+  public static final String s32550 = "32550";
+  public static final String s32551 = "32551";
+  public static final String s32552 = "32552";
+  public static final String s32553 = "32553";
+  public static final String s32554 = "32554";
+  public static final String s32555 = "32555";
+  public static final String s32556 = "32556";
+  public static final String s32557 = "32557";
+  public static final String s32558 = "32558";
+  public static final String s32559 = "32559";
+  public static final String s32560 = "32560";
+  public static final String s32561 = "32561";
+  public static final String s32562 = "32562";
+  public static final String s32563 = "32563";
+  public static final String s32564 = "32564";
+  public static final String s32565 = "32565";
+  public static final String s32566 = "32566";
+  public static final String s32567 = "32567";
+  public static final String s32568 = "32568";
+  public static final String s32569 = "32569";
+  public static final String s32570 = "32570";
+  public static final String s32571 = "32571";
+  public static final String s32572 = "32572";
+  public static final String s32573 = "32573";
+  public static final String s32574 = "32574";
+  public static final String s32575 = "32575";
+  public static final String s32576 = "32576";
+  public static final String s32577 = "32577";
+  public static final String s32578 = "32578";
+  public static final String s32579 = "32579";
+  public static final String s32580 = "32580";
+  public static final String s32581 = "32581";
+  public static final String s32582 = "32582";
+  public static final String s32583 = "32583";
+  public static final String s32584 = "32584";
+  public static final String s32585 = "32585";
+  public static final String s32586 = "32586";
+  public static final String s32587 = "32587";
+  public static final String s32588 = "32588";
+  public static final String s32589 = "32589";
+  public static final String s32590 = "32590";
+  public static final String s32591 = "32591";
+  public static final String s32592 = "32592";
+  public static final String s32593 = "32593";
+  public static final String s32594 = "32594";
+  public static final String s32595 = "32595";
+  public static final String s32596 = "32596";
+  public static final String s32597 = "32597";
+  public static final String s32598 = "32598";
+  public static final String s32599 = "32599";
+  public static final String s32600 = "32600";
+  public static final String s32601 = "32601";
+  public static final String s32602 = "32602";
+  public static final String s32603 = "32603";
+  public static final String s32604 = "32604";
+  public static final String s32605 = "32605";
+  public static final String s32606 = "32606";
+  public static final String s32607 = "32607";
+  public static final String s32608 = "32608";
+  public static final String s32609 = "32609";
+  public static final String s32610 = "32610";
+  public static final String s32611 = "32611";
+  public static final String s32612 = "32612";
+  public static final String s32613 = "32613";
+  public static final String s32614 = "32614";
+  public static final String s32615 = "32615";
+  public static final String s32616 = "32616";
+  public static final String s32617 = "32617";
+  public static final String s32618 = "32618";
+  public static final String s32619 = "32619";
+  public static final String s32620 = "32620";
+  public static final String s32621 = "32621";
+  public static final String s32622 = "32622";
+  public static final String s32623 = "32623";
+  public static final String s32624 = "32624";
+  public static final String s32625 = "32625";
+  public static final String s32626 = "32626";
+  public static final String s32627 = "32627";
+  public static final String s32628 = "32628";
+  public static final String s32629 = "32629";
+  public static final String s32630 = "32630";
+  public static final String s32631 = "32631";
+  public static final String s32632 = "32632";
+  public static final String s32633 = "32633";
+  public static final String s32634 = "32634";
+  public static final String s32635 = "32635";
+  public static final String s32636 = "32636";
+  public static final String s32637 = "32637";
+  public static final String s32638 = "32638";
+  public static final String s32639 = "32639";
+  public static final String s32640 = "32640";
+  public static final String s32641 = "32641";
+  public static final String s32642 = "32642";
+  public static final String s32643 = "32643";
+  public static final String s32644 = "32644";
+  public static final String s32645 = "32645";
+  public static final String s32646 = "32646";
+  public static final String s32647 = "32647";
+  public static final String s32648 = "32648";
+  public static final String s32649 = "32649";
+  public static final String s32650 = "32650";
+  public static final String s32651 = "32651";
+  public static final String s32652 = "32652";
+  public static final String s32653 = "32653";
+  public static final String s32654 = "32654";
+  public static final String s32655 = "32655";
+  public static final String s32656 = "32656";
+  public static final String s32657 = "32657";
+  public static final String s32658 = "32658";
+  public static final String s32659 = "32659";
+  public static final String s32660 = "32660";
+  public static final String s32661 = "32661";
+  public static final String s32662 = "32662";
+  public static final String s32663 = "32663";
+  public static final String s32664 = "32664";
+  public static final String s32665 = "32665";
+  public static final String s32666 = "32666";
+  public static final String s32667 = "32667";
+  public static final String s32668 = "32668";
+  public static final String s32669 = "32669";
+  public static final String s32670 = "32670";
+  public static final String s32671 = "32671";
+  public static final String s32672 = "32672";
+  public static final String s32673 = "32673";
+  public static final String s32674 = "32674";
+  public static final String s32675 = "32675";
+  public static final String s32676 = "32676";
+  public static final String s32677 = "32677";
+  public static final String s32678 = "32678";
+  public static final String s32679 = "32679";
+  public static final String s32680 = "32680";
+  public static final String s32681 = "32681";
+  public static final String s32682 = "32682";
+  public static final String s32683 = "32683";
+  public static final String s32684 = "32684";
+  public static final String s32685 = "32685";
+  public static final String s32686 = "32686";
+  public static final String s32687 = "32687";
+  public static final String s32688 = "32688";
+  public static final String s32689 = "32689";
+  public static final String s32690 = "32690";
+  public static final String s32691 = "32691";
+  public static final String s32692 = "32692";
+  public static final String s32693 = "32693";
+  public static final String s32694 = "32694";
+  public static final String s32695 = "32695";
+  public static final String s32696 = "32696";
+  public static final String s32697 = "32697";
+  public static final String s32698 = "32698";
+  public static final String s32699 = "32699";
+  public static final String s32700 = "32700";
+  public static final String s32701 = "32701";
+  public static final String s32702 = "32702";
+  public static final String s32703 = "32703";
+  public static final String s32704 = "32704";
+  public static final String s32705 = "32705";
+  public static final String s32706 = "32706";
+  public static final String s32707 = "32707";
+  public static final String s32708 = "32708";
+  public static final String s32709 = "32709";
+  public static final String s32710 = "32710";
+  public static final String s32711 = "32711";
+  public static final String s32712 = "32712";
+  public static final String s32713 = "32713";
+  public static final String s32714 = "32714";
+  public static final String s32715 = "32715";
+  public static final String s32716 = "32716";
+  public static final String s32717 = "32717";
+  public static final String s32718 = "32718";
+  public static final String s32719 = "32719";
+  public static final String s32720 = "32720";
+  public static final String s32721 = "32721";
+  public static final String s32722 = "32722";
+  public static final String s32723 = "32723";
+  public static final String s32724 = "32724";
+  public static final String s32725 = "32725";
+  public static final String s32726 = "32726";
+  public static final String s32727 = "32727";
+  public static final String s32728 = "32728";
+  public static final String s32729 = "32729";
+  public static final String s32730 = "32730";
+  public static final String s32731 = "32731";
+  public static final String s32732 = "32732";
+  public static final String s32733 = "32733";
+  public static final String s32734 = "32734";
+  public static final String s32735 = "32735";
+  public static final String s32736 = "32736";
+  public static final String s32737 = "32737";
+  public static final String s32738 = "32738";
+  public static final String s32739 = "32739";
+  public static final String s32740 = "32740";
+  public static final String s32741 = "32741";
+  public static final String s32742 = "32742";
+  public static final String s32743 = "32743";
+  public static final String s32744 = "32744";
+  public static final String s32745 = "32745";
+  public static final String s32746 = "32746";
+  public static final String s32747 = "32747";
+  public static final String s32748 = "32748";
+  public static final String s32749 = "32749";
+  public static final String s32750 = "32750";
+  public static final String s32751 = "32751";
+  public static final String s32752 = "32752";
+  public static final String s32753 = "32753";
+  public static final String s32754 = "32754";
+  public static final String s32755 = "32755";
+  public static final String s32756 = "32756";
+  public static final String s32757 = "32757";
+  public static final String s32758 = "32758";
+  public static final String s32759 = "32759";
+  public static final String s32760 = "32760";
+  public static final String s32761 = "32761";
+  public static final String s32762 = "32762";
+  public static final String s32763 = "32763";
+  public static final String s32764 = "32764";
+  public static final String s32765 = "32765";
+  public static final String s32766 = "32766";
+  public static final String s32767 = "32767";
+}
diff --git a/src/test/examples/loadconst/LoadConst.java b/src/test/examples/loadconst/LoadConst.java
new file mode 100644
index 0000000..7ba7f30
--- /dev/null
+++ b/src/test/examples/loadconst/LoadConst.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package loadconst;
+
+public class LoadConst {
+
+  public static void main(String[] args) {
+    // ldc int
+    System.out.print(0x7fffffff);
+    // ldc float
+    System.out.print(1234.5);
+    // ldc string
+    System.out.println("foobar");
+    // ldc const class
+    System.out.println(LoadConst.class);
+  }
+}
diff --git a/src/test/examples/memberrebinding/ClassAtBottomOfChain.java b/src/test/examples/memberrebinding/ClassAtBottomOfChain.java
new file mode 100644
index 0000000..38e5d50
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassAtBottomOfChain.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+public class ClassAtBottomOfChain extends EssentiallyEmptyClass {
+
+  @Override
+  public void superCallsProperlyPropagate() {
+    System.out.println("Try invoke on super, aka ClassInMiddleOfChain");
+    super.superCallsProperlyPropagate();
+  }
+
+  public void methodThatCallsSuperCallsProperlyPropagateTwo() {
+    // Invoke the method on the superclass even though this class does not override it.
+    super.superCallsProperlyPropagateTwo();
+  }
+
+  // Method with same name but different signature to test lookup.
+  public void methodThatShadowsPrivate(int ignore) {
+
+  }
+}
diff --git a/src/test/examples/memberrebinding/ClassExtendsLibraryClass.java b/src/test/examples/memberrebinding/ClassExtendsLibraryClass.java
new file mode 100644
index 0000000..80e5a30
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassExtendsLibraryClass.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ClassExtendsLibraryClass extends ArrayList<String> {
+
+  public void methodThatAddsHelloWorld() {
+    // call add on this type, so it gets rebound.
+    add("hello");
+    // call it on list to make the methodid available.
+    addOnArrayList(this, "world");
+  }
+
+  private static <T> void addOnArrayList(ArrayList<T> list, T item) {
+    list.add(item);
+  }
+
+  public void methodThatAddsHelloWorldUsingAddAll() {
+    // call this only on this type, so that it cannot be rebound to the interface.
+    String[] words = new String[]{"hello", "world"};
+    addAll(Arrays.asList(words));
+  }
+
+}
diff --git a/src/test/examples/memberrebinding/ClassExtendsOtherLibraryClass.java b/src/test/examples/memberrebinding/ClassExtendsOtherLibraryClass.java
new file mode 100644
index 0000000..ab138fc
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassExtendsOtherLibraryClass.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+import memberrebindinglib.AnIndependentInterface;
+
+public class ClassExtendsOtherLibraryClass extends
+    SuperClassOfClassExtendsOtherLibraryClass implements AnIndependentInterface {
+
+  @Override
+  public boolean aMethodThatReturnsTrue() {
+    return true;
+  }
+
+  @Override
+  public boolean aMethodThatReturnsFalse() {
+    return false;
+  }
+}
diff --git a/src/test/examples/memberrebinding/ClassInMiddleOfChain.java b/src/test/examples/memberrebinding/ClassInMiddleOfChain.java
new file mode 100644
index 0000000..8cf34b2
--- /dev/null
+++ b/src/test/examples/memberrebinding/ClassInMiddleOfChain.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+public class ClassInMiddleOfChain extends SuperClassOfAll {
+
+  @Override
+  public void superCallsProperlyPropagate() {
+    // Do not! call super here to break the super-chain.
+    System.out.println("Invoked superCallsProperlyPropagate on ClassInMiddleOfChain.");
+  }
+
+  @Override
+  public void superCallsProperlyPropagateTwo() {
+    // Do not! call super here to break the super-chain.
+    System.out.println("Invoked superCallsProperlyPropagateTwo on ClassInMiddleOfChain.");
+  }
+
+  public void methodThatShadowsPrivate() {
+    System.out.println("methodThatShadowsPrivate on ClassInMiddleOfChain");
+  }
+}
diff --git a/src/test/examples/memberrebinding/EssentiallyEmptyClass.java b/src/test/examples/memberrebinding/EssentiallyEmptyClass.java
new file mode 100644
index 0000000..43403b3
--- /dev/null
+++ b/src/test/examples/memberrebinding/EssentiallyEmptyClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+public class EssentiallyEmptyClass extends ClassInMiddleOfChain {
+
+}
diff --git a/src/test/examples/memberrebinding/SuperClassOfAll.java b/src/test/examples/memberrebinding/SuperClassOfAll.java
new file mode 100644
index 0000000..f986584
--- /dev/null
+++ b/src/test/examples/memberrebinding/SuperClassOfAll.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'memberrebinging.dex' is what is run.
+
+package memberrebinding;
+
+public class SuperClassOfAll {
+
+  public int superField;
+
+  public void superCallsProperlyPropagate() {
+    System.out.println("Invoked superCallsProperlyPropagate on SuperClassOfAll.");
+  }
+
+  public void superCallsProperlyPropagateTwo() {
+    System.out.println("Invoked superCallsProperlyPropagateTwo on SuperClassOfAll.");
+  }
+
+  private void methodThatShadowsPrivate() {
+    System.out.println("methodThatShadowsPrivate on SuperClassOfAll");
+  }
+
+  public void ensureAllCalled() {
+    methodThatShadowsPrivate();
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding/SuperClassOfClassExtendsOtherLibraryClass.java b/src/test/examples/memberrebinding/SuperClassOfClassExtendsOtherLibraryClass.java
new file mode 100644
index 0000000..03f1bdc
--- /dev/null
+++ b/src/test/examples/memberrebinding/SuperClassOfClassExtendsOtherLibraryClass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+import memberrebindinglib.ImplementedInProgramClass;
+import memberrebindinglib.SubClass;
+
+public abstract class SuperClassOfClassExtendsOtherLibraryClass extends SubClass implements
+    ImplementedInProgramClass {
+  public int aMethodThatReturnsFour() {
+    return 4;
+  }
+}
diff --git a/src/test/examples/memberrebinding/Test.java b/src/test/examples/memberrebinding/Test.java
new file mode 100644
index 0000000..ba5e2f0
--- /dev/null
+++ b/src/test/examples/memberrebinding/Test.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding;
+
+import memberrebinding.subpackage.PublicClass;
+import memberrebindinglib.AnIndependentInterface;
+
+public class Test {
+
+  public static void main(String[] args) {
+    ClassAtBottomOfChain bottomInstance = new ClassAtBottomOfChain();
+    bottomInstance.superCallsProperlyPropagate();
+    bottomInstance.methodThatCallsSuperCallsProperlyPropagateTwo();
+    bottomInstance.methodThatShadowsPrivate();
+    bottomInstance.ensureAllCalled();
+    System.out.println(bottomInstance.superField);
+    ClassExtendsLibraryClass classExtendsLibraryClass = new ClassExtendsLibraryClass();
+    classExtendsLibraryClass.methodThatAddsHelloWorld();
+    classExtendsLibraryClass.methodThatAddsHelloWorldUsingAddAll();
+    System.out.println(classExtendsLibraryClass.get(0));
+    System.out.println(classExtendsLibraryClass.get(1));
+    System.out.println(classExtendsLibraryClass.get(2));
+    PublicClass instance = new PublicClass();
+    instance.aMethod();
+    ClassExtendsOtherLibraryClass classExtendsOther = new ClassExtendsOtherLibraryClass();
+    System.out.println(classExtendsOther.aMethodThatReturnsOne());
+    System.out.println(classExtendsOther.aMethodThatReturnsTwo());
+    System.out.println(classExtendsOther.aMethodThatReturnsThree());
+    System.out.println(classExtendsOther.aMethodThatReturnsFour());
+    AnIndependentInterface iface = classExtendsOther;
+    System.out.println(iface.aMethodThatReturnsTwo());
+    SuperClassOfClassExtendsOtherLibraryClass superClass = classExtendsOther;
+    System.out.println(superClass.aMethodThatReturnsTrue());
+    System.out.println(superClass.aMethodThatReturnsFalse());
+  }
+}
diff --git a/src/test/examples/memberrebinding/package.map b/src/test/examples/memberrebinding/package.map
new file mode 100644
index 0000000..e9bfd68
--- /dev/null
+++ b/src/test/examples/memberrebinding/package.map
@@ -0,0 +1,4 @@
+#
+# package.map file used by java/com/android/tools/r8/dex/ExtraFileTest.java
+#
+memberrebinding.subpackage:1
diff --git a/src/test/examples/memberrebinding/proguard.map b/src/test/examples/memberrebinding/proguard.map
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/examples/memberrebinding/proguard.map
diff --git a/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java b/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
new file mode 100644
index 0000000..d749fb1
--- /dev/null
+++ b/src/test/examples/memberrebinding/subpackage/PackagePrivateClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding.subpackage;
+
+abstract class PackagePrivateClass {
+
+  public final void aMethod() {
+    System.out.println("Hello, I am aMethod");
+  }
+}
diff --git a/src/test/examples/memberrebinding/subpackage/PublicClass.java b/src/test/examples/memberrebinding/subpackage/PublicClass.java
new file mode 100644
index 0000000..df3a92d
--- /dev/null
+++ b/src/test/examples/memberrebinding/subpackage/PublicClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding.subpackage;
+
+public class PublicClass extends PackagePrivateClass {
+  // Intentionally left empty.
+}
diff --git a/src/test/examples/memberrebinding2/ClassAtBottomOfChain.java b/src/test/examples/memberrebinding2/ClassAtBottomOfChain.java
new file mode 100644
index 0000000..22ea072
--- /dev/null
+++ b/src/test/examples/memberrebinding2/ClassAtBottomOfChain.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2;
+
+public class ClassAtBottomOfChain extends EssentiallyEmptyClass {
+
+  public static int staticBottomField;
+
+  public int bottomField;
+}
diff --git a/src/test/examples/memberrebinding2/ClassExtendsLibraryClass.java b/src/test/examples/memberrebinding2/ClassExtendsLibraryClass.java
new file mode 100644
index 0000000..9094360
--- /dev/null
+++ b/src/test/examples/memberrebinding2/ClassExtendsLibraryClass.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class ClassExtendsLibraryClass extends ArrayList<String> {
+
+  private static <T> void addOnArrayList(ArrayList<T> list, T item) {
+    list.add(item);
+  }
+
+  public void methodThatAddsHelloWorldUsingAddAll() {
+    // call this only on this type, so that it cannot be rebound to the interface.
+    String[] words = new String[]{"hello", "world"};
+    addAll(Arrays.asList(words));
+  }
+
+}
diff --git a/src/test/examples/memberrebinding2/ClassInMiddleOfChain.java b/src/test/examples/memberrebinding2/ClassInMiddleOfChain.java
new file mode 100644
index 0000000..209abf7
--- /dev/null
+++ b/src/test/examples/memberrebinding2/ClassInMiddleOfChain.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2;
+
+public class ClassInMiddleOfChain extends SuperClassOfAll {
+
+  public static int staticMiddleField;
+
+  public int middleField;
+}
diff --git a/src/test/examples/memberrebinding2/EssentiallyEmptyClass.java b/src/test/examples/memberrebinding2/EssentiallyEmptyClass.java
new file mode 100644
index 0000000..4da56b5
--- /dev/null
+++ b/src/test/examples/memberrebinding2/EssentiallyEmptyClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2;
+
+public class EssentiallyEmptyClass extends ClassInMiddleOfChain {
+
+}
diff --git a/src/test/examples/memberrebinding2/SuperClassOfAll.java b/src/test/examples/memberrebinding2/SuperClassOfAll.java
new file mode 100644
index 0000000..fa4156f
--- /dev/null
+++ b/src/test/examples/memberrebinding2/SuperClassOfAll.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'memberrebinging.dex' is what is run.
+
+package memberrebinding2;
+
+public class SuperClassOfAll {
+
+  public static int staticSuperField;
+
+  public int superField;
+}
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding2/Test.java b/src/test/examples/memberrebinding2/Test.java
new file mode 100644
index 0000000..d4b005e
--- /dev/null
+++ b/src/test/examples/memberrebinding2/Test.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2;
+
+import memberrebinding2.subpackage.PublicClass;
+
+public class Test {
+
+  public static void main(String[] args) {
+    ClassAtBottomOfChain bottomInstance = new ClassAtBottomOfChain();
+    PublicClass instance = new PublicClass();
+
+    int x = 0;
+
+    bottomInstance.bottomField = 1;
+    bottomInstance.middleField = 2;
+    bottomInstance.superField = 3;
+    instance.field = 4;
+
+    bottomInstance.staticBottomField = 5;
+    bottomInstance.staticMiddleField = 6;
+    bottomInstance.staticSuperField = 7;
+    instance.staticField = 8;
+
+    x += bottomInstance.bottomField;
+    x += bottomInstance.middleField;
+    x += bottomInstance.superField;
+    x += instance.field;
+
+    x += bottomInstance.staticBottomField;
+    x += bottomInstance.staticMiddleField;
+    x += bottomInstance.staticSuperField;
+    x += instance.staticField;
+
+    System.out.println(x);
+  }
+}
diff --git a/src/test/examples/memberrebinding2/subpackage/PackagePrivateClass.java b/src/test/examples/memberrebinding2/subpackage/PackagePrivateClass.java
new file mode 100644
index 0000000..733776c
--- /dev/null
+++ b/src/test/examples/memberrebinding2/subpackage/PackagePrivateClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2.subpackage;
+
+abstract class PackagePrivateClass {
+
+  public static int staticField;
+
+  public int field;
+}
diff --git a/src/test/examples/memberrebinding2/subpackage/PublicClass.java b/src/test/examples/memberrebinding2/subpackage/PublicClass.java
new file mode 100644
index 0000000..070bdce
--- /dev/null
+++ b/src/test/examples/memberrebinding2/subpackage/PublicClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding2.subpackage;
+
+public class PublicClass extends PackagePrivateClass {
+  // Intentionally left empty.
+}
diff --git a/src/test/examples/memberrebinding3/ClassAtBottomOfChain.java b/src/test/examples/memberrebinding3/ClassAtBottomOfChain.java
new file mode 100644
index 0000000..8607f81
--- /dev/null
+++ b/src/test/examples/memberrebinding3/ClassAtBottomOfChain.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding3;
+
+public class ClassAtBottomOfChain extends EssentiallyEmptyClass {
+
+  void bottomMethod() {
+
+  }
+}
diff --git a/src/test/examples/memberrebinding3/ClassInMiddleOfChain.java b/src/test/examples/memberrebinding3/ClassInMiddleOfChain.java
new file mode 100644
index 0000000..6e763c0
--- /dev/null
+++ b/src/test/examples/memberrebinding3/ClassInMiddleOfChain.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding3;
+
+public class ClassInMiddleOfChain extends SuperClassOfAll {
+
+  void middleMethod() {
+
+  }
+}
diff --git a/src/test/examples/memberrebinding3/EssentiallyEmptyClass.java b/src/test/examples/memberrebinding3/EssentiallyEmptyClass.java
new file mode 100644
index 0000000..810fe0a
--- /dev/null
+++ b/src/test/examples/memberrebinding3/EssentiallyEmptyClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding3;
+
+public class EssentiallyEmptyClass extends ClassInMiddleOfChain {
+
+}
diff --git a/src/test/examples/memberrebinding3/SuperClassOfAll.java b/src/test/examples/memberrebinding3/SuperClassOfAll.java
new file mode 100644
index 0000000..b34d6c7
--- /dev/null
+++ b/src/test/examples/memberrebinding3/SuperClassOfAll.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'memberrebinging.dex' is what is run.
+
+package memberrebinding3;
+
+public class SuperClassOfAll {
+
+  void topMethod() {
+
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/memberrebinding3/Test.java b/src/test/examples/memberrebinding3/Test.java
new file mode 100644
index 0000000..d37f263
--- /dev/null
+++ b/src/test/examples/memberrebinding3/Test.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebinding3;
+
+public class Test extends ClassAtBottomOfChain {
+
+  void bottomMethod() {
+
+  }
+
+  void middleMethod() {
+
+  }
+
+  void topMethod() {
+
+  }
+
+  private void test() {
+    super.bottomMethod();
+    super.middleMethod();
+    super.topMethod();
+  }
+
+  public static void main(String[] args) {
+    new Test().test();
+  }
+}
diff --git a/src/test/examples/memberrebindinglib/AnIndependentInterface.java b/src/test/examples/memberrebindinglib/AnIndependentInterface.java
new file mode 100644
index 0000000..01f018a
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/AnIndependentInterface.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebindinglib;
+
+public interface AnIndependentInterface {
+  public int aMethodThatReturnsTwo();
+}
diff --git a/src/test/examples/memberrebindinglib/ImplementedInLibrary.java b/src/test/examples/memberrebindinglib/ImplementedInLibrary.java
new file mode 100644
index 0000000..786cc9e
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/ImplementedInLibrary.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebindinglib;
+
+public interface ImplementedInLibrary {
+  boolean aMethodThatReturnsTrue();
+}
diff --git a/src/test/examples/memberrebindinglib/ImplementedInProgramClass.java b/src/test/examples/memberrebindinglib/ImplementedInProgramClass.java
new file mode 100644
index 0000000..3c6ec33
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/ImplementedInProgramClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebindinglib;
+
+public interface ImplementedInProgramClass {
+  boolean aMethodThatReturnsFalse();
+}
diff --git a/src/test/examples/memberrebindinglib/SubClass.java b/src/test/examples/memberrebindinglib/SubClass.java
new file mode 100644
index 0000000..175053f
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/SubClass.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebindinglib;
+
+public abstract class SubClass extends SuperClass implements ImplementedInLibrary {
+  public int aMethodThatReturnsOne() {
+    return 1;
+  }
+
+  public int aMethodThatReturnsTwo() {
+    return 2;
+  }
+
+  public int aMethodThatReturnsFour() {
+    return 4;
+  }
+}
diff --git a/src/test/examples/memberrebindinglib/SuperClass.java b/src/test/examples/memberrebindinglib/SuperClass.java
new file mode 100644
index 0000000..c5818b4
--- /dev/null
+++ b/src/test/examples/memberrebindinglib/SuperClass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package memberrebindinglib;
+
+public class SuperClass {
+  public int aMethodThatReturnsTwo() {
+    return 2;
+  }
+
+  public int aMethodThatReturnsThree() {
+    return 3;
+  }
+}
diff --git a/src/test/examples/minification/ClassD.java b/src/test/examples/minification/ClassD.java
new file mode 100644
index 0000000..9804afc
--- /dev/null
+++ b/src/test/examples/minification/ClassD.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class ClassD implements InterfaceD {
+
+  @Override
+  public int anotherFunctionFromIntToInt(int arg) {
+    System.out.println("ClassD:anotherFunctionFromIntToInt");
+    return arg;
+  }
+
+  @Override
+  public int functionFromIntToInt(int arg) {
+    System.out.println("ClassD:functionFromIntToInt");
+    return arg;
+  }
+}
diff --git a/src/test/examples/minification/EmptyInterface.java b/src/test/examples/minification/EmptyInterface.java
new file mode 100644
index 0000000..1fda851
--- /dev/null
+++ b/src/test/examples/minification/EmptyInterface.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public interface EmptyInterface {
+
+}
diff --git a/src/test/examples/minification/InterfaceA.java b/src/test/examples/minification/InterfaceA.java
new file mode 100644
index 0000000..922ea3d
--- /dev/null
+++ b/src/test/examples/minification/InterfaceA.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public interface InterfaceA {
+
+  /**
+   * This method is also defined in {@link InterfaceB} and {@link InterfaceD}.
+   */
+  int functionFromIntToInt(int arg);
+
+  /**
+   * This signature is only defined here.
+   */
+  int uniqueLittleMethodInA();
+}
diff --git a/src/test/examples/minification/InterfaceB.java b/src/test/examples/minification/InterfaceB.java
new file mode 100644
index 0000000..ceddaa7
--- /dev/null
+++ b/src/test/examples/minification/InterfaceB.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public interface InterfaceB extends EmptyInterface {
+
+  /**
+   * This method is also defined in {@link InterfaceA} and {@link InterfaceD};
+   */
+  int functionFromIntToInt(int arg);
+
+  /**
+   * This signature is only defined here.
+   */
+  int uniqueLittleMethodInB();
+}
diff --git a/src/test/examples/minification/InterfaceC.java b/src/test/examples/minification/InterfaceC.java
new file mode 100644
index 0000000..e6c4917
--- /dev/null
+++ b/src/test/examples/minification/InterfaceC.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public interface InterfaceC extends InterfaceB {
+
+  /**
+   * This signature is only defined here.
+   */
+  int uniqueLittleMethodInC();
+}
diff --git a/src/test/examples/minification/InterfaceD.java b/src/test/examples/minification/InterfaceD.java
new file mode 100644
index 0000000..ec60f06
--- /dev/null
+++ b/src/test/examples/minification/InterfaceD.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public interface InterfaceD {
+
+  /**
+   * This method is only defined here.
+   */
+  int anotherFunctionFromIntToInt(int arg);
+
+  /**
+   * This method is also defined in {@link InterfaceA} and {@link InterfaceB};
+   */
+  int functionFromIntToInt(int arg);
+}
diff --git a/src/test/examples/minification/Minification.java b/src/test/examples/minification/Minification.java
new file mode 100644
index 0000000..b5f2f6e
--- /dev/null
+++ b/src/test/examples/minification/Minification.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class Minification {
+
+  public static void main(String[] args) {
+    SubClassA subClassA = new SubClassA();
+    exerciseA(subClassA);
+    SubSubClassAB subSubClassAB = new SubSubClassAB();
+    exerciseA(subSubClassAB);
+    exerciseB(subSubClassAB);
+    SubClassB subClassB = new SubClassB();
+    exerciseB(subClassB);
+    SubClassC subClassC = new SubClassC();
+    exerciseB(subClassC);
+    exerciseC(subClassC);
+    ClassD classD = new ClassD();
+    exerciseD(classD);
+  }
+
+  private static void exerciseA(InterfaceA thing) {
+    thing.functionFromIntToInt(thing.uniqueLittleMethodInA());
+  }
+
+  private static void exerciseB(InterfaceB thing) {
+    thing.functionFromIntToInt(thing.uniqueLittleMethodInB());
+  }
+
+  private static void exerciseC(InterfaceC thing) {
+    thing.functionFromIntToInt(thing.uniqueLittleMethodInC());
+  }
+
+  private static void exerciseD(InterfaceD thing) {
+    thing.anotherFunctionFromIntToInt(42);
+    thing.functionFromIntToInt(42);
+  }
+}
diff --git a/src/test/examples/minification/SubClassA.java b/src/test/examples/minification/SubClassA.java
new file mode 100644
index 0000000..b856f04
--- /dev/null
+++ b/src/test/examples/minification/SubClassA.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class SubClassA extends SuperClassA {
+
+  @Override
+  public int functionFromIntToInt(int arg) {
+    System.out.println("SubClassA:functionFromIntToInt(A)");
+    return arg;
+  }
+
+  @Override
+  public int uniqueLittleMethodInA() {
+    System.out.println("SubClassA:uniqueLittleMethodInA");
+    return 0;
+  }
+}
diff --git a/src/test/examples/minification/SubClassB.java b/src/test/examples/minification/SubClassB.java
new file mode 100644
index 0000000..663c87c
--- /dev/null
+++ b/src/test/examples/minification/SubClassB.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class SubClassB implements InterfaceB {
+
+  @Override
+  public int functionFromIntToInt(int arg) {
+    System.out.println("SubClassB.functionFromIntToInt");
+    return arg;
+  }
+
+  @Override
+  public int uniqueLittleMethodInB() {
+    System.out.println("SubClassB.uniqueLittleMethodInB");
+    return 0;
+  }
+}
diff --git a/src/test/examples/minification/SubClassC.java b/src/test/examples/minification/SubClassC.java
new file mode 100644
index 0000000..bdffc0b
--- /dev/null
+++ b/src/test/examples/minification/SubClassC.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class SubClassC extends SuperClassC implements InterfaceC {
+
+  @Override
+  public int functionFromIntToInt(int arg) {
+    System.out.println("SubClassC:functionFromIntToInt");
+    return arg;
+  }
+
+  @Override
+  public int uniqueLittleMethodInB() {
+    System.out.println("SubClassC:uniqueLittleMethodInB");
+    return 0;
+  }
+
+  @Override
+  public int uniqueLittleMethodInC() {
+    System.out.println("SubClassC:uniqueLittleMethodInC");
+    return 0;
+  }
+}
diff --git a/src/test/examples/minification/SubSubClassAB.java b/src/test/examples/minification/SubSubClassAB.java
new file mode 100644
index 0000000..4e7fa41
--- /dev/null
+++ b/src/test/examples/minification/SubSubClassAB.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class SubSubClassAB extends SubClassA implements InterfaceB {
+
+  @Override
+  public int functionFromIntToInt(int arg) {
+    System.out.println("SubSubClassAB:functionFromIntToInt");
+    return super.functionFromIntToInt(arg);
+  }
+
+  @Override
+  public int uniqueLittleMethodInB() {
+    System.out.println("SubSubClassAB:uniqueLittleMethodInB");
+    return 0;
+  }
+}
diff --git a/src/test/examples/minification/SuperClassA.java b/src/test/examples/minification/SuperClassA.java
new file mode 100644
index 0000000..99dfe22
--- /dev/null
+++ b/src/test/examples/minification/SuperClassA.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public abstract class SuperClassA implements InterfaceA {
+
+}
diff --git a/src/test/examples/minification/SuperClassB.java b/src/test/examples/minification/SuperClassB.java
new file mode 100644
index 0000000..7dd134f
--- /dev/null
+++ b/src/test/examples/minification/SuperClassB.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class SuperClassB {
+
+}
diff --git a/src/test/examples/minification/SuperClassC.java b/src/test/examples/minification/SuperClassC.java
new file mode 100644
index 0000000..156e863
--- /dev/null
+++ b/src/test/examples/minification/SuperClassC.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package minification;
+
+public class SuperClassC {
+
+}
diff --git a/src/test/examples/minification/keep-rules.txt b/src/test/examples/minification/keep-rules.txt
new file mode 100644
index 0000000..efd06c5
--- /dev/null
+++ b/src/test/examples/minification/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class minification.Minification {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/newarray/NewArray.java b/src/test/examples/newarray/NewArray.java
new file mode 100644
index 0000000..b652324
--- /dev/null
+++ b/src/test/examples/newarray/NewArray.java
@@ -0,0 +1,168 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package newarray;
+
+class NewArray {
+
+  static class A {
+    int v0;
+    int v1;
+    int v2;
+    int v3;
+    int v4;
+    int v5;
+    int v6;
+  }
+
+  public static void printArray(int[] array) {
+    for (int i : array) System.out.println(i);
+  }
+
+  public static void test() {
+    int x0[] = new int[]{};
+    int x1[] = new int[]{0};
+    int x2[] = new int[]{0, 1};
+    int x3[] = new int[]{0, 1, 2};
+    int x4[] = new int[]{0, 1, 2, 3};
+    int x5[] = new int[]{0, 1, 2, 3, 4};
+    int x6[] = new int[]{0, 1, 2, 3, 4, 5};
+    int x7[] = new int[]{0, 1, 2, 3, 4, 5, 6};
+    int x8[] = new int[]{0, 1, 2, 3, 4, 5, 6, 7};
+    int x9[] = new int[]{0, 1, 0, 3, 4, 0, 6, 7};
+    printArray(x0);
+    printArray(x1);
+    printArray(x2);
+    printArray(x3);
+    printArray(x4);
+    printArray(x5);
+    printArray(x6);
+    printArray(x7);
+    printArray(x8);
+    printArray(x9);
+  }
+
+  public static void testIntArgs(int v0, int v1, int v2, int v3, int v4, int v5) {
+    int x0[] = new int[]{};
+    int x1[] = new int[]{v0};
+    int x2[] = new int[]{v0, v1};
+    int x3[] = new int[]{v0, v1, v2};
+    int x4[] = new int[]{v0, v1, v2, v3};
+    int x5[] = new int[]{v0, v1, v2, v3, v4};
+    int x6[] = new int[]{v0, v1, v2, v3, v4, v5};
+    int x7[] = new int[]{v0, v1, v2, v3, v4, v5, v0, v1, v0, v4, v0};
+    printArray(x0);
+    printArray(x1);
+    printArray(x2);
+    printArray(x3);
+    printArray(x4);
+    printArray(x5);
+    printArray(x6);
+    printArray(x7);
+  }
+
+  public static void testObjectArg(A a) {
+    int x0[] = new int[]{};
+    int x1[] = new int[]{a.v0};
+    int x2[] = new int[]{a.v0, a.v1};
+    int x3[] = new int[]{a.v0, a.v1, a.v2};
+    int x4[] = new int[]{a.v0, a.v1, a.v2, a.v3};
+    int x5[] = new int[]{a.v0, a.v1, a.v2, a.v3, a.v4};
+    int x6[] = new int[]{a.v0, a.v1, a.v2, a.v3, a.v4, a.v5};
+    int x7[] = new int[]{a.v0, a.v1, a.v2, a.v3, a.v4, a.v5, a.v6};
+    int x8[] = new int[]{a.v0, a.v1, a.v2, a.v0, a.v3, a.v4, a.v5, a.v6};
+    printArray(x0);
+    printArray(x1);
+    printArray(x2);
+    printArray(x3);
+    printArray(x4);
+    printArray(x5);
+    printArray(x6);
+    printArray(x7);
+    printArray(x8);
+  }
+
+  public static void newMultiDimensionalArrays(int n) {
+    int[][] i2 = new int[n][n];
+    int[][][] i3 = new int[n][n][n];
+    int[][][][] i4 = new int[n][n][n][n];
+    int[][][][][] i5 = new int[n][n][n][n][n];
+    int[][][][][][] i6 = new int[n][n][n][n][n][n];
+    System.out.println(i2.length);
+    System.out.println(i3.length);
+    System.out.println(i4.length);
+    System.out.println(i5.length);
+    System.out.println(i6.length);
+  }
+
+  public static void newMultiDimensionalArrays2(int n1, int n2, int n3, int n4, int n5, int n6) {
+    int[][] i2 = new int[n1][n2];
+    System.out.println(i2.length);
+    int[][][] i3 = new int[n1][n2][n3];
+    System.out.println(i3.length);
+    int[][][][] i4 = new int[n1][n2][n3][n4];
+    System.out.println(i4.length);
+    int[][][][][] i5 = new int[n1][n2][n3][n4][n5];
+    System.out.println(i5.length);
+    int[][][][][][] i6 = new int[n1][n2][n3][n4][n5][n6];
+    System.out.println(i6.length);
+    int[][][][][][] i7 = new int[n1][n2][n1][n4][n5][n1];
+    System.out.println(i7.length);
+  }
+
+  public static void newMultiDimensionalArrays3(int n) {
+    int[][][] i3 = new int[n][n][];
+    int[][][][] i4 = new int[n][n][][];
+    int[][][][][][][] i7 = new int[n][n][n][n][n][n][];
+    int[][][][][][][][] i8 = new int[n][n][n][n][n][n][][];
+    System.out.println(i3.length);
+    System.out.println(i4.length);
+    System.out.println(i7.length);
+    System.out.println(i8.length);
+  }
+
+  public static void newMultiDimensionalArrays4() {
+    boolean[][] a1 = new boolean[1][2];
+    byte[][] a2 = new byte[3][4];
+    char[][] a3 = new char[5][6];
+    short[][] a4 = new short[7][8];
+    long[][] a5 = new long[9][10];
+    float[][] a6 = new float[11][12];
+    double[][] a7 = new double[13][14];
+    A[][] a8 = new A[15][16];
+    System.out.println(a1[0].length);
+    System.out.println(a2[0].length);
+    System.out.println(a3[0].length);
+    System.out.println(a4[0].length);
+    System.out.println(a5[0].length);
+    System.out.println(a6[0].length);
+    System.out.println(a7[0].length);
+    System.out.println(a8[0].length);
+    System.out.println(a1[0][0]);
+    System.out.println(a2[0][0]);
+    System.out.println(a3[0][0]);
+    System.out.println(a4[0][0]);
+    System.out.println(a5[0][0]);
+    System.out.println(a6[0][0]);
+    System.out.println(a7[0][0]);
+    System.out.println(a8[0][0]);
+  }
+
+  public static void main(String[] args) {
+    test();
+    testIntArgs(0, 1, 2, 3, 4, 5);
+    A a = new A();
+    a.v0 = 0;
+    a.v1 = 1;
+    a.v2 = 2;
+    a.v3 = 3;
+    a.v4 = 4;
+    a.v5 = 5;
+    a.v6 = 6;
+    testObjectArg(a);
+    newMultiDimensionalArrays(6);
+    newMultiDimensionalArrays2(1, 2, 3, 4, 5, 6);
+    newMultiDimensionalArrays3(8);
+    newMultiDimensionalArrays4();
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/regalloc/RegAlloc.java b/src/test/examples/regalloc/RegAlloc.java
new file mode 100644
index 0000000..68ad054
--- /dev/null
+++ b/src/test/examples/regalloc/RegAlloc.java
@@ -0,0 +1,391 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'regalloc.dex' is what is run.
+
+package regalloc;
+
+// Various test cases that are challenging for the register allocator.
+public class RegAlloc {
+
+  public static class BoxedInteger {
+    public int i;
+    public BoxedInteger(int i) {
+      this.i = i;
+    }
+  }
+
+  // Takes as many arguments as are allowed by the Java programming language (255) and
+  // does computations on them.
+  public static void binaryOpUsingHighRegistersArguments(
+      int d000, int d001, int d002, int d003, int d004, int d005,
+      int d006, int d007, int d008, int d009, int d010, int d011,
+      int d012, int d013, int d014, int d015, int d016, int d017,
+      int d018, int d019, int d020, int d021, int d022, int d023,
+      int d024, int d025, int d026, int d027, int d028, int d029,
+      int d030, int d031, int d032, int d033, int d034, int d035,
+      int d036, int d037, int d038, int d039, int d040, int d041,
+      int d042, int d043, int d044, int d045, int d046, int d047,
+      int d048, int d049, int d050, int d051, int d052, int d053,
+      int d054, int d055, int d056, int d057, int d058, int d059,
+      int d060, int d061, int d062, int d063, int d064, int d065,
+      int d066, int d067, int d068, int d069, int d070, int d071,
+      int d072, int d073, int d074, int d075, int d076, int d077,
+      int d078, int d079, int d080, int d081, int d082, int d083,
+      int d084, int d085, int d086, int d087, int d088, int d089,
+      int d090, int d091, int d092, int d093, int d094, int d095,
+      int d096, int d097, int d098, int d099, int d100, int d101,
+      int d102, int d103, int d104, int d105, int d106, int d107,
+      int d108, int d109, int d110, int d111, int d112, int d113,
+      int d114, int d115, int d116, int d117, int d118, int d119,
+      int d120, int d121, int d122, int d123, int d124, int d125,
+      int d126, int d127, int d128, int d129, int d130, int d131,
+      int d132, int d133, int d134, int d135, int d136, int d137,
+      int d138, int d139, int d140, int d141, int d142, int d143,
+      int d144, int d145, int d146, int d147, int d148, int d149,
+      int d150, int d151, int d152, int d153, int d154, int d155,
+      int d156, int d157, int d158, int d159, int d160, int d161,
+      int d162, int d163, int d164, int d165, int d166, int d167,
+      int d168, int d169, int d170, int d171, int d172, int d173,
+      int d174, int d175, int d176, int d177, int d178, int d179,
+      int d180, int d181, int d182, int d183, int d184, int d185,
+      int d186, int d187, int d188, int d189, int d190, int d191,
+      int d192, int d193, int d194, int d195, int d196, int d197,
+      int d198, int d199, int d200, int d201, int d202, int d203,
+      int d204, int d205, int d206, int d207, int d208, int d209,
+      int d210, int d211, int d212, int d213, int d214, int d215,
+      int d216, int d217, int d218, int d219, int d220, int d221,
+      int d222, int d223, int d224, int d225, int d226, int d227,
+      int d228, int d229, int d230, int d231, int d232, int d233,
+      int d234, int d235, int d236, int d237, int d238, int d239,
+      int d240, int d241, int d242, int d243, int d244, int d245,
+      int d246, int d247, int d248, int d249, int d250, int d251,
+      int d252, int d253, int d254) {
+    d254 += d253;
+    d253 += 4;
+    d016 += 17000;
+    System.out.println("binaryOpUsingHighRegistersArguments: " + d016 + " " + d253 + " " + d254);
+  }
+
+  // Takes as many arguments as are allowed by the Java programming language (255) and
+  // does computations on them.
+  public static void binaryDoubleOpUsingHighRegistersArguments(
+      double d000, double d001, double d002, double d003, double d004, double d005,
+      double d006, double d007, double d008, double d009, double d010, double d011,
+      double d012, double d013, double d014, double d015, double d016, double d017,
+      double d018, double d019, double d020, double d021, double d022, double d023,
+      double d024, double d025, double d026, double d027, double d028, double d029,
+      double d030, double d031, double d032, double d033, double d034, double d035,
+      double d036, double d037, double d038, double d039, double d040, double d041,
+      double d042, double d043, double d044, double d045, double d046, double d047,
+      double d048, double d049, double d050, double d051, double d052, double d053,
+      double d054, double d055, double d056, double d057, double d058, double d059,
+      double d060, double d061, double d062, double d063, double d064, double d065,
+      double d066, double d067, double d068, double d069, double d070, double d071,
+      double d072, double d073, double d074, double d075, double d076, double d077,
+      double d078, double d079, double d080, double d081, double d082, double d083,
+      double d084, double d085, double d086, double d087, double d088, double d089,
+      double d090, double d091, double d092, double d093, double d094, double d095,
+      double d096, double d097, double d098, double d099, double d100, double d101,
+      double d102, double d103, double d104, double d105, double d106, double d107,
+      double d108, double d109, double d110, double d111, double d112, double d113,
+      double d114, double d115, double d116, double d117, double d118, double d119,
+      double d120, double d121, double d122, double d123, double d124, double d125,
+      double d126) {
+    d126 += d125;
+    System.out.println("binaryDoubleOpUsingHighRegistersArguments: " + d126 + " " + d125);
+  }
+
+  public static <T> T identity(T i) {
+    return i;
+  }
+
+  // Uses a lot of locals that are alive across an addition of two of them. Trivial
+  // register allocation will assign too high registers for the additions.
+  public static void binaryOpUsingHighRegistersLocals() {
+    // Go through identity function in an attempt to make sure that the addition
+    // at the end of this method is not constant folded away.
+    int i000 = identity(0); int i001 = identity(1); int i002 = identity(2);
+    int i003 = identity(3); int i004 = identity(4); int i005 = identity(5);
+    int i006 = identity(6); int i007 = identity(7); int i008 = identity(8);
+    int i009 = identity(9); int i010 = identity(10); int i011 = identity(11);
+    int i012 = identity(12); int i013 = identity(13); int i014 = identity(14);
+    int i015 = identity(15); int i016 = identity(16); int i017 = identity(17);
+    int i018 = identity(18); int i019 = identity(19); int i020 = identity(20);
+    int i021 = identity(21); int i022 = identity(22); int i023 = identity(23);
+    int i024 = identity(24); int i025 = identity(25); int i026 = identity(26);
+    int i027 = identity(27); int i028 = identity(28); int i029 = identity(29);
+    int i030 = identity(30); int i031 = identity(31); int i032 = identity(32);
+    int i033 = identity(33); int i034 = identity(34); int i035 = identity(35);
+    int i036 = identity(36); int i037 = identity(37); int i038 = identity(38);
+    int i039 = identity(39); int i040 = identity(40); int i041 = identity(41);
+    int i042 = identity(42); int i043 = identity(43); int i044 = identity(44);
+    int i045 = identity(45); int i046 = identity(46); int i047 = identity(47);
+    int i048 = identity(48); int i049 = identity(49); int i050 = identity(50);
+    int i051 = identity(51); int i052 = identity(52); int i053 = identity(53);
+    int i054 = identity(54); int i055 = identity(55); int i056 = identity(56);
+    int i057 = identity(57); int i058 = identity(58); int i059 = identity(59);
+    int i060 = identity(60); int i061 = identity(61); int i062 = identity(62);
+    int i063 = identity(63); int i064 = identity(64); int i065 = identity(65);
+    int i066 = identity(66); int i067 = identity(67); int i068 = identity(68);
+    int i069 = identity(69); int i070 = identity(70); int i071 = identity(71);
+    int i072 = identity(72); int i073 = identity(73); int i074 = identity(74);
+    int i075 = identity(75); int i076 = identity(76); int i077 = identity(77);
+    int i078 = identity(78); int i079 = identity(79); int i080 = identity(80);
+    int i081 = identity(81); int i082 = identity(82); int i083 = identity(83);
+    int i084 = identity(84); int i085 = identity(85); int i086 = identity(86);
+    int i087 = identity(87); int i088 = identity(88); int i089 = identity(89);
+    int i090 = identity(90); int i091 = identity(91); int i092 = identity(92);
+    int i093 = identity(93); int i094 = identity(94); int i095 = identity(95);
+    int i096 = identity(96); int i097 = identity(97); int i098 = identity(98);
+    int i099 = identity(99); int i100 = identity(100); int i101 = identity(101);
+    int i102 = identity(102); int i103 = identity(103); int i104 = identity(104);
+    int i105 = identity(105); int i106 = identity(106); int i107 = identity(107);
+    int i108 = identity(108); int i109 = identity(109); int i110 = identity(110);
+    int i111 = identity(111); int i112 = identity(112); int i113 = identity(113);
+    int i114 = identity(114); int i115 = identity(115); int i116 = identity(116);
+    int i117 = identity(117); int i118 = identity(118); int i119 = identity(119);
+    int i120 = identity(120); int i121 = identity(121); int i122 = identity(122);
+    int i123 = identity(123); int i124 = identity(124); int i125 = identity(125);
+    int i126 = identity(126); int i127 = identity(127); int i128 = identity(128);
+    int i129 = identity(129); int i130 = identity(130); int i131 = identity(131);
+    int i132 = identity(132); int i133 = identity(133); int i134 = identity(134);
+    int i135 = identity(135); int i136 = identity(136); int i137 = identity(137);
+    int i138 = identity(138); int i139 = identity(139); int i140 = identity(140);
+    int i141 = identity(141); int i142 = identity(142); int i143 = identity(143);
+    int i144 = identity(144); int i145 = identity(145); int i146 = identity(146);
+    int i147 = identity(147); int i148 = identity(148); int i149 = identity(149);
+    int i150 = identity(150); int i151 = identity(151); int i152 = identity(152);
+    int i153 = identity(153); int i154 = identity(154); int i155 = identity(155);
+    int i156 = identity(156); int i157 = identity(157); int i158 = identity(158);
+    int i159 = identity(159); int i160 = identity(160); int i161 = identity(161);
+    int i162 = identity(162); int i163 = identity(163); int i164 = identity(164);
+    int i165 = identity(165); int i166 = identity(166); int i167 = identity(167);
+    int i168 = identity(168); int i169 = identity(169); int i170 = identity(170);
+    int i171 = identity(171); int i172 = identity(172); int i173 = identity(173);
+    int i174 = identity(174); int i175 = identity(175); int i176 = identity(176);
+    int i177 = identity(177); int i178 = identity(178); int i179 = identity(179);
+    int i180 = identity(180); int i181 = identity(181); int i182 = identity(182);
+    int i183 = identity(183); int i184 = identity(184); int i185 = identity(185);
+    int i186 = identity(186); int i187 = identity(187); int i188 = identity(188);
+    int i189 = identity(189); int i190 = identity(190); int i191 = identity(191);
+    int i192 = identity(192); int i193 = identity(193); int i194 = identity(194);
+    int i195 = identity(195); int i196 = identity(196); int i197 = identity(197);
+    int i198 = identity(198); int i199 = identity(199); int i200 = identity(200);
+    int i201 = identity(201); int i202 = identity(202); int i203 = identity(203);
+    int i204 = identity(204); int i205 = identity(205); int i206 = identity(206);
+    int i207 = identity(207); int i208 = identity(208); int i209 = identity(209);
+    int i210 = identity(210); int i211 = identity(211); int i212 = identity(212);
+    int i213 = identity(213); int i214 = identity(214); int i215 = identity(215);
+    int i216 = identity(216); int i217 = identity(217); int i218 = identity(218);
+    int i219 = identity(219); int i220 = identity(220); int i221 = identity(221);
+    int i222 = identity(222); int i223 = identity(223); int i224 = identity(224);
+    int i225 = identity(225); int i226 = identity(226); int i227 = identity(227);
+    int i228 = identity(228); int i229 = identity(229); int i230 = identity(230);
+    int i231 = identity(231); int i232 = identity(232); int i233 = identity(233);
+    int i234 = identity(234); int i235 = identity(235); int i236 = identity(236);
+    int i237 = identity(237); int i238 = identity(238); int i239 = identity(239);
+    int i240 = identity(240); int i241 = identity(241); int i242 = identity(242);
+    int i243 = identity(243); int i244 = identity(244); int i245 = identity(245);
+    int i246 = identity(246); int i247 = identity(247); int i248 = identity(248);
+    int i249 = identity(249); int i250 = identity(250); int i251 = identity(251);
+    int i252 = identity(252); int i253 = identity(253); int i254 = identity(254);
+    int i255 = identity(255); int i256 = identity(256); int i257 = identity(257);
+    int i258 = identity(258); int i259 = identity(259);
+
+    int i = i259 + i259;
+    System.out.println("binaryOpUsingHighRegistersLocals " + i);
+
+    i = new BoxedInteger(42).i;
+    System.out.println("instance get many registers" + i);
+
+    int j = i000 + i001 + i002 + i003 + i004 + i005 + i006 + i007 + i008 + i009 + i010 + i011 +
+        i012 + i013 + i014 + i015 + i016 + i017 + i018 + i019 + i020 + i021 + i022 + i023 +
+        i024 + i025 + i026 + i027 + i028 + i029 + i030 + i031 + i032 + i033 + i034 + i035 +
+        i036 + i037 + i038 + i039 + i040 + i041 + i042 + i043 + i044 + i045 + i046 + i047 +
+        i048 + i049 + i050 + i051 + i052 + i053 + i054 + i055 + i056 + i057 + i058 + i059 +
+        i060 + i061 + i062 + i063 + i064 + i065 + i066 + i067 + i068 + i069 + i070 + i071 +
+        i072 + i073 + i074 + i075 + i076 + i077 + i078 + i079 + i080 + i081 + i082 + i083 +
+        i084 + i085 + i086 + i087 + i088 + i089 + i090 + i091 + i092 + i093 + i094 + i095 +
+        i096 + i097 + i098 + i099 + i100 + i101 + i102 + i103 + i104 + i105 + i106 + i107 +
+        i108 + i109 + i110 + i111 + i112 + i113 + i114 + i115 + i116 + i117 + i118 + i119 +
+        i120 + i121 + i122 + i123 + i124 + i125 + i126 + i127 + i128 + i129 + i130 + i131 +
+        i132 + i133 + i134 + i135 + i136 + i137 + i138 + i139 + i140 + i141 + i142 + i143 +
+        i144 + i145 + i146 + i147 + i148 + i149 + i150 + i151 + i152 + i153 + i154 + i155 +
+        i156 + i157 + i158 + i159 + i160 + i161 + i162 + i163 + i164 + i165 + i166 + i167 +
+        i168 + i169 + i170 + i171 + i172 + i173 + i174 + i175 + i176 + i177 + i178 + i179 +
+        i180 + i181 + i182 + i183 + i184 + i185 + i186 + i187 + i188 + i189 + i190 + i191 +
+        i192 + i193 + i194 + i195 + i196 + i197 + i198 + i199 + i200 + i201 + i202 + i203 +
+        i204 + i205 + i206 + i207 + i208 + i209 + i210 + i211 + i212 + i213 + i214 + i215 +
+        i216 + i217 + i218 + i219 + i220 + i221 + i222 + i223 + i224 + i225 + i226 + i227 +
+        i228 + i229 + i230 + i231 + i232 + i233 + i234 + i235 + i236 + i237 + i238 + i239 +
+        i240 + i241 + i242 + i243 + i244 + i245 + i246 + i247 + i248 + i249 + i250 + i251 +
+        i252 + i253 + i254 + i255 + i256 + i257 + i258 + i259;
+    System.out.println("sum: " + j);
+  }
+
+  // Uses a lot of locals that are alive across an addition of two of them. Trivial
+  // register allocation will assign too high registers for the additions.
+  public static void binaryDoubleOpUsingHighRegistersLocals() {
+    // Go through identity function in an attempt to make sure that the addition
+    // at the end of this method is not constant folded away.
+    double i000 = identity(0.0); double i001 = identity(1.0); double i002 = identity(2.0);
+    double i003 = identity(3.0); double i004 = identity(4.0); double i005 = identity(5.0);
+    double i006 = identity(6.0); double i007 = identity(7.0); double i008 = identity(8.0);
+    double i009 = identity(9.0); double i010 = identity(10.0); double i011 = identity(11.0);
+    double i012 = identity(12.0); double i013 = identity(13.0); double i014 = identity(14.0);
+    double i015 = identity(15.0); double i016 = identity(16.0); double i017 = identity(17.0);
+    double i018 = identity(18.0); double i019 = identity(19.0); double i020 = identity(20.0);
+    double i021 = identity(21.0); double i022 = identity(22.0); double i023 = identity(23.0);
+    double i024 = identity(24.0); double i025 = identity(25.0); double i026 = identity(26.0);
+    double i027 = identity(27.0); double i028 = identity(28.0); double i029 = identity(29.0);
+    double i030 = identity(30.0); double i031 = identity(31.0); double i032 = identity(32.0);
+    double i033 = identity(33.0); double i034 = identity(34.0); double i035 = identity(35.0);
+    double i036 = identity(36.0); double i037 = identity(37.0); double i038 = identity(38.0);
+    double i039 = identity(39.0); double i040 = identity(40.0); double i041 = identity(41.0);
+    double i042 = identity(42.0); double i043 = identity(43.0); double i044 = identity(44.0);
+    double i045 = identity(45.0); double i046 = identity(46.0); double i047 = identity(47.0);
+    double i048 = identity(48.0); double i049 = identity(49.0); double i050 = identity(50.0);
+    double i051 = identity(51.0); double i052 = identity(52.0); double i053 = identity(53.0);
+    double i054 = identity(54.0); double i055 = identity(55.0); double i056 = identity(56.0);
+    double i057 = identity(57.0); double i058 = identity(58.0); double i059 = identity(59.0);
+    double i060 = identity(60.0); double i061 = identity(61.0); double i062 = identity(62.0);
+    double i063 = identity(63.0); double i064 = identity(64.0); double i065 = identity(65.0);
+    double i066 = identity(66.0); double i067 = identity(67.0); double i068 = identity(68.0);
+    double i069 = identity(69.0); double i070 = identity(70.0); double i071 = identity(71.0);
+    double i072 = identity(72.0); double i073 = identity(73.0); double i074 = identity(74.0);
+    double i075 = identity(75.0); double i076 = identity(76.0); double i077 = identity(77.0);
+    double i078 = identity(78.0); double i079 = identity(79.0); double i080 = identity(80.0);
+    double i081 = identity(81.0); double i082 = identity(82.0); double i083 = identity(83.0);
+    double i084 = identity(84.0); double i085 = identity(85.0); double i086 = identity(86.0);
+    double i087 = identity(87.0); double i088 = identity(88.0); double i089 = identity(89.0);
+    double i090 = identity(90.0); double i091 = identity(91.0); double i092 = identity(92.0);
+    double i093 = identity(93.0); double i094 = identity(94.0); double i095 = identity(95.0);
+    double i096 = identity(96.0); double i097 = identity(97.0); double i098 = identity(98.0);
+    double i099 = identity(99.0); double i100 = identity(100.0); double i101 = identity(101.0);
+    double i102 = identity(102.0); double i103 = identity(103.0); double i104 = identity(104.0);
+    double i105 = identity(105.0); double i106 = identity(106.0); double i107 = identity(107.0);
+    double i108 = identity(108.0); double i109 = identity(109.0); double i110 = identity(110.0);
+    double i111 = identity(111.0); double i112 = identity(112.0); double i113 = identity(113.0);
+    double i114 = identity(114.0); double i115 = identity(115.0); double i116 = identity(116.0);
+    double i117 = identity(117.0); double i118 = identity(118.0); double i119 = identity(119.0);
+    double i120 = identity(120.0); double i121 = identity(121.0); double i122 = identity(122.0);
+    double i123 = identity(123.0); double i124 = identity(124.0); double i125 = identity(125.0);
+    double i126 = identity(126.0); double i127 = identity(127.0); double i128 = identity(128.0);
+    double i129 = identity(129.0); double i130 = identity(130.0); double i131 = identity(131.0);
+    double i132 = identity(132.0); double i133 = identity(133.0); double i134 = identity(134.0);
+    double i135 = identity(135.0); double i136 = identity(136.0); double i137 = identity(137.0);
+    double i138 = identity(138.0); double i139 = identity(139.0); double i140 = identity(140.0);
+    double i141 = identity(141.0); double i142 = identity(142.0); double i143 = identity(143.0);
+    double i144 = identity(144.0); double i145 = identity(145.0); double i146 = identity(146.0);
+    double i147 = identity(147.0); double i148 = identity(148.0); double i149 = identity(149.0);
+    double i150 = identity(150.0); double i151 = identity(151.0); double i152 = identity(152.0);
+    double i153 = identity(153.0); double i154 = identity(154.0); double i155 = identity(155.0);
+    double i156 = identity(156.0); double i157 = identity(157.0); double i158 = identity(158.0);
+    double i159 = identity(159.0); double i160 = identity(160.0); double i161 = identity(161.0);
+    double i162 = identity(162.0); double i163 = identity(163.0); double i164 = identity(164.0);
+    double i165 = identity(165.0); double i166 = identity(166.0); double i167 = identity(167.0);
+    double i168 = identity(168.0); double i169 = identity(169.0); double i170 = identity(170.0);
+    double i171 = identity(171.0); double i172 = identity(172.0); double i173 = identity(173.0);
+    double i174 = identity(174.0); double i175 = identity(175.0); double i176 = identity(176.0);
+    double i177 = identity(177.0); double i178 = identity(178.0); double i179 = identity(179.0);
+    double i180 = identity(180.0); double i181 = identity(181.0); double i182 = identity(182.0);
+    double i183 = identity(183.0); double i184 = identity(184.0); double i185 = identity(185.0);
+    double i186 = identity(186.0); double i187 = identity(187.0); double i188 = identity(188.0);
+    double i189 = identity(189.0); double i190 = identity(190.0); double i191 = identity(191.0);
+    double i192 = identity(192.0); double i193 = identity(193.0); double i194 = identity(194.0);
+    double i195 = identity(195.0); double i196 = identity(196.0); double i197 = identity(197.0);
+    double i198 = identity(198.0); double i199 = identity(199.0); double i200 = identity(200.0);
+    double i201 = identity(201.0); double i202 = identity(202.0); double i203 = identity(203.0);
+    double i204 = identity(204.0); double i205 = identity(205.0); double i206 = identity(206.0);
+    double i207 = identity(207.0); double i208 = identity(208.0); double i209 = identity(209.0);
+    double i210 = identity(210.0); double i211 = identity(211.0); double i212 = identity(212.0);
+    double i213 = identity(213.0); double i214 = identity(214.0); double i215 = identity(215.0);
+    double i216 = identity(216.0); double i217 = identity(217.0); double i218 = identity(218.0);
+    double i219 = identity(219.0); double i220 = identity(220.0); double i221 = identity(221.0);
+    double i222 = identity(222.0); double i223 = identity(223.0); double i224 = identity(224.0);
+    double i225 = identity(225.0); double i226 = identity(226.0); double i227 = identity(227.0);
+    double i228 = identity(228.0); double i229 = identity(229.0); double i230 = identity(230.0);
+    double i231 = identity(231.0); double i232 = identity(232.0); double i233 = identity(233.0);
+    double i234 = identity(234.0); double i235 = identity(235.0); double i236 = identity(236.0);
+    double i237 = identity(237.0); double i238 = identity(238.0); double i239 = identity(239.0);
+    double i240 = identity(240.0); double i241 = identity(241.0); double i242 = identity(242.0);
+    double i243 = identity(243.0); double i244 = identity(244.0); double i245 = identity(245.0);
+    double i246 = identity(246.0); double i247 = identity(247.0); double i248 = identity(248.0);
+    double i249 = identity(249.0); double i250 = identity(250.0); double i251 = identity(251.0);
+    double i252 = identity(252.0); double i253 = identity(253.0); double i254 = identity(254.0);
+    double i255 = identity(255.0); double i256 = identity(256.0); double i257 = identity(257.0);
+    double i258 = identity(258.0); double i259 = identity(259.0);
+
+    double i = i259 + i259;
+    System.out.println("binaryOpUsingHighRegistersLocals " + i);
+
+    double j = i000 + i001 + i002 + i003 + i004 + i005 + i006 + i007 + i008 + i009 + i010 + i011 +
+        i012 + i013 + i014 + i015 + i016 + i017 + i018 + i019 + i020 + i021 + i022 + i023 +
+        i024 + i025 + i026 + i027 + i028 + i029 + i030 + i031 + i032 + i033 + i034 + i035 +
+        i036 + i037 + i038 + i039 + i040 + i041 + i042 + i043 + i044 + i045 + i046 + i047 +
+        i048 + i049 + i050 + i051 + i052 + i053 + i054 + i055 + i056 + i057 + i058 + i059 +
+        i060 + i061 + i062 + i063 + i064 + i065 + i066 + i067 + i068 + i069 + i070 + i071 +
+        i072 + i073 + i074 + i075 + i076 + i077 + i078 + i079 + i080 + i081 + i082 + i083 +
+        i084 + i085 + i086 + i087 + i088 + i089 + i090 + i091 + i092 + i093 + i094 + i095 +
+        i096 + i097 + i098 + i099 + i100 + i101 + i102 + i103 + i104 + i105 + i106 + i107 +
+        i108 + i109 + i110 + i111 + i112 + i113 + i114 + i115 + i116 + i117 + i118 + i119 +
+        i120 + i121 + i122 + i123 + i124 + i125 + i126 + i127 + i128 + i129 + i130 + i131 +
+        i132 + i133 + i134 + i135 + i136 + i137 + i138 + i139 + i140 + i141 + i142 + i143 +
+        i144 + i145 + i146 + i147 + i148 + i149 + i150 + i151 + i152 + i153 + i154 + i155 +
+        i156 + i157 + i158 + i159 + i160 + i161 + i162 + i163 + i164 + i165 + i166 + i167 +
+        i168 + i169 + i170 + i171 + i172 + i173 + i174 + i175 + i176 + i177 + i178 + i179 +
+        i180 + i181 + i182 + i183 + i184 + i185 + i186 + i187 + i188 + i189 + i190 + i191 +
+        i192 + i193 + i194 + i195 + i196 + i197 + i198 + i199 + i200 + i201 + i202 + i203 +
+        i204 + i205 + i206 + i207 + i208 + i209 + i210 + i211 + i212 + i213 + i214 + i215 +
+        i216 + i217 + i218 + i219 + i220 + i221 + i222 + i223 + i224 + i225 + i226 + i227 +
+        i228 + i229 + i230 + i231 + i232 + i233 + i234 + i235 + i236 + i237 + i238 + i239 +
+        i240 + i241 + i242 + i243 + i244 + i245 + i246 + i247 + i248 + i249 + i250 + i251 +
+        i252 + i253 + i254 + i255 + i256 + i257 + i258 + i259;
+    System.out.println("sum: " + j);
+  }
+
+  public static void main(String[] args) {
+    binaryOpUsingHighRegistersArguments(
+        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+        12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+        24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+        36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+        48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+        60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+        72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+        84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+        96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+        108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+        120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+        132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+        144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155,
+        156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
+        168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+        180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+        192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
+        204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215,
+        216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
+        228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+        240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
+        252, 253, 254);
+    binaryDoubleOpUsingHighRegistersArguments(
+        0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,
+        10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0,
+        20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0,
+        30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0,
+        40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0, 49.0,
+        50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0, 57.0, 58.0, 59.0,
+        60.0, 61.0, 62.0, 63.0, 64.0, 65.0, 66.0, 67.0, 68.0, 69.0,
+        70.0, 71.0, 72.0, 73.0, 74.0, 75.0, 76.0, 77.0, 78.0, 79.0,
+        80.0, 81.0, 82.0, 83.0, 84.0, 85.0, 86.0, 87.0, 88.0, 89.0,
+        90.0, 91.0, 92.0, 93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0,
+        100.0, 101.0, 102.0, 103.0, 104.0, 105.0, 106.0, 107.0, 108.0, 109.0,
+        110.0, 111.0, 112.0, 113.0, 114.0, 115.0, 116.0, 117.0, 118.0, 119.0,
+        120.0, 121.0, 122.0, 123.0, 124.0, 125.0, 126.0);
+    binaryOpUsingHighRegistersLocals();
+    binaryDoubleOpUsingHighRegistersLocals();
+  }
+}
diff --git a/src/test/examples/regress/Regress.java b/src/test/examples/regress/Regress.java
new file mode 100644
index 0000000..4b931b6
--- /dev/null
+++ b/src/test/examples/regress/Regress.java
@@ -0,0 +1,48 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'arithmetic.dex' is what is run.
+
+package regress;
+
+public class Regress {
+
+  static void b33336471_int_float() {
+    int i = 0;
+    float f0 = 0.0f;
+    float f1 = 1.0f;
+    for (int j = i; j < 2; j++) {
+      System.out.println("LOOP");
+    }
+    float f[] = new float[1];
+    if (f[0] != f0) {
+      System.out.println("FLOAT COMPARISON FAILED");
+    }
+    if (f[0] == f1) {
+      System.out.println("FLOAT COMPARISON FAILED");
+    }
+  }
+
+  static void b33336471_long_double() {
+    long i = 0;
+    double d0 = 0.0f;
+    double d1 = 1.0f;
+    for (long j = i; j < 2; j++) {
+      System.out.println("LOOP");
+    }
+    double d[] = new double[1];
+    if (d[0] != d0) {
+      System.out.println("DOUBLE COMPARISON FAILED");
+    }
+    if (d[0] == d1) {
+      System.out.println("DOUBLE COMPARISON FAILED");
+    }
+  }
+
+  public static void main(String[] args) {
+    b33336471_int_float();
+    b33336471_long_double();
+  }
+}
diff --git a/src/test/examples/regress2/Regress2.java b/src/test/examples/regress2/Regress2.java
new file mode 100644
index 0000000..151a520
--- /dev/null
+++ b/src/test/examples/regress2/Regress2.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package regress2;
+
+public class Regress2 {
+
+  static class X {
+
+    void add() {
+    }
+  }
+
+  static private boolean test() {
+    X x = null;
+    X y = null;
+
+    int a = 5;
+    System.out.println("START");
+    while (a-- > 0) {
+      System.out.println("LOOP");
+      int b = 0;
+      switch (b) {
+        case 1:
+          X current = new X();
+          if (x == null) {
+            x = current;
+          } else {
+            x = null;
+          }
+          y.add();
+          break;
+        case 2:
+          if (x != null) {
+            x = null;
+          }
+          y.add();
+          break;
+      }
+    }
+    System.out.println("END");
+    return true;
+  }
+
+  public static void main(String[] args) {
+    test();
+  }
+}
diff --git a/src/test/examples/regress_37658666/Regress.java b/src/test/examples/regress_37658666/Regress.java
new file mode 100644
index 0000000..4f6019d
--- /dev/null
+++ b/src/test/examples/regress_37658666/Regress.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package regress_37658666;
+
+class Float {
+  int cmp(float a, float b) {
+    if (a > b)
+      return 1;
+    if (a == b)
+      return 0;
+    return -1;
+  }
+}
+
+public class Regress {
+
+  public static boolean check(int i, int j) {
+    return i == j;
+  }
+
+  public static void main(String[] args) {
+    Float f = new Float();
+    System.out.println(check(0, f.cmp(+0f, -0f)));
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/regress_37726195/Regress.java b/src/test/examples/regress_37726195/Regress.java
new file mode 100644
index 0000000..ad9781a
--- /dev/null
+++ b/src/test/examples/regress_37726195/Regress.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package regress_37726195;
+
+public class Regress {
+
+  // Regression test for issue where aput instructions for different primitive array types
+  // were joined. The art verifier doesn't allow that.
+  public static void set(Object array, int index, byte value) {
+    if (array instanceof float[]) {
+      float[] floats = (float[]) array;
+      floats[index] = value;
+    } else if (array instanceof int[]) {
+      int[] ints = (int[]) array;
+      ints[index] = value;
+    }
+  }
+
+  // Regression test for issue where aget instructions for different primitive array types
+  // were joined. The art verifier doesn't allow that.
+  public static void get(Object array, int index) {
+    if (array instanceof float[]) {
+      float[] floats = (float[]) array;
+      float f = floats[index];
+    } else if (array instanceof int[]) {
+      int[] ints = (int[]) array;
+      int i = ints[index];
+    }
+  }
+
+  public static void main(String[] args) {
+    int[] ints = { 0 };
+    float[] floats = { 0.0f };
+    set(ints, 0, (byte) 4);
+    System.out.println(ints[0]);
+    set(floats, 0, (byte) 4);
+    System.out.println(floats[0]);
+    get(ints, 0);
+    get(floats, 0);
+  }
+}
diff --git a/src/test/examples/regress_37875803/Regress.java b/src/test/examples/regress_37875803/Regress.java
new file mode 100644
index 0000000..9360046
--- /dev/null
+++ b/src/test/examples/regress_37875803/Regress.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package regress_37875803;
+
+public class Regress {
+
+  private static int digitSub(int codePoint) {
+    int result = -888;
+    if ('a' <= codePoint && codePoint <= 'z') {
+      result = 10 + (codePoint - 'a');
+    } else if ('A' <= codePoint && codePoint <= 'Z') {
+      result = 10 + (codePoint - 'A');
+    }
+    if (result < 0) {
+      throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+    }
+    return result;
+  }
+
+  private static int digitSubLeft(int codePoint) {
+    int result = -888;
+    if ('a' <= codePoint && codePoint <= 'z') {
+      result = 10 + ('a' - codePoint);
+    } else if ('A' <= codePoint && codePoint <= 'Z') {
+      result = 10 + ('A' - codePoint);
+    }
+    if (result < 0) {
+      throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+    }
+    return result;
+  }
+
+  private static int digitAdd(int codePoint) {
+    int result = -888;
+    if ('a' <= codePoint && codePoint <= 'z') {
+      result = 10 + (codePoint + 'a');
+    } else if ('A' <= codePoint && codePoint <= 'Z') {
+      result = 10 + (codePoint + 'A');
+    }
+    if (result < 0) {
+      throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+    }
+    return result;
+  }
+
+  private static int digitOr(int codePoint) {
+    int result = -888;
+    if ('a' <= codePoint && codePoint <= 'z') {
+      result = 10 + (codePoint | 'a');
+    } else if ('A' <= codePoint && codePoint <= 'Z') {
+      result = 10 + (codePoint | 'A');
+    }
+    if (result < 0) {
+      throw new RuntimeException("codePoint = " + codePoint + " result = " + result);
+    }
+    return result;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(digitSub((int) 'a'));
+    System.out.println(digitSub((int) 'A'));
+    System.out.println(digitSubLeft((int) 'a'));
+    System.out.println(digitSubLeft((int) 'A'));
+    System.out.println(digitAdd((int) 'a'));
+    System.out.println(digitAdd((int) 'A'));
+    System.out.println(digitOr((int) 'a'));
+    System.out.println(digitOr((int) 'A'));
+  }
+}
diff --git a/src/test/examples/regress_37955340/Regress.java b/src/test/examples/regress_37955340/Regress.java
new file mode 100644
index 0000000..a8b5e0b
--- /dev/null
+++ b/src/test/examples/regress_37955340/Regress.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package regress_37955340;
+
+public class Regress {
+
+  static class A {
+
+    int x(int a) {
+      return a;
+    }
+  }
+
+  static class B extends A {
+
+    int x(int a) {
+      return 2;
+    }
+  }
+
+  public static void main(String[] args) {
+    A a = new B();
+    System.out.println(a.x(1));
+  }
+}
diff --git a/src/test/examples/returns/Returns.java b/src/test/examples/returns/Returns.java
new file mode 100644
index 0000000..c496ce5
--- /dev/null
+++ b/src/test/examples/returns/Returns.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package returns;
+
+public class  Returns {
+  public static void main(String[] args) {
+    doubleJunk(); // Ignored to get pop2
+    longJunk();
+    longFloat();
+  }
+
+  private static double doubleJunk() {
+    return 42.42;
+  }
+
+  private static long longJunk() {
+    long billion5 = 5000000000L;
+    return billion5;
+  }
+
+  private static float longFloat() {
+    float foobar = 1.0F;
+    return foobar;
+  }
+}
diff --git a/src/test/examples/rewrite/LongCompare.java b/src/test/examples/rewrite/LongCompare.java
new file mode 100644
index 0000000..7c96c10
--- /dev/null
+++ b/src/test/examples/rewrite/LongCompare.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package rewrite;
+
+public class LongCompare {
+
+  public static int simpleCompare(long l1, long l2) {
+    try {
+      return Long.compare(l1, l2);
+    } catch (Throwable t) {
+      System.out.println(t);
+    }
+    return 2;
+  }
+
+  public static long getValue1() {
+    return 123456789L;
+  }
+
+  public static long getValue2() {
+    return 0;
+  }
+
+  public static boolean complexCompare(long l1, long l2) {
+    return Long.compare(getValue1(), l1) == 0 && Long.compare(l2, getValue2()) > 0;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(simpleCompare(123456789L, 987654321L));
+    System.out.println(simpleCompare(Long.MAX_VALUE, 0L));
+    System.out.println(simpleCompare(Long.MIN_VALUE, 0L));
+    System.out.println(simpleCompare(Long.MAX_VALUE, Long.MAX_VALUE));
+
+    System.out.println(complexCompare(123456789L, 1));
+    System.out.println(complexCompare(123456789L, -1));
+    System.out.println(complexCompare(1234567890L, 1));
+    System.out.println(complexCompare(1234567890L, -1));
+  }
+}
diff --git a/src/test/examples/rewrite/RequireNonNull.java b/src/test/examples/rewrite/RequireNonNull.java
new file mode 100644
index 0000000..d8868bf
--- /dev/null
+++ b/src/test/examples/rewrite/RequireNonNull.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package rewrite;
+
+import java.util.Objects;
+
+public class RequireNonNull {
+
+  public static void main(String[] args) {
+    RequireNonNull o = new RequireNonNull();
+    System.out.println(o.nonnullRemove().toString());
+    System.out.println(o.nonnullRemove(o).toString());
+    o.nonnullWithPhiInstruction(true, o);
+    o.nonnullWithPhiInstruction(false, o);
+  }
+
+  private Object nonnullRemove() {
+    return Objects.requireNonNull(this);
+  }
+
+  private Object nonnullRemove(Object o) {
+    Objects.requireNonNull(o);
+    return o;
+  }
+
+  private void nonnullWithPhiInstruction(boolean b, Object input) {
+    Object o = null;
+    if (b) {
+      o = Objects.requireNonNull(input);
+    }
+    System.out.println(o);
+  }
+
+  @Override
+  public String toString() {
+    return "toString";
+  }
+}
diff --git a/src/test/examples/shaking1/Shaking.java b/src/test/examples/shaking1/Shaking.java
new file mode 100644
index 0000000..8c39ab8
--- /dev/null
+++ b/src/test/examples/shaking1/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking1;
+
+public class Shaking {
+  public static void main(String[] args) {
+    System.out.println(new Used("world").method());
+  }
+}
diff --git a/src/test/examples/shaking1/Unused.java b/src/test/examples/shaking1/Unused.java
new file mode 100644
index 0000000..fd6fe40
--- /dev/null
+++ b/src/test/examples/shaking1/Unused.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking1;
+
+public class Unused {
+
+  public static int aStaticFieldThatIsNotUsedButKept = 42;
+
+  public static void unusedStaticMethod() {
+    System.out.println("unusedStaticMethod");
+  }
+
+  public void unusedInstanceMethod() {
+    System.out.println("unusedInstanceMethod");
+  }
+
+  public String aMethodThatIsNotUsedButKept() {
+    return "Unused string";
+  }
+}
diff --git a/src/test/examples/shaking1/Used.java b/src/test/examples/shaking1/Used.java
new file mode 100644
index 0000000..23450ab
--- /dev/null
+++ b/src/test/examples/shaking1/Used.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking1;
+
+public class Used {
+
+  public static int aStaticFieldThatIsNotUsedButKept = 42;
+
+  private final String name;
+
+  public Used(String name) {
+    this.name = name;
+  }
+
+  public String method() {
+    return "Hello, " + name;
+  }
+
+  public String aMethodThatIsNotUsedButKept() {
+    return "Unused string";
+  }
+}
diff --git a/src/test/examples/shaking1/keep-rules-dont-shrink.txt b/src/test/examples/shaking1/keep-rules-dont-shrink.txt
new file mode 100644
index 0000000..6edaafc
--- /dev/null
+++ b/src/test/examples/shaking1/keep-rules-dont-shrink.txt
@@ -0,0 +1,7 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontshrink
+
+@keep-rules.txt
\ No newline at end of file
diff --git a/src/test/examples/shaking1/keep-rules-repackaging.txt b/src/test/examples/shaking1/keep-rules-repackaging.txt
new file mode 100644
index 0000000..b5c2194
--- /dev/null
+++ b/src/test/examples/shaking1/keep-rules-repackaging.txt
@@ -0,0 +1,21 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking1.Shaking {
+  public static void main(...);
+}
+
+# Keep two members if class is live
+-keepclassmembers class * {
+  public static int aStaticFieldThatIsNotUsedButKept;
+  public java.lang.String aMethodThatIsNotUsedButKept();
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
+
+# change package of renamed classes
+-repackageclasses 'repackaged'
diff --git a/src/test/examples/shaking1/keep-rules.txt b/src/test/examples/shaking1/keep-rules.txt
new file mode 100644
index 0000000..66cf1c6
--- /dev/null
+++ b/src/test/examples/shaking1/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking1.Shaking {
+  public static void main(...);
+}
+
+# Keep two members if class is live
+-keepclassmembers class * {
+  public static int aStaticFieldThatIsNotUsedButKept;
+  public java.lang.String aMethodThatIsNotUsedButKept();
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
diff --git a/src/test/examples/shaking1/print-mapping.ref b/src/test/examples/shaking1/print-mapping.ref
new file mode 100644
index 0000000..8382304
--- /dev/null
+++ b/src/test/examples/shaking1/print-mapping.ref
@@ -0,0 +1,4 @@
+shaking1.Shaking -> shaking1.Shaking:
+shaking1.Used -> shaking1.a:
+    java.lang.String name -> a
+    java.lang.String method() -> a
diff --git a/src/test/examples/shaking10/HasAFooImpl.java b/src/test/examples/shaking10/HasAFooImpl.java
new file mode 100644
index 0000000..d975b26
--- /dev/null
+++ b/src/test/examples/shaking10/HasAFooImpl.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking10;
+
+import shakinglib.HasAFoo;
+import shakinglib.HasAGetter;
+
+public class HasAFooImpl implements HasAFoo {
+
+  @Override
+  public HasAGetter foo() {
+    return new ReturnsOne();
+  }
+
+  public int bar() {
+    return 0;
+  }
+
+  private static class ReturnsOne implements HasAGetter {
+
+    @Override
+    public int getIt() {
+      return 1;
+    }
+  }
+}
diff --git a/src/test/examples/shaking10/Shaking.java b/src/test/examples/shaking10/Shaking.java
new file mode 100644
index 0000000..0737ec8
--- /dev/null
+++ b/src/test/examples/shaking10/Shaking.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking10;
+
+import shakinglib.IndirectMethodInvoker;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    HasAFooImpl impl = new HasAFooImpl();
+    IndirectMethodInvoker invoker = new IndirectMethodInvoker();
+    System.out.println(invoker.invokeFoo(impl));
+  }
+}
diff --git a/src/test/examples/shaking10/keep-rules.txt b/src/test/examples/shaking10/keep-rules.txt
new file mode 100644
index 0000000..0895849
--- /dev/null
+++ b/src/test/examples/shaking10/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking10.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking11/Shaking.java b/src/test/examples/shaking11/Shaking.java
new file mode 100644
index 0000000..6dd396f
--- /dev/null
+++ b/src/test/examples/shaking11/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+  }
+}
diff --git a/src/test/examples/shaking11/Subclass.java b/src/test/examples/shaking11/Subclass.java
new file mode 100644
index 0000000..e6b2973
--- /dev/null
+++ b/src/test/examples/shaking11/Subclass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class Subclass extends SuperClass {
+  // Intentionally empty.
+}
diff --git a/src/test/examples/shaking11/SubclassWithMethod.java b/src/test/examples/shaking11/SubclassWithMethod.java
new file mode 100644
index 0000000..073d352
--- /dev/null
+++ b/src/test/examples/shaking11/SubclassWithMethod.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class SubclassWithMethod extends SuperClass {
+
+  public void aMethod() {
+    // Intentionally empty.
+  }
+}
diff --git a/src/test/examples/shaking11/SuperClass.java b/src/test/examples/shaking11/SuperClass.java
new file mode 100644
index 0000000..1c0fc8f
--- /dev/null
+++ b/src/test/examples/shaking11/SuperClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking11;
+
+public class SuperClass {
+
+  public void aMethod() {
+    // Intentionally empty.
+  }
+}
diff --git a/src/test/examples/shaking11/keep-rules-keep-method.txt b/src/test/examples/shaking11/keep-rules-keep-method.txt
new file mode 100644
index 0000000..8b65519
--- /dev/null
+++ b/src/test/examples/shaking11/keep-rules-keep-method.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking11.Shaking {
+  public static void main(...);
+}
+
+-keep class shaking11.SuperClass {}
+
+-keep class shaking11.Sub* {
+  public void aMethod();
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking11/keep-rules.txt b/src/test/examples/shaking11/keep-rules.txt
new file mode 100644
index 0000000..0a58c01
--- /dev/null
+++ b/src/test/examples/shaking11/keep-rules.txt
@@ -0,0 +1,16 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking11.Shaking {
+  public static void main(...);
+}
+
+-keepclasseswithmembers class * {
+  public void aMethod();
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking12/AnimalClass.java b/src/test/examples/shaking12/AnimalClass.java
new file mode 100644
index 0000000..87557b3
--- /dev/null
+++ b/src/test/examples/shaking12/AnimalClass.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class AnimalClass extends Named {
+
+  @Override
+  public String getName() {
+    return "AnimalClass";
+  }
+}
diff --git a/src/test/examples/shaking12/MetaphorClass.java b/src/test/examples/shaking12/MetaphorClass.java
new file mode 100644
index 0000000..3192368
--- /dev/null
+++ b/src/test/examples/shaking12/MetaphorClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class MetaphorClass extends Named {
+
+  public String getName() {
+    return "MetaphorClass";
+  }
+}
diff --git a/src/test/examples/shaking12/Named.java b/src/test/examples/shaking12/Named.java
new file mode 100644
index 0000000..b829050
--- /dev/null
+++ b/src/test/examples/shaking12/Named.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public abstract class Named {
+
+  public abstract String getName();
+}
diff --git a/src/test/examples/shaking12/PeopleClass.java b/src/test/examples/shaking12/PeopleClass.java
new file mode 100644
index 0000000..24c1f3e
--- /dev/null
+++ b/src/test/examples/shaking12/PeopleClass.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class PeopleClass extends Named {
+
+  @Override
+  public String getName() {
+    return "PeopleClass";
+  }
+}
diff --git a/src/test/examples/shaking12/Shaking.java b/src/test/examples/shaking12/Shaking.java
new file mode 100644
index 0000000..3d1c40e
--- /dev/null
+++ b/src/test/examples/shaking12/Shaking.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class Shaking {
+
+  static private Named createInstance(Class<? extends Named> aClass) {
+    try {
+      return aClass.newInstance();
+    } catch (InstantiationException | IllegalAccessException e) {
+      e.printStackTrace();
+    }
+    return new Named() {
+      @Override
+      public String getName() {
+        return "Unknown";
+      }
+    };
+  }
+
+  public static void main(String[] args) {
+    List<Class<? extends Named>> classes = new ArrayList<>(3);
+    classes.add(MetaphorClass.class);
+    classes.add(PeopleClass.class);
+    classes.add(ThingClass.class);
+    Iterator<Class<? extends Named>> iterator = classes.iterator();
+    iterator.next();
+    while (iterator.hasNext()) {
+      Named item = createInstance(iterator.next());
+      if (item instanceof AnimalClass) {
+        System.out.println("An animal!");
+      }
+      System.out.println(createInstance(iterator.next()).getName());
+    }
+  }
+}
diff --git a/src/test/examples/shaking12/ThingClass.java b/src/test/examples/shaking12/ThingClass.java
new file mode 100644
index 0000000..554b87b
--- /dev/null
+++ b/src/test/examples/shaking12/ThingClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking12;
+
+public class ThingClass extends Named {
+
+  public String getName() {
+    return "ThingClass";
+  }
+}
diff --git a/src/test/examples/shaking12/keep-rules.txt b/src/test/examples/shaking12/keep-rules.txt
new file mode 100644
index 0000000..6a5e277
--- /dev/null
+++ b/src/test/examples/shaking12/keep-rules.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking12.Shaking {
+  public static void main(...);
+}
+
+# Keep the constructors that are used via newInstance.
+-keep public class shaking12.ThingClass,shaking12.PeopleClass {
+  <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/examples/shaking13/AClassWithFields.java b/src/test/examples/shaking13/AClassWithFields.java
new file mode 100644
index 0000000..6a4052b
--- /dev/null
+++ b/src/test/examples/shaking13/AClassWithFields.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking13;
+
+public class AClassWithFields {
+  public static int staticIntField;
+  public int intField;
+
+}
diff --git a/src/test/examples/shaking13/Shaking.java b/src/test/examples/shaking13/Shaking.java
new file mode 100644
index 0000000..36e615f
--- /dev/null
+++ b/src/test/examples/shaking13/Shaking.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking13;
+
+import shakinglib.LibraryClass;
+
+public class Shaking {
+
+  private static void fieldTest() {
+    AClassWithFields instance = new AClassWithFields();
+    instance.intField = 1;
+    AClassWithFields.staticIntField = 2;
+
+    LibraryClass.staticIntField = 3;
+    LibraryClass libraryInstance = new LibraryClass();
+    libraryInstance.intField = 4;
+  }
+
+  public static void main(String[] args) {
+    fieldTest();
+  }
+}
diff --git a/src/test/examples/shaking13/keep-rules.txt b/src/test/examples/shaking13/keep-rules.txt
new file mode 100644
index 0000000..531e320
--- /dev/null
+++ b/src/test/examples/shaking13/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking13.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/examples/shaking14/Shaking.java b/src/test/examples/shaking14/Shaking.java
new file mode 100644
index 0000000..b12bd70
--- /dev/null
+++ b/src/test/examples/shaking14/Shaking.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking14;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    SubSubClass instance = new SubSubClass();
+    System.out.println(Subclass.aMethod(99));
+    System.out.println(instance.anotherMethod(100));
+  }
+}
diff --git a/src/test/examples/shaking14/SubSubClass.java b/src/test/examples/shaking14/SubSubClass.java
new file mode 100644
index 0000000..c06ebb2
--- /dev/null
+++ b/src/test/examples/shaking14/SubSubClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking14;
+
+public class SubSubClass extends Subclass {
+  // Intentionally left empty.
+}
diff --git a/src/test/examples/shaking14/Subclass.java b/src/test/examples/shaking14/Subclass.java
new file mode 100644
index 0000000..31dd650
--- /dev/null
+++ b/src/test/examples/shaking14/Subclass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking14;
+
+public class Subclass extends Superclass {
+  static int aMethod(int value) {
+    return value + 42;
+  }
+
+  static double anotherMethod(double value) {
+    return value + 42;
+  }
+}
diff --git a/src/test/examples/shaking14/Superclass.java b/src/test/examples/shaking14/Superclass.java
new file mode 100644
index 0000000..9dafff3
--- /dev/null
+++ b/src/test/examples/shaking14/Superclass.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking14;
+
+public class Superclass {
+  static int aMethod(int value) {
+    return value;
+  }
+
+  static double anotherMethod(double value) {
+    return value;
+  }
+}
diff --git a/src/test/examples/shaking14/keep-rules.txt b/src/test/examples/shaking14/keep-rules.txt
new file mode 100644
index 0000000..94682a3
--- /dev/null
+++ b/src/test/examples/shaking14/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking14.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking15/Shaking.java b/src/test/examples/shaking15/Shaking.java
new file mode 100644
index 0000000..4d81efd
--- /dev/null
+++ b/src/test/examples/shaking15/Shaking.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking15;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    Subclass thing = new Subclass();
+    thing.callingSuper();
+  }
+}
diff --git a/src/test/examples/shaking15/Subclass.java b/src/test/examples/shaking15/Subclass.java
new file mode 100644
index 0000000..16b9f1a
--- /dev/null
+++ b/src/test/examples/shaking15/Subclass.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking15;
+
+public class Subclass extends Superclass {
+  public int a,b,c;
+
+  @Override
+  public void aMethod() {
+    System.out.println("Called aMethod in Subclass");
+  }
+
+  @Override
+  public void callingSuper() {
+    System.out.println("Called callingSuper in Subclass");
+    super.callingSuper();
+    super.aMethod();
+  }
+}
diff --git a/src/test/examples/shaking15/Superclass.java b/src/test/examples/shaking15/Superclass.java
new file mode 100644
index 0000000..5e46622
--- /dev/null
+++ b/src/test/examples/shaking15/Superclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking15;
+
+public class Superclass {
+
+  public void aMethod() {
+    System.out.println("Called aMethod in Superclass");
+  }
+
+  public void callingSuper() {
+    System.out.println("Called callingSuper in Superclass");
+  }
+}
diff --git a/src/test/examples/shaking15/dictionary b/src/test/examples/shaking15/dictionary
new file mode 100644
index 0000000..b7c9f2f
--- /dev/null
+++ b/src/test/examples/shaking15/dictionary
@@ -0,0 +1,14 @@
+# this is a comment
+abc# this is another comment
+def # this is again another comment after an ignored character
+ghi
+jkl
+mno pqr
+stu!vw$[
+
+/*-+&~"'
+xyz_
+
+    ea
+1abc
+!
\ No newline at end of file
diff --git a/src/test/examples/shaking15/keep-rules.txt b/src/test/examples/shaking15/keep-rules.txt
new file mode 100644
index 0000000..70948bb
--- /dev/null
+++ b/src/test/examples/shaking15/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking15.Shaking {
+  public static void main(...);
+}
+-dontshrink
+
+# allow access modification to enable minification
+-allowaccessmodification
+
+# use dictionaries
+-obfuscationdictionary dictionary
+-classobfuscationdictionary dictionary
+-packageobfuscationdictionary dictionary
diff --git a/src/test/examples/shaking15/sub/A.java b/src/test/examples/shaking15/sub/A.java
new file mode 100644
index 0000000..25e4d7a
--- /dev/null
+++ b/src/test/examples/shaking15/sub/A.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+package shaking15.sub;
+
+public class A {
+
+}
diff --git a/src/test/examples/shaking16/Shaking.java b/src/test/examples/shaking16/Shaking.java
new file mode 100644
index 0000000..7de3ebc
--- /dev/null
+++ b/src/test/examples/shaking16/Shaking.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking16;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    a thing = new a(3, 5);
+    thing.a();
+    thing.b();
+  }
+}
diff --git a/src/test/examples/shaking16/a.java b/src/test/examples/shaking16/a.java
new file mode 100644
index 0000000..3a6a4ec
--- /dev/null
+++ b/src/test/examples/shaking16/a.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking16;
+
+public class a extends b {
+  
+  public int a;
+  public int b;
+  
+  public a(int a, int b) {
+    super(a -1, b + 1);
+    this.a = a;
+    this.b = b;
+  }
+
+  @Override
+  public void a() {
+    System.out.println("Called a in Subclass " + a + ", " + b);
+  }
+
+  @Override
+  public void b() {
+    System.out.println("Called b in Subclass");
+    super.b();
+    super.a();
+  }
+}
diff --git a/src/test/examples/shaking16/b.java b/src/test/examples/shaking16/b.java
new file mode 100644
index 0000000..7ded44f
--- /dev/null
+++ b/src/test/examples/shaking16/b.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking16;
+
+public class b {
+  
+  public int a;
+  public int b;
+  
+  public b(int a, int b) {
+    this.a = a;
+    this.b = b;
+  }
+
+  public void a() {
+    System.out.println("Called a in Superclass" + a + ", " + b);
+  }
+
+  public void b() {
+    System.out.println("Called b in Superclass");
+  }
+}
diff --git a/src/test/examples/shaking16/keep-rules-1.txt b/src/test/examples/shaking16/keep-rules-1.txt
new file mode 100644
index 0000000..8043cb2
--- /dev/null
+++ b/src/test/examples/shaking16/keep-rules-1.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking16.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking16/keep-rules-2.txt b/src/test/examples/shaking16/keep-rules-2.txt
new file mode 100644
index 0000000..f11c3ad
--- /dev/null
+++ b/src/test/examples/shaking16/keep-rules-2.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking16.Shaking {
+  public static void main(...);
+}
+
+-keep public class shaking16.a {
+  int a;
+  void a();
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking2/Interface.java b/src/test/examples/shaking2/Interface.java
new file mode 100644
index 0000000..3862b05
--- /dev/null
+++ b/src/test/examples/shaking2/Interface.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public interface Interface extends SuperInterface1, SuperInterface2 {
+  public void unusedInterfaceMethod();
+  public void interfaceMethod();
+  public void interfaceMethod4();
+  public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8);
+}
diff --git a/src/test/examples/shaking2/Shaking.java b/src/test/examples/shaking2/Shaking.java
new file mode 100644
index 0000000..f99fd8c
--- /dev/null
+++ b/src/test/examples/shaking2/Shaking.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class Shaking {
+  public static void callInterfaceMethod(Interface object) {
+    object.interfaceMethod4();
+    object.interfaceMethod5(1, 2, 3, 4, 5, 6, 7, 8);
+  }
+
+  public static void callAsSuperClassAndInterface(SuperClass object) {
+    object.interfaceMethod();
+    object.interfaceMethod2();
+    object.interfaceMethod3();
+    object.virtualMethod();
+    object.virtualMethod2(1, 2, 3, 4, 5, 6, 7, 8);
+    object.accessFields();
+    callInterfaceMethod(object);
+  }
+
+  public static void accessStaticFields() {
+    System.out.println("StaticFields::used: " + StaticFields.used);
+    System.out.println("StaitcFields::read" +
+        " " + StaticFields.readInt +
+        " " + StaticFields.readBoolean+
+        " " + StaticFields.readByte +
+        " " + StaticFields.readChar +
+        " " + StaticFields.readObject +
+        " " + StaticFields.readShort +
+        " " + StaticFields.readDouble);
+    StaticFields.writeInt = 1;
+    StaticFields.writeBoolean = true;
+    StaticFields.writeByte = 2;
+    StaticFields.writeChar = 3;
+    StaticFields.writeObject = new Object();
+    StaticFields.writeShort = 3;
+    StaticFields.writeDouble = 3.3;
+  }
+
+  public static void main(String[] args) {
+    accessStaticFields();
+    SuperClass.staticMethod();
+    SuperClass.staticMethod2(1, 2, 3, 4, 5, 6, 7, 8);
+    SubClass1 instance1 = new SubClass1(1);
+    callAsSuperClassAndInterface(instance1);
+    instance1.virtualMethod3();
+    instance1.virtualMethod4(1, 2, 3, 4, 5, 6, 7, 8);
+    callAsSuperClassAndInterface(new SubClass1(1, 2, 3, 4, 5, 6, 7, 8));
+    SubClass2 instance2 = new SubClass2(2);
+    callAsSuperClassAndInterface(instance2);
+    instance2.virtualMethod3();
+    instance2.virtualMethod4(1, 2, 3, 4, 5, 6, 7, 8);
+  }
+}
diff --git a/src/test/examples/shaking2/StaticFields.java b/src/test/examples/shaking2/StaticFields.java
new file mode 100644
index 0000000..80bf6ba
--- /dev/null
+++ b/src/test/examples/shaking2/StaticFields.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class StaticFields {
+  public static int used = 42;
+  // Unused but initialized by <clinit>.
+  public static int unused = -42;
+  // Not even used by <clinit>.
+  public static int completelyUnused;
+
+  public static int readInt;
+  public static int writeInt;
+  public static boolean readBoolean;
+  public static boolean writeBoolean;
+  public static byte readByte;
+  public static byte writeByte;
+  public static char readChar;
+  public static char writeChar;
+  public static Object readObject;
+  public static Object writeObject;
+  public static short readShort;
+  public static short writeShort;
+  public static double readDouble;
+  public static double writeDouble;
+}
\ No newline at end of file
diff --git a/src/test/examples/shaking2/SubClass1.java b/src/test/examples/shaking2/SubClass1.java
new file mode 100644
index 0000000..722c6e0
--- /dev/null
+++ b/src/test/examples/shaking2/SubClass1.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class SubClass1 extends SuperClass {
+  private int used;
+  private int alsoUsed;
+  private boolean usedBoolean;
+  private byte usedByte;
+  private char usedChar;
+  private Object usedObject;
+  private short usedShort;
+  private double usedDouble;
+
+  private int unused;
+
+  public SubClass1(int used) {
+    this.used = used;
+  }
+
+  public SubClass1(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    this.used = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;
+  }
+
+  @Override
+  public void virtualMethod() {
+    System.out.println("SubClass1::virtualMethod");
+  }
+
+  @Override
+  public void virtualMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SubClass1::virtualMethod2 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  @Override
+  public void virtualMethod3() {
+    super.virtualMethod3();
+    System.out.println("SubClass1::virtualMethod3");
+  }
+
+  @Override
+  public void virtualMethod4(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    super.virtualMethod4(a1, a2, a3, a4, a5, a6, a7, a8);
+    System.out.println("SubClass1::virtualMethod4 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  @Override
+  public void unusedVirtualMethod() {
+    System.out.println("SubClass1::unusedVirtualMethod");
+  }
+
+  @Override
+  public void interfaceMethod() {
+    System.out.println("SubClass1::interfaceMethod");
+  }
+
+  @Override
+  public void interfaceMethod2() {
+    System.out.println("SubClass1::interfaceMethod2");
+  }
+
+  @Override
+  public void interfaceMethod3() {
+    System.out.println("SubClass1::interfaceMethod3");
+  }
+
+  @Override
+  public void interfaceMethod4() {
+    System.out.println("SubClass1::interfaceMethod4");
+  }
+
+  @Override
+  public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SubClass1::interfaceMethod5 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  @Override
+  public void accessFields() {
+    super.accessFields();
+    System.out.println("SubClass1::fields: " + used + " " + alsoUsed + " " + usedBoolean +
+        " " + usedByte + " " + usedChar + " " + usedObject + " " + usedShort + " " + usedDouble);
+    used = 1;
+    alsoUsed = 2;
+    usedBoolean = true;
+    usedByte = 3;
+    usedChar = '?';
+    usedObject = new Object();
+    usedShort = 4;
+    usedDouble = 42.42;
+  }
+}
diff --git a/src/test/examples/shaking2/SubClass2.java b/src/test/examples/shaking2/SubClass2.java
new file mode 100644
index 0000000..6a0dcb2
--- /dev/null
+++ b/src/test/examples/shaking2/SubClass2.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class SubClass2 extends SuperClass {
+  private int used;
+  private int unused;
+  private int unusedToo;
+
+  public SubClass2(int used) {
+    this.used = used;
+  }
+
+  @Override
+  public void virtualMethod() {
+    System.out.println("SubClass2::virtualMethod");
+  }
+
+  @Override
+  public void virtualMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SubClass2::virtualMethod2 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  @Override
+  public void virtualMethod3() {
+    super.virtualMethod3();
+    System.out.println("SubClass2::virtualMethod3");
+  }
+
+  @Override
+  public void virtualMethod4(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    super.virtualMethod4(a1, a2, a3, a4, a5, a6, a7, a8);
+    System.out.println("SubClass2::virtualMethod4 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  @Override
+  public void interfaceMethod() {
+    System.out.println("SubClass2::interfaceMethod");
+  }
+
+  @Override
+  public void interfaceMethod2() {
+    System.out.println("SubClass2::interfaceMethod2");
+  }
+
+  @Override
+  public void interfaceMethod3() {
+    System.out.println("SubClass2::interfaceMethod3");
+  }
+
+  @Override
+  public void interfaceMethod4() {
+    System.out.println("SubClass2::interfaceMethod4");
+  }
+
+  @Override
+  public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SubClass1::interfaceMethod5 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+}
diff --git a/src/test/examples/shaking2/SuperClass.java b/src/test/examples/shaking2/SuperClass.java
new file mode 100644
index 0000000..f382671
--- /dev/null
+++ b/src/test/examples/shaking2/SuperClass.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public abstract class SuperClass implements Interface {
+  public int used;
+
+  public static void staticMethod() {
+    System.out.println("SuperClass::staticMethod");
+  }
+
+  public static void staticMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SuperClass::staticMethod2: " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  public static void unusedStaticMethod() {
+    System.out.println("SuperClass::unusedStaticMethod");
+  }
+
+  public void virtualMethod() {
+    System.out.println("SuperClass::virtualMethod");
+  }
+
+  public void virtualMethod2(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SuperClass::virtualMethod2 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  public void virtualMethod3() {
+    System.out.println("SuperClass::virtualMethod3");
+  }
+
+  public void virtualMethod4(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SuperClass::virtualMethod4 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+
+  public void unusedVirtualMethod() {
+    System.out.println("SuperClass::unusedVirtualMethod");
+  }
+
+  public void unusedInterfaceMethod() { System.out.println("SuperClass::unusedInterfaceMethod"); }
+
+  public void accessFields() {
+    System.out.println("SuperClass::fields: " + used);
+  }
+}
diff --git a/src/test/examples/shaking2/SuperInterface1.java b/src/test/examples/shaking2/SuperInterface1.java
new file mode 100644
index 0000000..7ec5d4e
--- /dev/null
+++ b/src/test/examples/shaking2/SuperInterface1.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public interface SuperInterface1 {
+  public void interfaceMethod2();
+}
diff --git a/src/test/examples/shaking2/SuperInterface2.java b/src/test/examples/shaking2/SuperInterface2.java
new file mode 100644
index 0000000..f9a1ff7
--- /dev/null
+++ b/src/test/examples/shaking2/SuperInterface2.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public interface SuperInterface2 extends SuperInterface1 {
+  public void interfaceMethod3();
+}
diff --git a/src/test/examples/shaking2/UnusedSubclass.java b/src/test/examples/shaking2/UnusedSubclass.java
new file mode 100644
index 0000000..400ec64
--- /dev/null
+++ b/src/test/examples/shaking2/UnusedSubclass.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking2;
+
+public class UnusedSubclass extends SuperClass {
+  private int used;
+  private int unused;
+
+  public UnusedSubclass(int used) {
+    this.used = used;
+  }
+
+  @Override
+  public void virtualMethod() {
+    System.out.println("UnusedSubclass::virtualMethod");
+  }
+
+  @Override
+  public void interfaceMethod() {
+    System.out.println("UnusedSubclass::interfaceMethod");
+  }
+
+  @Override
+  public void interfaceMethod2() {
+    System.out.println("UnusedSubclass::interfaceMethod2");
+  }
+
+  @Override
+  public void interfaceMethod3() {
+    System.out.println("UnusedSubclass::interfaceMethod3");
+  }
+
+  @Override
+  public void interfaceMethod4() {
+    System.out.println("UnusedSubclass::interfaceMethod4");
+  }
+
+  @Override
+  public void interfaceMethod5(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+    System.out.println("SubClass1::interfaceMethod5 " + (a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8));
+  }
+}
diff --git a/src/test/examples/shaking2/keep-rules-dont-shrink.txt b/src/test/examples/shaking2/keep-rules-dont-shrink.txt
new file mode 100644
index 0000000..6edaafc
--- /dev/null
+++ b/src/test/examples/shaking2/keep-rules-dont-shrink.txt
@@ -0,0 +1,7 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontshrink
+
+@keep-rules.txt
\ No newline at end of file
diff --git a/src/test/examples/shaking2/keep-rules.txt b/src/test/examples/shaking2/keep-rules.txt
new file mode 100644
index 0000000..e05f735
--- /dev/null
+++ b/src/test/examples/shaking2/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking2.Shaking {
+  public static void main(java.lang.String[]);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/A.java b/src/test/examples/shaking3/A.java
new file mode 100644
index 0000000..45413a6
--- /dev/null
+++ b/src/test/examples/shaking3/A.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@shaking3.UsedByReflection
+class A implements Comparable<A>, AnInterfaceWithATag {
+
+  @shaking3.RandomTag
+  public A() {
+    // Intentionally left empty.
+  }
+
+  @Override
+  public String toString() {
+    return "A";
+  }
+
+  public void unused() { }
+
+  @Override
+  public int compareTo(A other) {
+    if (other == this) {
+      return 0;
+    }
+    return 1;
+  }
+}
diff --git a/src/test/examples/shaking3/AnAbstractClass.java b/src/test/examples/shaking3/AnAbstractClass.java
new file mode 100644
index 0000000..e38501e
--- /dev/null
+++ b/src/test/examples/shaking3/AnAbstractClass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+abstract public class AnAbstractClass {
+
+  public void withAMethod() {
+
+  }
+}
diff --git a/src/test/examples/shaking3/AnInterfaceWithATag.java b/src/test/examples/shaking3/AnInterfaceWithATag.java
new file mode 100644
index 0000000..4072be1
--- /dev/null
+++ b/src/test/examples/shaking3/AnInterfaceWithATag.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@SubtypeUsedByReflection
+public interface AnInterfaceWithATag {
+
+}
diff --git a/src/test/examples/shaking3/B.java b/src/test/examples/shaking3/B.java
new file mode 100644
index 0000000..011f8a6
--- /dev/null
+++ b/src/test/examples/shaking3/B.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@RandomTag
+public class B {
+
+  public AnAbstractClass notVeryInteresting() {
+    return null;
+  }
+
+}
diff --git a/src/test/examples/shaking3/RandomTag.java b/src/test/examples/shaking3/RandomTag.java
new file mode 100644
index 0000000..2a33f91
--- /dev/null
+++ b/src/test/examples/shaking3/RandomTag.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@interface RandomTag {
+
+  Class type() default void.class;
+}
diff --git a/src/test/examples/shaking3/Shaking.java b/src/test/examples/shaking3/Shaking.java
new file mode 100644
index 0000000..76f6b4a
--- /dev/null
+++ b/src/test/examples/shaking3/Shaking.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+public class Shaking {
+
+  public static void main(String[] args)
+      throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+    Class t = Class.forName("shaking3.A");
+    Object object = t.newInstance();
+    System.out.println(object);
+  }
+}
diff --git a/src/test/examples/shaking3/SubtypeUsedByReflection.java b/src/test/examples/shaking3/SubtypeUsedByReflection.java
new file mode 100644
index 0000000..10db4bc
--- /dev/null
+++ b/src/test/examples/shaking3/SubtypeUsedByReflection.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+public @interface SubtypeUsedByReflection {
+
+}
diff --git a/src/test/examples/shaking3/UsedByReflection.java b/src/test/examples/shaking3/UsedByReflection.java
new file mode 100644
index 0000000..65ec5b0
--- /dev/null
+++ b/src/test/examples/shaking3/UsedByReflection.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking3;
+
+@interface UsedByReflection { }
diff --git a/src/test/examples/shaking3/keep-by-tag-default.txt b/src/test/examples/shaking3/keep-by-tag-default.txt
new file mode 100644
index 0000000..857e4e5
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-default.txt
@@ -0,0 +1,13 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+  public static void main(...);
+}
+
+-keep @shaking3.UsedByReflection class *
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag-on-method.txt b/src/test/examples/shaking3/keep-by-tag-on-method.txt
new file mode 100644
index 0000000..39f2674
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-on-method.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+  public static void main(...);
+}
+
+-keepclasseswithmembers class ** {
+  @shaking3.RandomTag <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag-via-interface.txt b/src/test/examples/shaking3/keep-by-tag-via-interface.txt
new file mode 100644
index 0000000..6202a25
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-via-interface.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+  public static void main(...);
+}
+
+-keep class ** implements @shaking3.SubtypeUsedByReflection ** {
+  <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag-with-pattern.txt b/src/test/examples/shaking3/keep-by-tag-with-pattern.txt
new file mode 100644
index 0000000..d880bf2
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag-with-pattern.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+  public static void main(...);
+}
+
+-keep @*.UsedByReflection class ** {
+  <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-by-tag.txt b/src/test/examples/shaking3/keep-by-tag.txt
new file mode 100644
index 0000000..08b5862
--- /dev/null
+++ b/src/test/examples/shaking3/keep-by-tag.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+  public static void main(...);
+}
+
+-keep @shaking3.UsedByReflection class ** {
+  <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking3/keep-no-abstract-classes.txt b/src/test/examples/shaking3/keep-no-abstract-classes.txt
new file mode 100644
index 0000000..6412247
--- /dev/null
+++ b/src/test/examples/shaking3/keep-no-abstract-classes.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point.
+-keep public class shaking3.Shaking {
+  public static void main(...);
+}
+
+-keep !abstract class ** {
+  <init>(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking4/A.java b/src/test/examples/shaking4/A.java
new file mode 100644
index 0000000..15926dc
--- /dev/null
+++ b/src/test/examples/shaking4/A.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public class A extends Superclass implements Interface {
+}
diff --git a/src/test/examples/shaking4/Interface.java b/src/test/examples/shaking4/Interface.java
new file mode 100644
index 0000000..4dd904c
--- /dev/null
+++ b/src/test/examples/shaking4/Interface.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public interface Interface {
+  public void virtualMethod();
+}
diff --git a/src/test/examples/shaking4/Shaking.java b/src/test/examples/shaking4/Shaking.java
new file mode 100644
index 0000000..cc8fa35
--- /dev/null
+++ b/src/test/examples/shaking4/Shaking.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public class Shaking {
+  public static void main(String[] args) {
+    A a = new A();
+    a.virtualMethod();
+  }
+}
diff --git a/src/test/examples/shaking4/Superclass.java b/src/test/examples/shaking4/Superclass.java
new file mode 100644
index 0000000..31f7537
--- /dev/null
+++ b/src/test/examples/shaking4/Superclass.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking4;
+
+public class Superclass {
+  public void virtualMethod() {
+    System.out.println("virtualMethod");
+  }
+}
diff --git a/src/test/examples/shaking4/keep-rules-dont-shrink.txt b/src/test/examples/shaking4/keep-rules-dont-shrink.txt
new file mode 100644
index 0000000..6edaafc
--- /dev/null
+++ b/src/test/examples/shaking4/keep-rules-dont-shrink.txt
@@ -0,0 +1,7 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontshrink
+
+@keep-rules.txt
\ No newline at end of file
diff --git a/src/test/examples/shaking4/keep-rules.txt b/src/test/examples/shaking4/keep-rules.txt
new file mode 100644
index 0000000..64953e2
--- /dev/null
+++ b/src/test/examples/shaking4/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking4.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking5/A.java b/src/test/examples/shaking5/A.java
new file mode 100644
index 0000000..5a9a738
--- /dev/null
+++ b/src/test/examples/shaking5/A.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking5;
+
+public class A extends Superclass {
+
+  public void virtualMethod() {
+    System.out.println("virtualMethod");
+  }
+}
diff --git a/src/test/examples/shaking5/Shaking.java b/src/test/examples/shaking5/Shaking.java
new file mode 100644
index 0000000..06c797d
--- /dev/null
+++ b/src/test/examples/shaking5/Shaking.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking5;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    A a = new A();
+    a.virtualMethod();
+  }
+}
diff --git a/src/test/examples/shaking5/Superclass.java b/src/test/examples/shaking5/Superclass.java
new file mode 100644
index 0000000..6807967
--- /dev/null
+++ b/src/test/examples/shaking5/Superclass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking5;
+
+public class Superclass {
+
+  private void virtualMethod() {
+    System.out.println("private virtualMethod");
+  }
+}
diff --git a/src/test/examples/shaking5/keep-rules.txt b/src/test/examples/shaking5/keep-rules.txt
new file mode 100644
index 0000000..616b03e
--- /dev/null
+++ b/src/test/examples/shaking5/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking5.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/Shaking.java b/src/test/examples/shaking6/Shaking.java
new file mode 100644
index 0000000..bc0b827
--- /dev/null
+++ b/src/test/examples/shaking6/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking6;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+  }
+}
diff --git a/src/test/examples/shaking6/Subclass.java b/src/test/examples/shaking6/Subclass.java
new file mode 100644
index 0000000..cc193ba
--- /dev/null
+++ b/src/test/examples/shaking6/Subclass.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking6;
+
+public class Subclass extends Superclass {
+
+  public void publicMethod() {
+    // Intentionally empty.
+  }
+
+  private void privateMethod() {
+    // Intentionally empty.
+  }
+
+  // Public method with same name as private method in superclass.
+  public void justAMethod() {
+    // Intentionally empty.
+  }
+
+  public void justAMethod(int ignore) {
+    // Intentionally empty.
+  }
+
+  public void justAMethod(boolean ignore) {
+    // Intentionally empty.
+  }
+
+  public int justAMethod(double ignore) {
+    // Intentionally empty.
+    return 0;
+  }
+
+  final void aFinalMethod() {
+    // Intentionally empty.
+  }
+}
diff --git a/src/test/examples/shaking6/Superclass.java b/src/test/examples/shaking6/Superclass.java
new file mode 100644
index 0000000..b7fefb7
--- /dev/null
+++ b/src/test/examples/shaking6/Superclass.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking6;
+
+public class Superclass {
+
+  private void justAMethod() {
+    // Intentionally empty
+  }
+}
diff --git a/src/test/examples/shaking6/keep-justAMethod-OnInt.txt b/src/test/examples/shaking6/keep-justAMethod-OnInt.txt
new file mode 100644
index 0000000..2ff630a
--- /dev/null
+++ b/src/test/examples/shaking6/keep-justAMethod-OnInt.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking6.Shaking {
+  public static void main(...);
+}
+
+# Also keep the int justAMethod
+-keep public class ** {
+  void justAMethod(int);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/keep-justAMethod-public.txt b/src/test/examples/shaking6/keep-justAMethod-public.txt
new file mode 100644
index 0000000..8d910f7
--- /dev/null
+++ b/src/test/examples/shaking6/keep-justAMethod-public.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking6.Shaking {
+  public static void main(...);
+}
+
+# Also keep the public justAMethod
+-keep public class ** {
+  public void justAMethod(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/keep-non-public.txt b/src/test/examples/shaking6/keep-non-public.txt
new file mode 100644
index 0000000..cc1316a
--- /dev/null
+++ b/src/test/examples/shaking6/keep-non-public.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep everything non-public and main
+-keep public class ** {
+  !public *;
+}
+
+-keep public class shaking6.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking6/keep-public.txt b/src/test/examples/shaking6/keep-public.txt
new file mode 100644
index 0000000..a4f1ea6
--- /dev/null
+++ b/src/test/examples/shaking6/keep-public.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep everything public
+-keep public class ** {
+  public *;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/Liar.java b/src/test/examples/shaking7/Liar.java
new file mode 100644
index 0000000..bad63a5
--- /dev/null
+++ b/src/test/examples/shaking7/Liar.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Liar extends Superclass {
+
+  public int theDoubleField;
+
+  public double theIntField;
+}
diff --git a/src/test/examples/shaking7/Shaking.java b/src/test/examples/shaking7/Shaking.java
new file mode 100644
index 0000000..8701ad7
--- /dev/null
+++ b/src/test/examples/shaking7/Shaking.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+  }
+}
diff --git a/src/test/examples/shaking7/Subclass.java b/src/test/examples/shaking7/Subclass.java
new file mode 100644
index 0000000..492acd7
--- /dev/null
+++ b/src/test/examples/shaking7/Subclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Subclass extends Superclass {
+
+  public int theIntField = 4;
+
+  public double theDoubleField = 2.0;
+
+  public String toString() {
+    return "Subclass";
+  }
+}
diff --git a/src/test/examples/shaking7/Superclass.java b/src/test/examples/shaking7/Superclass.java
new file mode 100644
index 0000000..a1027c5
--- /dev/null
+++ b/src/test/examples/shaking7/Superclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking7;
+
+public class Superclass {
+
+  private int theIntField = 42;
+
+  public final double theDoubleField = 1.0;
+
+  public String toString() {
+    return "Superclass";
+  }
+}
diff --git a/src/test/examples/shaking7/keep-double-fields.txt b/src/test/examples/shaking7/keep-double-fields.txt
new file mode 100644
index 0000000..813bce9
--- /dev/null
+++ b/src/test/examples/shaking7/keep-double-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+  public static void main(...);
+}
+
+# Also keep double fields (named theDoubleField, there is no wildcard on names...)
+-keep public class ** {
+  double theDoubleField;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/keep-public-fields.txt b/src/test/examples/shaking7/keep-public-fields.txt
new file mode 100644
index 0000000..c5e8af3
--- /dev/null
+++ b/src/test/examples/shaking7/keep-public-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+  public static void main(...);
+}
+
+# Also keep public fields
+-keep public class ** {
+  public <fields>;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/keep-public-theDoubleField-fields.txt b/src/test/examples/shaking7/keep-public-theDoubleField-fields.txt
new file mode 100644
index 0000000..941217b
--- /dev/null
+++ b/src/test/examples/shaking7/keep-public-theDoubleField-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+  public static void main(...);
+}
+
+# Also keep all public fields named theDoubleField
+-keep public class ** {
+  public *** theDoubleField;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking7/keep-public-theIntField-fields.txt b/src/test/examples/shaking7/keep-public-theIntField-fields.txt
new file mode 100644
index 0000000..8b93c59
--- /dev/null
+++ b/src/test/examples/shaking7/keep-public-theIntField-fields.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking7.Shaking {
+  public static void main(...);
+}
+
+# Also keep all public fields named theIntField
+-keep public class ** {
+  public *** theIntField;
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking8/OtherThing.java b/src/test/examples/shaking8/OtherThing.java
new file mode 100644
index 0000000..053501b
--- /dev/null
+++ b/src/test/examples/shaking8/OtherThing.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+public class OtherThing {
+
+  private final int otherField;
+
+  public OtherThing(int otherField) {
+    this.otherField = otherField;
+  }
+
+  @Override
+  public String toString() {
+    return "OtherThing(" + otherField + ")";
+  }
+}
diff --git a/src/test/examples/shaking8/Shaking.java b/src/test/examples/shaking8/Shaking.java
new file mode 100644
index 0000000..2c2d700
--- /dev/null
+++ b/src/test/examples/shaking8/Shaking.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+import java.util.Arrays;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    Thing[] empty = new Thing[0];
+    OtherThing[] one = {new OtherThing(1)};
+    callCloneOnArray(null);
+    System.out.println(Arrays.toString(empty));
+    System.out.println(Arrays.toString(one));
+  }
+
+  private static void callCloneOnArray(YetAnotherThing[] array) {
+    if (array != null) {
+      array.clone();
+    }
+  }
+}
diff --git a/src/test/examples/shaking8/Thing.java b/src/test/examples/shaking8/Thing.java
new file mode 100644
index 0000000..9b8f013
--- /dev/null
+++ b/src/test/examples/shaking8/Thing.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+public class Thing {
+
+  private final int aField;
+
+  public Thing(int aField) {
+    this.aField = aField;
+  }
+
+  @Override
+  public String toString() {
+    return "Thing(" + aField + ")";
+  }
+}
diff --git a/src/test/examples/shaking8/YetAnotherThing.java b/src/test/examples/shaking8/YetAnotherThing.java
new file mode 100644
index 0000000..ad3df45
--- /dev/null
+++ b/src/test/examples/shaking8/YetAnotherThing.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking8;
+
+public class YetAnotherThing {
+
+  private final int anInt;
+
+  public YetAnotherThing(int anInt) {
+    this.anInt = anInt;
+  }
+
+  @Override
+  public String toString() {
+    return "YetAnotherThing(" + anInt + ")";
+  }
+}
diff --git a/src/test/examples/shaking8/keep-rules.txt b/src/test/examples/shaking8/keep-rules.txt
new file mode 100644
index 0000000..a97f896
--- /dev/null
+++ b/src/test/examples/shaking8/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking8.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shaking9/Shaking.java b/src/test/examples/shaking9/Shaking.java
new file mode 100644
index 0000000..578dda2
--- /dev/null
+++ b/src/test/examples/shaking9/Shaking.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking9;
+
+public class Shaking {
+
+  public static void main(String[] args) {
+    Subclass thing = new Subclass();
+    thing.callingSuper();
+  }
+}
diff --git a/src/test/examples/shaking9/Subclass.java b/src/test/examples/shaking9/Subclass.java
new file mode 100644
index 0000000..7c1e809
--- /dev/null
+++ b/src/test/examples/shaking9/Subclass.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking9;
+
+public class Subclass extends Superclass {
+
+  @Override
+  public void aMethod() {
+    System.out.println("Called aMethod in Subclass");
+  }
+
+  @Override
+  public void callingSuper() {
+    System.out.println("Called callingSuper in Subclass");
+    super.callingSuper();
+    super.aMethod();
+  }
+}
diff --git a/src/test/examples/shaking9/Superclass.java b/src/test/examples/shaking9/Superclass.java
new file mode 100644
index 0000000..90041ee
--- /dev/null
+++ b/src/test/examples/shaking9/Superclass.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shaking9;
+
+public class Superclass {
+
+  public void aMethod() {
+    System.out.println("Called aMethod in Superclass");
+  }
+
+  public void callingSuper() {
+    System.out.println("Called callingSuper in Superclass");
+  }
+}
diff --git a/src/test/examples/shaking9/keep-rules.txt b/src/test/examples/shaking9/keep-rules.txt
new file mode 100644
index 0000000..e8b0919
--- /dev/null
+++ b/src/test/examples/shaking9/keep-rules.txt
@@ -0,0 +1,12 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class shaking9.Shaking {
+  public static void main(...);
+}
+
+# allow access modification to enable minifcation
+-allowaccessmodification
diff --git a/src/test/examples/shakinglib/HasAFoo.java b/src/test/examples/shakinglib/HasAFoo.java
new file mode 100644
index 0000000..077c86f
--- /dev/null
+++ b/src/test/examples/shakinglib/HasAFoo.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public interface HasAFoo {
+
+  HasAGetter foo();
+}
diff --git a/src/test/examples/shakinglib/HasAGetter.java b/src/test/examples/shakinglib/HasAGetter.java
new file mode 100644
index 0000000..9195b76
--- /dev/null
+++ b/src/test/examples/shakinglib/HasAGetter.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public interface HasAGetter {
+
+  int getIt();
+}
diff --git a/src/test/examples/shakinglib/IndirectMethodInvoker.java b/src/test/examples/shakinglib/IndirectMethodInvoker.java
new file mode 100644
index 0000000..1ec7415
--- /dev/null
+++ b/src/test/examples/shakinglib/IndirectMethodInvoker.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public class IndirectMethodInvoker {
+
+  public int invokeFoo(HasAFoo thing) {
+    return thing.foo().getIt();
+  }
+}
diff --git a/src/test/examples/shakinglib/LibraryClass.java b/src/test/examples/shakinglib/LibraryClass.java
new file mode 100644
index 0000000..f50bdf4
--- /dev/null
+++ b/src/test/examples/shakinglib/LibraryClass.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package shakinglib;
+
+public class LibraryClass {
+  public static int staticIntField;
+
+  public int intField;
+}
diff --git a/src/test/examples/staticfield/StaticField.java b/src/test/examples/staticfield/StaticField.java
new file mode 100644
index 0000000..f4d5854
--- /dev/null
+++ b/src/test/examples/staticfield/StaticField.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package staticfield;
+
+public class StaticField {
+
+  // Final static initialized fields, out of order, in dex these must be sorted by field idx.
+  public static final String fieldB = "B";
+  public static final String fieldC = "C";
+  public static final String fieldA = "A";
+
+  public static StaticField field = null;
+
+  private int x;
+
+  public StaticField(int x) {
+    this.x = x;
+  }
+
+  @Override
+  public String toString() {
+    return "" + x;
+  }
+
+  public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+    StaticField value = new StaticField(101010);
+    StaticField.field = value;
+    System.out.println(StaticField.field);
+    System.out.println(value.field);
+
+    System.out.print(StaticField.fieldA);
+    System.out.print(StaticField.fieldB);
+    System.out.println(StaticField.fieldC);
+
+    // Check that we can access the same static final value via the class object.
+    System.out.print(StaticField.class.getField("fieldA").get(value));
+    System.out.print(StaticField.class.getField("fieldB").get(value));
+    System.out.println(StaticField.class.getField("fieldC").get(value));
+  }
+}
diff --git a/src/test/examples/stringbuilding/StringBuilding.java b/src/test/examples/stringbuilding/StringBuilding.java
new file mode 100644
index 0000000..18152fb
--- /dev/null
+++ b/src/test/examples/stringbuilding/StringBuilding.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'stringbuilding.dex' is what is run.
+
+package stringbuilding;
+
+class StringBuilding {
+
+  static class X {
+
+    public String toString() {
+      return "an X";
+    }
+  }
+
+  public static void main(String[] args) {
+    StringBuilder builder = new StringBuilder();
+    buildWithStatements(builder);
+    buildWithExpressions(builder);
+    System.out.print(builder);
+    System.out.println(buildWithConcat());
+    System.out.print(buildWithAllAppendSignatures());
+  }
+
+  private static void buildWithStatements(StringBuilder builder) {
+    builder.append("a");
+    builder.append(2);
+    builder.append("c");
+    builder.append("-");
+  }
+
+  private static void buildWithExpressions(StringBuilder builder) {
+    builder.append("x").append('y').append("z").append("-");
+  }
+
+  private static String buildWithConcat() {
+    return "a" + "b" + "c" + someValue() + "x" + "y" + "z";
+  }
+
+  private static String buildWithAllAppendSignatures() {
+    CharSequence seq = "1234";
+    StringBuilder builder = new StringBuilder();
+    builder
+        .append(true)
+        .append('A')
+        .append(new char[]{'B', 'C'})
+        .append(new char[]{'C', 'D', 'E', 'F'}, 1, 2)
+        .append(seq)
+        .append(seq, 1, 3)
+        .append(2.2)
+        .append(1.1f)
+        .append(0)
+        .append(1L)
+        .append(new X())
+        .append("string")
+        .append(new StringBuilder("builder"));
+    return builder.toString();
+  }
+
+  private static int someValue() {
+    return 7;
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/switches/Switches.java b/src/test/examples/switches/Switches.java
new file mode 100644
index 0000000..588e541
--- /dev/null
+++ b/src/test/examples/switches/Switches.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'switches.dex' is what is run.
+
+package switches;
+
+class Switches {
+
+  public static void packedSwitch(int value) {
+    System.out.print("packedSwitch cases: ");
+    switch (value) {
+      case 0:
+        System.out.print("0 ");
+      case 1:
+      case 2:
+        System.out.print("1 2 ");
+        break;
+      case 3:
+        System.out.print("3 ");
+        break;
+    }
+    System.out.println("after switch " + value);
+  }
+
+  public static void sparseSwitch(int value) {
+    switch (value) {
+      case 0:
+        System.out.println("0 ");
+      case 100:
+        System.out.println("100 ");
+        break;
+      case 200:
+        System.out.println("200 ");
+        break;
+    }
+    System.out.println("after switch " + value);
+  }
+
+  public static void switchWithLocals(int value) {
+    switch (value) {
+      case 0: {
+        int i = 42;
+        System.out.println(" " + i + value);
+        break;
+      }
+      case 2: {
+        double d = 1.0;
+        System.out.println(" " + d + value);
+        break;
+      }
+    }
+  }
+
+  public static void maybePackedSwitch(int value) {
+    switch (value) {
+      case 0:
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+      case 8:
+      case 10:
+      case 11:
+      case 12:
+      case 13:
+      case 14:
+      case 15:
+      case 16:
+      case 17:
+      case 18:
+      case 19:
+      case 20:
+      case 21:
+        System.out.print("0-21 ");
+        break;
+      case 60:
+        System.out.print("60 ");
+        break;
+    }
+    System.out.println("after switch " + value);
+  }
+
+  public static void main(String[] args) {
+    packedSwitch(0);
+    packedSwitch(1);
+    packedSwitch(2);
+    packedSwitch(-1);  // No such case, use fallthrough.
+    sparseSwitch(0);
+    sparseSwitch(100);
+    sparseSwitch(200);
+    sparseSwitch(-1);  // No such case, use fallthrough.
+    switchWithLocals(0);
+    switchWithLocals(2);
+    maybePackedSwitch(1);
+    maybePackedSwitch(10);
+    maybePackedSwitch(40);  // Fallthrough.
+    maybePackedSwitch(60);
+  }
+}
diff --git a/src/test/examples/sync/Sync.java b/src/test/examples/sync/Sync.java
new file mode 100644
index 0000000..7459d0e
--- /dev/null
+++ b/src/test/examples/sync/Sync.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package sync;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+public class Sync {
+
+  public static final int THREADS = 10;
+  public static final int ITERATIONS = 10;
+
+  private static final int INITIAL_SHARED_STATE = -1;
+  private static final long SLEEP = 10;
+
+  // Shared mutable state that is tested to be consistent
+  private static int sharedState = INITIAL_SHARED_STATE;
+  private static boolean shouldThrow = false;
+
+  // Copy of interface java.util.function.Consumer to make this test work without a Java 8 runtime
+  // library
+  public interface Consumer<T> {
+
+    void accept(T t);
+  }
+
+  public static void couldThrow(int index) {
+    if (shouldThrow) throw new RuntimeException();
+    // Copy shared state and trash it (we set our index).
+    int local = sharedState;
+    sharedState = index;
+    try {
+      Thread.sleep(SLEEP);
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+    // Restore the shared state if it is still valid.
+    if (sharedState == index) {
+      sharedState = local;
+    }
+  }
+
+  public static synchronized void staticSynchronized(int index) {
+    System.out.println("static");
+    couldThrow(index);
+    System.out.println("end");
+  }
+
+  public synchronized void instanceSynchronized(int index) {
+    System.out.println("instance");
+    couldThrow(index);
+    System.out.println("end");
+  }
+
+  public void manualSynchronized(int index) {
+    System.out.println("manual");
+    synchronized (this) {
+      couldThrow(index);
+    }
+    System.out.println("manual");
+  }
+
+  public synchronized void tryCatchSynchronized(int index) {
+    System.out.println("trycatch");
+    try {
+      couldThrow(index);
+      try {
+        couldThrow(index);
+      } finally {
+        System.out.println("end");
+        return;
+      }
+    } catch (RuntimeException e) {
+      System.out.println("caught & end");
+      return;
+    } catch (Throwable e) {
+      System.out.println("caught other");
+    }
+    System.out.println("end");
+  }
+
+  public static synchronized void throwStaticSynchronized() {
+    throw new RuntimeException();
+  }
+
+  public synchronized void throwInstanceSynchronized() {
+    throw new RuntimeException();
+  }
+
+  public static void run(ExecutorService service, final Consumer<Integer> fn)
+      throws ExecutionException, InterruptedException {
+    Future[] results = new Future[ITERATIONS];
+    for (int i = 0; i < ITERATIONS; ++i) {
+      final int index = i;
+      results[i] = service.submit(new Runnable() {
+        @Override
+        public void run() {
+          fn.accept(index);
+        }
+      });
+    }
+    for (Future result : results) {
+      result.get();
+    }
+    if (sharedState != INITIAL_SHARED_STATE) {
+      throw new RuntimeException("Synchronization error!");
+    }
+  }
+
+  public static void main(String[] args) throws ExecutionException, InterruptedException {
+    shouldThrow = args.length > 100;
+    ExecutorService service = Executors.newFixedThreadPool(THREADS);
+    run(service, new Consumer<Integer>() {
+      @Override
+      public void accept(Integer index) {
+         Sync.staticSynchronized(index);
+      }
+    });
+    final Sync sync = new Sync();
+    run(service, new Consumer<Integer>() {
+      @Override
+      public void accept(Integer index) {
+        sync.instanceSynchronized(index);
+      }
+    });
+    run(service, new Consumer<Integer>() {
+      @Override
+      public void accept(Integer index) {
+        sync.manualSynchronized(index);
+      }
+    });
+    run(service, new Consumer<Integer>() {
+      @Override
+      public void accept(Integer index) {
+        sync.tryCatchSynchronized(index);
+      }
+    });
+    service.shutdown();
+    service.awaitTermination(5, TimeUnit.SECONDS);
+    try {
+      Sync.throwStaticSynchronized();
+      throw new Error("expected throw");
+    } catch (RuntimeException e) {
+      System.out.println("caught throw");
+    }
+    try {
+      sync.throwInstanceSynchronized();
+      throw new Error("expected throw");
+    } catch (RuntimeException e) {
+      System.out.println("caught throw");
+    }
+  }
+}
diff --git a/src/test/examples/throwing/Overloaded.java b/src/test/examples/throwing/Overloaded.java
new file mode 100644
index 0000000..44b5435
--- /dev/null
+++ b/src/test/examples/throwing/Overloaded.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package throwing;
+
+import java.util.List;
+
+public abstract class Overloaded {
+
+  public int aMethod(int x) {
+    return 0;
+  }
+
+  public int conflictingMethod(int x) {
+    return 0;
+  }
+
+  public abstract int bMethod(double x);
+
+  public int conflictingMethod(double x) {
+    return 0;
+  }
+
+  public int cMethod(boolean x) {
+    return 0;
+  }
+
+  public int conflictingMethod(boolean x) {
+    return 0;
+  }
+
+  public int anotherConflict(boolean x) {
+    return 0;
+  }
+
+  public int unique(List x) {
+    return 0;
+  }
+}
diff --git a/src/test/examples/throwing/RenamedClass.java b/src/test/examples/throwing/RenamedClass.java
new file mode 100644
index 0000000..a3fd537
--- /dev/null
+++ b/src/test/examples/throwing/RenamedClass.java
@@ -0,0 +1,59 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'throwing.dex' is what is run.
+
+package throwing;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class' logic is completely bogus. The only purpose is to be recursive to avoid inlining
+ * and terminate.
+ */
+public class RenamedClass {
+  public List list = new ArrayList();
+
+  public List getList() {
+    if (list == null) { // always false
+      setList(getList());
+    }
+    return list;
+  }
+
+  public void setList(List list) {
+    if (list == null) {
+      setList(new LinkedList());
+    } else {
+      this.list = list;
+    }
+  }
+
+  // Another method with the same signature as getList
+  public void swap(List list) {
+    List before = getList();
+    setList(list);
+    if (before == null) { // always false
+      swap(list);
+    }
+  }
+
+  static RenamedClass create() {
+    RenamedClass theClass = new RenamedClass();
+    theClass.setList(new LinkedList());
+    return theClass;
+  }
+
+  void takeThingsForASpin(int value) {
+    if (value == 42) {
+      swap(new LinkedList<>());
+      setList(getList());
+    } else {
+      takeThingsForASpin(42);
+    }
+  }
+}
diff --git a/src/test/examples/throwing/Throwing.java b/src/test/examples/throwing/Throwing.java
new file mode 100644
index 0000000..574556f
--- /dev/null
+++ b/src/test/examples/throwing/Throwing.java
@@ -0,0 +1,227 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'throwing.dex' is what is run.
+
+package throwing;
+
+import java.util.Collections;
+import java.util.List;
+
+class Throwing {
+
+  static int[] used = new int[10];
+
+  public static void main(String[] args) {
+    try {
+      used[0] = throwAtFistLine(42);
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+    try {
+      used[1] = throwInMiddle(42);
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+    try {
+      used[2] = throwAfterMultiInline(42);
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+    try {
+      int value = magicNumber(42);
+      // This throws after an inline, on the top-level.
+      used[6] = value * 10;
+      used[7] = anotherInlinedFunction(value);
+      //
+      // Some space to increase line numbers...
+      //
+      used[8] = value / (value & 0x0);
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+
+    Nested nested = new Nested();
+
+    try {
+      used[3] = nested.justThrow(42);
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+
+    nested.doSomethingUseless();
+
+    used[0] += Nested.callAMethod(nested, 11);
+    used[0] += Nested.callAMethod(nested, 42);
+
+    RenamedClass aInstance = RenamedClass.create();
+    aInstance.takeThingsForASpin(42);
+
+    System.out.print(used[0]);
+
+    try {
+      throwInAFunctionThatIsNotInlinedAndCalledTwice();
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+
+    try {
+      throwInAFunctionThatIsNotInlinedAndCalledTwice();
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+
+    try {
+      aFunctionThatCallsAnInlinedMethodThatThrows(Collections.emptyList());
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+
+    try {
+      anotherFunctionThatCallsAnInlinedMethodThatThrows("string");
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+
+    try {
+      aFunctionsThatThrowsBeforeAnInlinedMethod(magicNumber(42));
+    } catch (Exception e) {
+      printFrameHead(e);
+    }
+  }
+
+  public static int magicNumber(int value) {
+    if (value < 0) {
+      return magicNumber(value++);
+    }
+    return value;
+  }
+
+  public static void printFrameHead(Exception e) {
+    for (StackTraceElement element : e.getStackTrace()) {
+      System.out.println("FRAME: " + element);
+    }
+  }
+
+  // This throws in the first line of the method.
+  public static int throwAtFistLine(int value) {
+    int aValue = value * 2 / (value & 0x0);
+    return aValue;
+  }
+
+  // This throws a little further down.
+  public static int throwInMiddle(int value) {
+    used[2] = value * 10;
+    used[3] = value >> 3;
+    used[4] = value / (value & 0x0);
+    used[5] = value * 20;
+    return value >> 5;
+  }
+
+  // This throws after another inlined function.
+  public static int throwAfterMultiInline(int value) {
+    used[6] = value * 10;
+    used[7] = anotherInlinedFunction(value);
+    //
+    // Some space to increase line numbers...
+    //
+    used[8] = value / (value & 0x0);
+    return value >> 5;
+  }
+
+  public static int throwInAFunctionThatIsNotInlinedAndCalledTwice() {
+    for (int i = 0; i < 10; i++) {
+      used[9] += i;
+      System.out.println("Increment by one!");
+    }
+    System.out.println("Incremented by 10.");
+    used[9] = used[9] / (used[9] & 0x0);
+    return used[9];
+  }
+
+  // Small method that throws and can be inlined.
+  private static int anotherThrowingMethodToInline(int value) {
+    used[4] = value / (value & 0x0);
+    return value >> 5;
+  }
+
+  // It is important that this function uses an argument type that is otherwise unused, so it gets
+  // the same minified name.
+  public static int aFunctionThatCallsAnInlinedMethodThatThrows(List aList) {
+    used[9] = aList.size();
+    for (int i = 0; i < 10; i++) {
+      used[9] += i;
+      System.out.println("Increment by one!");
+    }
+    System.out.println("Incremented by 10.");
+    used[9] = anotherThrowingMethodToInline(used[9]);
+    return used[9];
+  }
+
+  // Small method that throws and can be inlined.
+  private static int yetAnotherThrowingMethodToInline(int value) {
+    used[5] = value / (value & 0x0);
+    return value >> 5;
+  }
+
+  // It is important that this function uses an argument type that is otherwise unused, so it gets
+  // the same minified name.
+  public static int anotherFunctionThatCallsAnInlinedMethodThatThrows(String aString) {
+    used[0] = aString.length();
+    for (int i = 0; i < 10; i++) {
+      used[8] += i;
+      System.out.println("Increment by one!");
+    }
+    System.out.println("Incremented by 10.");
+    used[8] = yetAnotherThrowingMethodToInline(used[8]);
+    return used[8];
+  }
+
+  public static int aFunctionsThatThrowsBeforeAnInlinedMethod(int value) {
+    used[1] = value / (value & 0x0);
+    anotherInlinedFunction(used[1]);
+    return used[1];
+  }
+
+  // This will be inlined above but does not throw
+  public static int anotherInlinedFunction(int value) {
+    return value / (value & 0xff);
+  }
+
+  /**
+   * A nested class with different kind of methods to have inlining from a nested class and also
+   * renamings of a nested class in the mapping file.
+   *
+   * <p>Some methods are recursive to avoid inlining.
+   */
+  static class Nested {
+
+    int justThrow(int value) {
+      return used[8] = value / (value & 0x0);
+    }
+
+    // This will also be inlined. Not used in test but for generating interesting mapping files.
+    void doSomethingUseless() {
+      Throwing.used[9] = 11;
+    }
+
+    static int callAMethod(Nested on, int value) {
+      if (value > 20) {
+        return callAMethod(on, value - 1);
+      } else {
+        return on.aMethod(value);
+      }
+    }
+
+    int aMethod(int value) {
+      if (value > 10) {
+        return aMethod(value - 1);
+      } else {
+        return value;
+      }
+    }
+  }
+
+}
diff --git a/src/test/examples/throwing/proguard.cfg b/src/test/examples/throwing/proguard.cfg
new file mode 100644
index 0000000..6de724a
--- /dev/null
+++ b/src/test/examples/throwing/proguard.cfg
@@ -0,0 +1,14 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-keep class throwing.Throwing {
+  public static void main(java.lang.String[]);
+}
+
+-keep,allowobfuscation class throwing.Overloaded {
+  *;
+}
+
+-keepattributes SourceFile,LineNumberTable
+
diff --git a/src/test/examples/trivial/Trivial.java b/src/test/examples/trivial/Trivial.java
new file mode 100644
index 0000000..b6b8299
--- /dev/null
+++ b/src/test/examples/trivial/Trivial.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package trivial;
+
+public class Trivial {
+  public static void main(String[] args) {
+  }
+}
diff --git a/src/test/examples/trycatch/TryCatch.java b/src/test/examples/trycatch/TryCatch.java
new file mode 100644
index 0000000..509e7c9
--- /dev/null
+++ b/src/test/examples/trycatch/TryCatch.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'trycatch.dex' is what is run.
+
+package trycatch;
+
+class TryCatch {
+
+
+  private static class Thrower {
+
+    private boolean shouldThrow;
+
+    public Thrower(boolean shouldThrow) {
+      this.shouldThrow = shouldThrow;
+    }
+
+    public void maybeThrow() {
+      if (shouldThrow) {
+        throw new RuntimeException();
+      }
+    }
+  }
+
+  private static void throwOrCatch(Thrower thrower) {
+    String firstJunk = "junk 1";
+    String result = "Did not throw";
+    String secondJunk = "junk 2";
+    try {
+      thrower.maybeThrow();
+    } catch (Throwable e) {
+      String tmp = secondJunk;
+      secondJunk = firstJunk;
+      result = "Did throw";
+      firstJunk = tmp;
+    }
+    System.out.println(result);
+    System.out.println(firstJunk);
+    System.out.println(secondJunk);
+  }
+
+  private static void throwOnPositive(int i) {
+    if (i > 0) {
+      throw new RuntimeException();
+    }
+  }
+
+  private static int loopWhileThrow(int i) {
+    while (true) {
+      int result = 100;
+      try {
+        throwOnPositive(i--);
+        return result;
+      } catch (Throwable e) {
+      }
+    }
+  }
+
+  private static void foo() {
+    try {
+      throw new RuntimeException();
+    } catch (IllegalStateException e) {
+      System.out.println("Error!");
+    }
+  }
+
+  private static String tryCatchPhi(int i) {
+    String result = "one";
+    String otherResult = "two";
+    try {
+      throwOnPositive(i++);
+      result = otherResult;
+      throwOnPositive(i);
+    } catch (Throwable e) {
+      return result;
+    }
+    return "three";
+  }
+
+  private static void emptyMethod(int x) {
+  }
+
+  private static int regressRemoveCatchHandlers(int a, int b) {
+    try {
+      if (a == b) {
+        emptyMethod(a);
+        return 0;
+      } else {
+        emptyMethod(a);
+        return a / b;
+      }
+    } catch (IllegalStateException | ArithmeticException e) {
+      System.out.println("Error!");
+    }
+    return -1;
+  }
+
+  static final int NUM_OF_EVENTS_PER_FILE = 8192;
+
+  public static Object regressMultiNewArray() {
+    try {
+      int[][] args = new int[3][NUM_OF_EVENTS_PER_FILE];
+      return args;
+    } catch (Exception e) {
+      return null;
+    }
+  }
+
+  // Create a class hierarchy which introduces a bridge method (B::x returning Object) by
+  // overriding a method (A::x) with one with a narrower type.
+  public static class A {
+
+    public Object x() {
+      return null;
+    }
+  }
+
+  public static class B extends A {
+
+    // This bridge method will be generated.
+    // public Object x() {
+    //   return result of call "String x()"
+    //}
+
+    public String x() {
+      return null;
+    }
+  }
+
+  static B b = new B();
+
+  public static Object regressBridgeMethod() {
+    String s;
+    try {
+      s = b.x();
+    } catch (ClassCastException e) {
+      return null;
+    }
+    return s;
+  }
+
+  public static void main(String[] args) {
+    loopWhileThrow(-100);
+    throwOrCatch(new Thrower(false));
+    throwOrCatch(new Thrower(true));
+    System.out.println(tryCatchPhi(-1));
+    System.out.println(tryCatchPhi(0));
+    System.out.println(tryCatchPhi(1));
+    try {
+      foo();
+    } catch (RuntimeException e) {
+      System.out.print("Success!");
+    }
+    regressRemoveCatchHandlers(1, 0);
+    regressMultiNewArray();
+    regressBridgeMethod();
+  }
+}
\ No newline at end of file
diff --git a/src/test/examples/trycatchmany/TryCatchMany.java b/src/test/examples/trycatchmany/TryCatchMany.java
new file mode 100644
index 0000000..eb07f27
--- /dev/null
+++ b/src/test/examples/trycatchmany/TryCatchMany.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code is not run directly. It needs to be compiled to dex code.
+// 'trycatchmany.dex' is what is run.
+
+package trycatchmany;
+
+class TryCatchMany {
+  public static void main(String[] args) {
+    try {
+      foo();
+      try {
+        bar();
+      } catch (RuntimeException e) {
+        System.out.println("Another error");
+      }
+      foo();
+    } catch (IllegalStateException e) {
+      System.out.print("Error again");
+    } catch (RuntimeException e) {
+      System.out.print("Success");
+    } finally {
+      System.out.print("!");
+    }
+  }
+
+  private static void foo() {
+    try {
+      throw new RuntimeException();
+    } catch (IllegalStateException e) {
+      System.out.println("Error");
+    }
+  }
+
+  private static void bar() {
+    throw new IllegalStateException();
+  }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/C2.java b/src/test/examplesAndroidN/interfacemethods/C2.java
new file mode 100644
index 0000000..da0d112
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/C2.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package interfacemethods;
+
+public class C2 implements I2 {
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/DefaultMethods.java b/src/test/examplesAndroidN/interfacemethods/DefaultMethods.java
new file mode 100644
index 0000000..49e792c
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/DefaultMethods.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package interfacemethods;
+
+public class DefaultMethods {
+
+  public static void main(String[] args) {
+    new C2().d1();
+  }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/I1.java b/src/test/examplesAndroidN/interfacemethods/I1.java
new file mode 100644
index 0000000..52b00ae
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/I1.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package interfacemethods;
+
+public interface I1 {
+
+  static void s1() {
+    System.out.println("s1");
+  }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/I2.java b/src/test/examplesAndroidN/interfacemethods/I2.java
new file mode 100644
index 0000000..6973c21
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/I2.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package interfacemethods;
+
+public interface I2 {
+
+  default void d1() {
+    System.out.println("d1");
+  }
+}
diff --git a/src/test/examplesAndroidN/interfacemethods/StaticInterfaceMethods.java b/src/test/examplesAndroidN/interfacemethods/StaticInterfaceMethods.java
new file mode 100644
index 0000000..32a651e
--- /dev/null
+++ b/src/test/examplesAndroidN/interfacemethods/StaticInterfaceMethods.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package interfacemethods;
+
+public class StaticInterfaceMethods {
+
+  public static void main(String[] args) {
+    I1.s1();
+  }
+}
diff --git a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
new file mode 100644
index 0000000..0d4d5c5
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
@@ -0,0 +1,177 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package invokecustom;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+interface J {
+
+  default void targetMethodTest8() {
+    System.out.println("targetMethodTest8 from J");
+  }
+
+  default void targetMethodTest7() {
+    System.out.println("targetMethodTest7 from J");
+  }
+
+  default void targetMethodTest6() {
+    System.out.println("targetMethodTest6 from J");
+  }
+}
+
+interface I extends J {
+  void targetMethodTest8();
+
+  default void targetMethodTest6() {
+    System.out.println("targetMethodTest6 from I");
+  }
+
+  default void targetMethodTest9() {
+    System.out.println("targetMethodTest9 from I");
+  }
+
+  default void targetMethodTest10() {
+    System.out.println("targetMethodTest10 from I");
+  }
+}
+
+abstract class Super {
+  public void targetMethodTest5() {
+    System.out.println("targetMethodTest5 from Super");
+  }
+
+  abstract void targetMethodTest10();
+}
+
+public class InvokeCustom extends Super implements I {
+
+  private static String staticField1 = "StaticField1";
+
+  private String instanceField1 = "instanceField1";
+
+  private static void targetMethodTest1() {
+    System.out.println("Hello World!");
+  }
+
+  private static void targetMethodTest2(boolean z, byte b, char c, short s, int i, float f, long l,
+      double d, String str) {
+    System.out.println(z);
+    System.out.println(b);
+    System.out.println(c);
+    System.out.println(s);
+    System.out.println(i);
+    System.out.println(f);
+    System.out.println(l);
+    System.out.println(d);
+    System.out.println(str);
+  }
+
+  private static void targetMethodTest3() {
+  }
+
+  public static CallSite bsmLookupStatic(MethodHandles.Lookup caller, String name, MethodType type)
+      throws NoSuchMethodException, IllegalAccessException {
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+    return new ConstantCallSite(targetMH.asType(type));
+  }
+
+  public static CallSite bsmLookupStaticWithExtraArgs(
+      MethodHandles.Lookup caller, String name, MethodType type, int i, long l, float f, double d)
+      throws NoSuchMethodException, IllegalAccessException {
+    System.out.println(i);
+    System.out.println(l);
+    System.out.println(f);
+    System.out.println(d);
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+    return new ConstantCallSite(targetMH.asType(type));
+  }
+
+  @Override
+  public void targetMethodTest5() {
+    System.out.println("targetMethodTest5 from InvokeCustom");
+  }
+
+  private static void targetMethodTest4() {
+    System.out.println("targetMethodTest4");
+  }
+
+  public static CallSite bsmCreateCallSite(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+    final MethodHandle targetMH = MethodHandles.lookup().findSpecial(Super.class,
+                "targetMethodTest5", MethodType.methodType(void.class), InvokeCustom.class);
+    return new ConstantCallSite(targetMH);
+  }
+
+  public static CallSite bsmCreateCallCallingtargetMethodTest6(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+    final MethodHandle targetMH =
+        MethodHandles.lookup().findVirtual(
+            I.class, "targetMethodTest6", MethodType.methodType(void.class));
+    return new ConstantCallSite(targetMH);
+  }
+
+  public static CallSite bsmCreateCallCallingtargetMethodTest7(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+    final MethodHandle targetMH =
+        MethodHandles.lookup().findVirtual(
+            J.class, "targetMethodTest7", MethodType.methodType(void.class));
+    return new ConstantCallSite(targetMH);
+  }
+
+  public void targetMethodTest8() {
+    System.out.println("targetMethodTest8 from InvokeCustom");
+  }
+
+  public static CallSite bsmCreateCallCallingtargetMethodTest8(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+    final MethodHandle targetMH =
+        MethodHandles.lookup().findVirtual(
+            J.class, "targetMethodTest8", MethodType.methodType(void.class));
+    return new ConstantCallSite(targetMH);
+  }
+
+  public static CallSite bsmCreateCallCallingtargetMethodTest9(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+    final MethodHandle targetMH =
+        MethodHandles.lookup().findVirtual(
+            InvokeCustom.class, "targetMethodTest9", MethodType.methodType(void.class));
+    return new ConstantCallSite(targetMH);
+  }
+
+  public void targetMethodTest10() {
+    System.out.println("targetMethodTest10 from InvokeCustom");
+  }
+
+  public static CallSite bsmCreateCallCallingtargetMethodTest10(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
+    final MethodHandle targetMH =
+        MethodHandles.lookup().findVirtual(
+            InvokeCustom.class, "targetMethodTest10", MethodType.methodType(void.class));
+    return new ConstantCallSite(targetMH);
+  }
+
+  public static CallSite bsmCreateCallCallingtargetMethod(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    return new ConstantCallSite(mh);
+  }
+}
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
new file mode 100644
index 0000000..883106f
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -0,0 +1,422 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package invokecustom;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class TestGenerator {
+
+  private final Path classNamePath;
+
+  public static void main(String[] args) throws IOException {
+    assert args.length == 1;
+    TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
+        TestGenerator.class.getPackage().getName(), InvokeCustom.class.getSimpleName() + ".class"));
+    testGenerator.generateTests();
+  }
+
+  public TestGenerator(Path classNamePath) {
+    this.classNamePath = classNamePath;
+  }
+
+  private void generateTests() throws IOException {
+    ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+    cr.accept(
+        new ClassVisitor(Opcodes.ASM5, cw) {
+          @Override
+          public void visitEnd() {
+            generateMethodTest1(cw);
+            generateMethodTest2(cw);
+            generateMethodTest3(cw);
+            generateMethodTest4(cw);
+            generateMethodTest5(cw);
+            generateMethodTest6(cw);
+            generateMethodTest7(cw);
+            generateMethodTest8(cw);
+            generateMethodTest9(cw);
+            generateMethodTest10(cw);
+            generateMethodTest11(cw);
+            generateMethodTest12(cw);
+            generateMethodTest13(cw);
+            generateMethodMain(cw);
+            super.visitEnd();
+          }
+        }, 0);
+    new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+  }
+
+  /* generate main method that only call all test methods. */
+  private void generateMethodMain(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(
+            Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test1", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test2", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test3", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test4", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test5", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test6", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test7", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test8", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test9", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test10", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test11", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test12", "()V", false);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test13", "()V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method without extra args and no arg
+   *  to the target method.
+   */
+  private void generateMethodTest1(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
+            null, null);
+    MethodType mt =
+        MethodType.methodType(
+            CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitInvokeDynamicInsn("targetMethodTest1", "()V", bootstrap);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method without extra args and
+   *  args to the target method.
+   */
+  private void generateMethodTest2(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(
+            CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn(new Boolean(true));
+    mv.visitLdcInsn(new Byte((byte) 127));
+    mv.visitLdcInsn(new Character('c'));
+    mv.visitLdcInsn(new Short((short) 1024));
+    mv.visitLdcInsn(new Integer(123456));
+    mv.visitLdcInsn(new Float(1.2f));
+    mv.visitLdcInsn(new Long(123456789));
+    mv.visitLdcInsn(new Double(3.5123456789));
+    mv.visitLdcInsn("String");
+    mv.visitInvokeDynamicInsn("targetMethodTest2", "(ZBCSIFJDLjava/lang/String;)V", bootstrap);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with extra args and no arg
+   *  to the target method.
+   */
+  private void generateMethodTest3(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(
+            CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, int.class,
+        long.class, float.class, double.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStaticWithExtraArgs", mt.toMethodDescriptorString(), false);
+    mv.visitInvokeDynamicInsn("targetMethodTest3", "()V", bootstrap, new Integer(1),
+        new Long(123456789), new Float(123.456), new Double(123456.789123));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invokespecial.
+   */
+  private void generateMethodTest4(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest5", "(Linvokecustom/InvokeCustom;)V", bootstrap,
+        new Handle( Opcodes.H_INVOKESPECIAL, Type.getInternalName(Super.class),
+            "targetMethodTest5", "()V", false));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invoke interface. The target method is a default method into an interface
+   *  that shadows another default method from a super interface.
+   */
+  private void generateMethodTest5(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test5", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethodTest6", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest6", "(Linvokecustom/I;)V", bootstrap,
+        new Handle(Opcodes.H_INVOKEINTERFACE, Type.getInternalName(I.class),
+            "targetMethodTest6", "()V", true));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invoke interface. The target method is a default method into an interface
+   *  that is at the end of a chain of interfaces.
+   */
+  private void generateMethodTest6(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test6", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethodTest7", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest7", "(Linvokecustom/J;)V", bootstrap,
+        new Handle(Opcodes.H_INVOKEINTERFACE, Type.getInternalName(J.class),
+            "targetMethodTest7", "()V", true));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invoke interface. The target method is a method into an interface
+   *  that is shadowed by another definition into a sub interfaces.
+   */
+  private void generateMethodTest7(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test7", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethodTest8", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest8", "(Linvokecustom/J;)V", bootstrap,
+        new Handle(Opcodes.H_INVOKEINTERFACE, Type.getInternalName(J.class),
+            "targetMethodTest8", "()V", true));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invoke virtual. The target method is a method into an interface that is
+   *  not shadowed by an implementation into a classes implementing the interface.
+   */
+  private void generateMethodTest8(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test8", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethodTest9", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest9", "(Linvokecustom/InvokeCustom;)V", bootstrap,
+        new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(InvokeCustom.class),
+            "targetMethodTest9", "()V", false));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invoke virtual. The target method is a method into a class implementing
+   *  an abstract method and that shadows a default method from an interface.
+   */
+  private void generateMethodTest9(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethodTest10", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest10", "(Linvokecustom/InvokeCustom;)V", bootstrap,
+        new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(InvokeCustom.class),
+            "targetMethodTest10", "()V", false));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind get static. The method handle read a static field from a class.
+   */
+  private void generateMethodTest10(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test10", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+    mv.visitFieldInsn(Opcodes.GETSTATIC,
+        "java/lang/System",
+        "out",
+        "Ljava/io/PrintStream;");
+    mv.visitInvokeDynamicInsn("staticField1", "()Ljava/lang/String;", bootstrap,
+        new Handle(Opcodes.H_GETSTATIC, Type.getInternalName(InvokeCustom.class),
+            "staticField1", "Ljava/lang/String;", false));
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+        "java/io/PrintStream",
+        "println",
+        "(Ljava/lang/String;)V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   * MethodHandle of kind put static. The method handle write a static field in a class and then
+   * print its value.
+   */
+  private void generateMethodTest11(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test11", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+            MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn("Write static field");
+    mv.visitInvokeDynamicInsn("staticField1", "(Ljava/lang/String;)V", bootstrap,
+        new Handle(Opcodes.H_PUTSTATIC, Type.getInternalName(InvokeCustom.class),
+            "staticField1", "Ljava/lang/String;", false));
+    mv.visitFieldInsn(Opcodes.GETSTATIC,
+        "java/lang/System",
+        "out",
+        "Ljava/io/PrintStream;");
+    mv.visitFieldInsn(Opcodes.GETSTATIC,
+        Type.getInternalName(InvokeCustom.class),
+        "staticField1",
+        "Ljava/lang/String;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+        "java/io/PrintStream",
+        "println",
+        "(Ljava/lang/String;)V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind get instance. The method handle read an instance field from a class.
+   */
+  private void generateMethodTest12(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test12", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+    mv.visitFieldInsn(Opcodes.GETSTATIC,
+        "java/lang/System",
+        "out",
+        "Ljava/io/PrintStream;");
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("instanceField1", "(Linvokecustom/InvokeCustom;)Ljava/lang/String;",
+        bootstrap, new Handle(Opcodes.H_GETFIELD, Type.getInternalName(InvokeCustom.class),
+            "instanceField1", "Ljava/lang/String;", false));
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+        "java/io/PrintStream",
+        "println",
+        "(Ljava/lang/String;)V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   * MethodHandle of kind put instance. The method handle write an instance field in a class and
+   * then print its value.
+   */
+  private void generateMethodTest13(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test13", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, MethodHandle.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(
+        Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false);
+    mv.visitVarInsn(Opcodes.ASTORE, 0);
+    mv.visitVarInsn(Opcodes.ALOAD, 0);
+    mv.visitLdcInsn("Write instance field");
+    mv.visitInvokeDynamicInsn("instanceField1", "(Linvokecustom/InvokeCustom;Ljava/lang/String;)V",
+        bootstrap, new Handle(Opcodes.H_PUTFIELD, Type.getInternalName(InvokeCustom.class),
+            "instanceField1", "Ljava/lang/String;", false));
+    mv.visitFieldInsn(Opcodes.GETSTATIC,
+        "java/lang/System",
+        "out",
+        "Ljava/io/PrintStream;");
+    mv.visitVarInsn(Opcodes.ALOAD, 0);
+    mv.visitFieldInsn(Opcodes.GETFIELD,
+        Type.getInternalName(InvokeCustom.class),
+        "instanceField1",
+        "Ljava/lang/String;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
+        "java/io/PrintStream",
+        "println",
+        "(Ljava/lang/String;)V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+}
diff --git a/src/test/examplesAndroidO/invokecustom/keep-rules.txt b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
new file mode 100644
index 0000000..52fa2a7
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom/keep-rules.txt
@@ -0,0 +1,17 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point and the target methods of invoke-custom because these methods
+# can not be known at compile time. Get rid of everything that is not reachable from there.
+-keep public class invokecustom.InvokeCustom {
+  public static void main(...);
+}
+
+-keepclassmembers  class * {
+  *** targetMethodTest*(...);
+}
+
+# allow access modification to enable minification
+-allowaccessmodification
+
diff --git a/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java b/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java
new file mode 100644
index 0000000..90cb064
--- /dev/null
+++ b/src/test/examplesAndroidO/invokepolymorphic/InvokePolymorphic.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package invokepolymorphic;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class Data {
+}
+
+public class InvokePolymorphic {
+
+  public String buildString(Integer i1, int i2, String s) {
+    return (i1 == null ? "N" : "!N") + "-" + i2 + "-" + s;
+  }
+
+  public void testInvokePolymorphic() {
+    MethodType mt = MethodType.methodType(String.class, Integer.class, int.class, String.class);
+    MethodHandles.Lookup lk = MethodHandles.lookup();
+
+    try {
+      MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
+      System.out.println(mh.invoke(this, null, 1, "string"));
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  public String buildString(
+      byte b, char c, short s, float f, double d, long l, Integer i1, int i2, String str) {
+    return b + "-" + c + "-" + s + "-" + f + "-" + d + "-" + l + "-" + (i1 == null ? "N" : "!N")
+        + "-" + i2 + "-" + str;
+  }
+
+  public void testInvokePolymorphicRange() {
+    MethodType mt = MethodType.methodType(String.class, byte.class, char.class, short.class,
+        float.class, double.class, long.class, Integer.class, int.class, String.class);
+    MethodHandles.Lookup lk = MethodHandles.lookup();
+
+    try {
+      MethodHandle mh = lk.findVirtual(getClass(), "buildString", mt);
+      System.out.println(
+          mh.invoke(this, (byte) 2, 'a', (short) 0xFFFF, 1.1f, 2.24d, 12345678L, null,
+              1, "string"));
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  public static void testWithAllTypes(
+      boolean z, char a, short b, int c, long d, float e, double f, String g, Object h) {
+    System.out.println(z);
+    System.out.println(a);
+    System.out.println(b);
+    System.out.println(c);
+    System.out.println(d);
+    System.out.println(e);
+    System.out.println(f);
+    System.out.println(g);
+    System.out.println(h);
+  }
+
+  public void testInvokePolymorphicWithAllTypes() {
+    try {
+      MethodHandle mth =
+          MethodHandles.lookup()
+              .findStatic(
+                  InvokePolymorphic.class,
+                  "testWithAllTypes",
+                  MethodType.methodType(
+                      void.class, boolean.class, char.class, short.class, int.class, long.class,
+                      float.class, double.class, String.class, Object.class));
+      mth.invokeExact(false,'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+          0.56f, 100.0d, "hello", (Object) "goodbye");
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  public MethodHandle testInvokePolymorphicWithConstructor() {
+    MethodHandle mh = null;
+    MethodType mt = MethodType.methodType(void.class);
+    MethodHandles.Lookup lk = MethodHandles.lookup();
+
+    try {
+      mh = lk.findConstructor(Data.class, mt);
+      System.out.println(mh.invoke().getClass() == Data.class);
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+
+    return mh;
+  }
+
+  public static void main(String[] args) {
+    InvokePolymorphic invokePolymorphic = new InvokePolymorphic();
+    invokePolymorphic.testInvokePolymorphic();
+    invokePolymorphic.testInvokePolymorphicRange();
+    invokePolymorphic.testInvokePolymorphicWithAllTypes();
+    invokePolymorphic.testInvokePolymorphicWithConstructor();
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
new file mode 100644
index 0000000..4b7ebd9
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
@@ -0,0 +1,529 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaring;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import lambdadesugaring.legacy.Legacy;
+import lambdadesugaring.other.OtherRefs;
+
+public class LambdaDesugaring {
+  interface I {
+    String foo();
+  }
+
+  interface V {
+    void foo();
+  }
+
+  interface VT<T> {
+    void foo(T t);
+  }
+
+  interface P1<X> {
+    X foo(int i);
+  }
+
+  interface I2 extends I {
+  }
+
+  interface I3 {
+    String foo();
+  }
+
+  interface M1 {
+  }
+
+  interface M2 {
+  }
+
+  interface J {
+    String foo(String a, int b, boolean c);
+  }
+
+  interface G {
+    A foo();
+  }
+
+  interface H<T extends A> {
+    T foo(T o);
+  }
+
+  interface K {
+    Object foo(String a, String b, String c);
+  }
+
+  interface ObjectProvider {
+    Object act();
+  }
+
+  interface S2Z {
+    boolean foo(String a);
+  }
+
+  interface SS2Z {
+    boolean foo(String a, String b);
+  }
+
+  interface ArrayTransformerA<T> {
+    T[] transform(T[] a);
+  }
+
+  @SuppressWarnings("unchecked")
+  interface ArrayTransformerB<T> {
+    T[] transform(T... a);
+  }
+
+  static <T> void print(T[] a) {
+    StringBuilder builder = new StringBuilder("{");
+    String sep = "";
+    for (T s : a) {
+      builder.append(sep).append(s.toString());
+      sep = ", ";
+    }
+    builder.append("}");
+    System.out.println(builder.toString());
+  }
+
+  <T> T[] reorder(T[] a) {
+    int size = a.length;
+    for (int x = 0; x < size / 2; x++) {
+      T t = a[x];
+      a[x] = a[size - 1 - x];
+      a[size - 1 - x] = t;
+    }
+    return a;
+  }
+
+  static void atA(ArrayTransformerA<Integer> f) {
+    print(f.transform(new Integer[] { 1, 2, 3 }));
+  }
+
+  static void atB(ArrayTransformerB<String> f) {
+    print(f.transform("A", "B", "C"));
+  }
+
+  public static String staticUnused() {
+    return "ReleaseTests::staticUnused";
+  }
+
+  public static void testUnusedLambdas() {
+    System.out.print("Before unused ... ");
+    Object o = (I) LambdaDesugaring::staticUnused;
+    System.out.println("after unused.");
+  }
+
+  class A {
+    final String toString;
+
+    A(String toString) {
+      this.toString = toString;
+    }
+
+    @Override
+    public String toString() {
+      return toString;
+    }
+  }
+
+  class B extends A {
+    B(String toString) {
+      super(toString);
+    }
+  }
+
+  class C extends B {
+    C(String toString) {
+      super(toString);
+    }
+  }
+
+  class D extends C {
+    D(String toString) {
+      super(toString);
+    }
+  }
+
+  public static class Refs {
+    public static String f(I i) {
+      return i.foo();
+    }
+
+    public static void v(V v) {
+      v.foo();
+    }
+
+    public static void vt(VT<String> v) {
+      v.foo(null);
+    }
+
+    public static String p1(P1 p) {
+      return p.foo(123).getClass().getCanonicalName();
+    }
+
+    public static String pSS2Z(SS2Z p) {
+      return "" + p.foo("123", "321");
+    }
+
+    public static String pS2Z(S2Z p) {
+      return "" + p.foo("123");
+    }
+
+    public static String p3(K k) {
+      return k.foo("A", "B", "C").toString();
+    }
+
+    public static String g(ObjectProvider op) {
+      return op.act().toString();
+    }
+
+    static class A extends OtherRefs {
+      String fooInternal() {
+        return "Refs::A::fooInternal()";
+      }
+
+      protected String fooProtected() {
+        return "Refs::A::fooProtected()";
+      }
+
+      protected String fooProtectedOverridden() {
+        return "Refs::A::fooProtectedOverridden()";
+      }
+
+      protected static String staticProtected() {
+        return "Refs::A::staticProtected()";
+      }
+
+      static String staticInternal() {
+        return "Refs::A::staticInternal()";
+      }
+    }
+
+    public static class B extends A {
+      public void test() {
+        System.out.println(f(new A()::fooInternal));
+        System.out.println(f(this::fooInternal));
+        System.out.println(f(this::fooProtected));
+        System.out.println(f(this::fooProtectedOverridden));
+        System.out.println(f(this::fooPublic));
+        System.out.println(f(this::fooInternal));
+
+        System.out.println(f(super::fooProtectedOverridden));
+        System.out.println(f(this::fooOtherProtected));
+        System.out.println(f(this::fooOtherPublic));
+
+        System.out.println(g(this::fooPrivate));
+        System.out.println(g(new Integer(123)::toString));
+        System.out.println(g(System::lineSeparator));
+
+        System.out.println(f(A::staticInternal));
+        System.out.println(f(A::staticProtected));
+        System.out.println(f(B::staticPrivate));
+        System.out.println(f(OtherRefs::staticOtherPublic));
+        System.out.println(f(OtherRefs::staticOtherProtected));
+
+        System.out.println(g(StringBuilder::new));
+        System.out.println(g(OtherRefs.PublicInit::new));
+        System.out.println(ProtectedInit.testProtected());
+        System.out.println(g(ProtectedInit::new));
+        System.out.println(g(InternalInit::new));
+        System.out.println(PrivateInit.testPrivate());
+        System.out.println(g(PrivateInit::new));
+
+        System.out.println(p1(D[]::new));
+        System.out.println(p1(Integer::new));
+        System.out.println(p1(B::staticArray));
+
+        System.out.println(pSS2Z(String::equalsIgnoreCase));
+        System.out.println(pS2Z("123321"::contains));
+        System.out.println(pS2Z(String::isEmpty));
+
+        System.out.println(p3(B::fooConcat));
+
+        v(D::new); // Discarding the return value
+        vt((new ArrayList<String>())::add);
+
+        I3 i3 = this::fooPrivate;
+        System.out.println(f(i3::foo));
+      }
+
+      private static String staticPrivate() {
+        return "Refs::B::staticPrivate()";
+      }
+
+      private String fooPrivate() {
+        return "Refs::B::fooPrivate()";
+      }
+
+      String fooInternal() {
+        return "Refs::B::fooInternal()";
+      }
+
+      public static StringBuilder fooConcat(Object... objs) {
+        StringBuilder builder = new StringBuilder("Refs::B::fooConcat(");
+        String sep = "";
+        for (Object obj : objs) {
+          builder.append(sep).append(obj.toString());
+          sep = ", ";
+        }
+        return builder.append(")");
+      }
+
+      @Override
+      protected String fooProtectedOverridden() {
+        return "Refs::B::fooProtectedOverridden()";
+      }
+
+      public String fooPublic() {
+        return "Refs::B::fooPublic()";
+      }
+
+      static int[] staticArray(int size) {
+        return new int[size];
+      }
+    }
+
+    static class D {
+      D() {
+        System.out.println("Refs::D::init()");
+      }
+    }
+
+    public static class ProtectedInit extends OtherRefs.PublicInit {
+      protected ProtectedInit() {
+      }
+
+      static String testProtected() {
+        return g(ProtectedInit::new);
+      }
+
+      @Override
+      public String toString() {
+        return "OtherRefs::ProtectedInit::init()";
+      }
+    }
+
+    static class InternalInit extends ProtectedInit {
+      InternalInit() {
+      }
+
+      @Override
+      public String toString() {
+        return "Refs::InternalInit::init()";
+      }
+    }
+
+    static class PrivateInit extends InternalInit {
+      private PrivateInit() {
+      }
+
+      static String testPrivate() {
+        return g(PrivateInit::new);
+      }
+
+      @Override
+      public String toString() {
+        return "Refs::PrivateInit::init()";
+      }
+    }
+  }
+
+  public void testLambdasSimple() {
+    System.out.println(f(() -> "testLambdasSimple#1"));
+    System.out.println(
+        g((a, b, c) -> "{" + a + ":" + b + ":" + c + "}",
+            "testLambdasSimple#2", 123, true));
+  }
+
+  public void testLambdasSimpleWithCaptures() {
+    String s = "<stirng>";
+    long l = 1234567890123456789L;
+    char c = '#';
+
+    System.out.println(
+        g((x, y, z) -> "{" + s + ":" + l + ":" + c + ":" + x + ":" + y + ":" + z + "}",
+            "param1", 2, false));
+
+    I i1 = () -> "i1";
+    I i2 = () -> i1.foo() + ":i2";
+    I i3 = () -> i2.foo() + ":i3";
+    System.out.println(f(() -> "{" + i3.foo() + ":anonymous}"));
+  }
+
+  public void testInstructionPatchingWithCatchHandlers() {
+    try {
+      int a = 1, b = 0;
+      System.out.println(f(() -> "testInstructionPatchingWithCatchHandlers:1"));
+      System.out.println(f(() -> ("does not matter " + (a / b))));
+    } catch (IndexOutOfBoundsException | ArithmeticException e) {
+      System.out.println("testInstructionPatchingWithCatchHandlers:Divide By Zero");
+    } catch (RuntimeException re) {
+      throw re;
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+
+    int changes = -1;
+    try {
+      if (f(() -> "").isEmpty()) {
+        changes = 32;
+        System.out.println(f(() -> "testInstructionPatchingWithCatchHandlers:lambda"));
+        throw new RuntimeException();
+      } else {
+        changes = 42;
+        throw new RuntimeException();
+      }
+    } catch (Throwable t) {
+      System.out.println("testInstructionPatchingWithCatchHandlers:changes=" + changes);
+    }
+  }
+
+  public void testInstanceLambdaMethods() {
+    Integer i = 12345;
+    System.out.println(h(() -> new A("{testInstanceLambdaMethods:" + i + "}")));
+  }
+
+  @SuppressWarnings("unchecked")
+  private void testEnforcedSignatureHelper() {
+    H h = ((H<B>) x -> new B("{testEnforcedSignature:" + x + "}"));
+    System.out.println(h.foo(new A("A")).toString());
+  }
+
+  public void testEnforcedSignature() {
+    String capture = "capture";
+    System.out.println(i(x -> new B("{testEnforcedSignature:" + x + "}")));
+    System.out.println(i(x -> new B("{testEnforcedSignature:" + capture + "}")));
+
+    try {
+      testEnforcedSignatureHelper();
+    } catch (Exception e) {
+      System.out.println(e.getMessage());
+    }
+
+    atA(t -> new LambdaDesugaring().reorder(t));
+    atB(t -> new LambdaDesugaring().reorder(t));
+  }
+
+  public void testMultipleInterfaces() {
+    System.out.println(j((I2 & M1 & I3 & M2) () -> "{testMultipleInterfaces:1}"));
+
+    Object o = (I2 & M1 & I3 & M2) () -> "{testMultipleInterfaces:2}";
+    M1 m1 = (M1) o;
+    M2 m2 = (M2) m1;
+    I i = (I) m2;
+    System.out.println(((I3) i).foo());
+
+    o = (I2 & Serializable & M2) () -> "{testMultipleInterfaces:3}";
+    m2 = (M2) o;
+    Serializable s = (Serializable) m2;
+    System.out.println(((I) s).foo());
+  }
+
+  public void testBridges() {
+    k((Legacy.BH) (x -> x), "{testBridges:1}");
+    k((Legacy.BK<Legacy.D> & Serializable) (x -> x), new Legacy.D("{testBridges:2}"));
+    // k((Legacy.BL) (x -> x), new Legacy.B("{testBridges:3}")); crashes javac
+    k((Legacy.BM) (x -> x), new Legacy.C("{testBridges:4}"));
+  }
+
+  public String f(I i) {
+    return i.foo();
+  }
+
+  String g(J j, String a, int b, boolean c) {
+    return j.foo(a, b, c);
+  }
+
+  String h(G g) {
+    return g.foo().toString();
+  }
+
+  String i(H<B> h) {
+    return h.foo(new B("i(H<B>)")).toString();
+  }
+
+  <T extends I2 & M1 & M2 & I3> String j(T l) {
+    return ((I3) ((M2) ((M1) (((I2) l))))).foo();
+  }
+
+  static <T> void k(Legacy.BI<T> i, T v) {
+    System.out.println(i.foo(v).toString());
+  }
+
+  static I statelessLambda() {
+    return InstanceAndClassChecks::staticProvider;
+  }
+
+  static I statefulLambda() {
+    return InstanceAndClassChecks.INSTANCE::instanceProvider;
+  }
+
+  static class InstanceAndClassChecks {
+    static final InstanceAndClassChecks INSTANCE = new InstanceAndClassChecks();
+
+    static void test() {
+      assertSameInstance(
+          InstanceAndClassChecks::staticProvider,
+          InstanceAndClassChecks::staticProvider,
+          "Instances must be same");
+      assertSameInstance(
+          InstanceAndClassChecks::staticProvider,
+          statelessLambda(),
+          "Instances must be same");
+
+      assertDifferentInstance(
+          INSTANCE::instanceProvider,
+          INSTANCE::instanceProvider,
+          "Instances must be different");
+      assertDifferentInstance(
+          INSTANCE::instanceProvider,
+          statefulLambda(), "Instances must be different");
+    }
+
+    public static String staticProvider() {
+      return "staticProvider";
+    }
+
+    public String instanceProvider() {
+      return "instanceProvider";
+    }
+
+    static void assertSameInstance(I a, I b, String msg) {
+      if (a != b) {
+        throw new AssertionError(msg);
+      }
+    }
+
+    static void assertDifferentInstance(I a, I b, String msg) {
+      if (a == b) {
+        throw new AssertionError(msg);
+      }
+    }
+  }
+
+  public static void main(String[] args) {
+    LambdaDesugaring tests = new LambdaDesugaring();
+    tests.testLambdasSimple();
+    LambdaDesugaring.testUnusedLambdas();
+    tests.testLambdasSimpleWithCaptures();
+    tests.testInstructionPatchingWithCatchHandlers();
+    tests.testInstanceLambdaMethods();
+    tests.testEnforcedSignature();
+    tests.testMultipleInterfaces();
+    tests.testBridges();
+    new Refs.B().test();
+    if (isAndroid()) {
+      InstanceAndClassChecks.test();
+    }
+  }
+
+  static boolean isAndroid() {
+    try {
+      Class.forName("dalvik.system.VMRuntime");
+      return true;
+    } catch (Exception ignored) {
+    }
+    return false;
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
new file mode 100644
index 0000000..b9d2752
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/ValueAdjustments.java
@@ -0,0 +1,330 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaring;
+
+public class ValueAdjustments {
+  interface B2i {
+    int foo(Byte i);
+  }
+
+  interface BnUnB {
+    Object foo(boolean z, Boolean Z, byte b, Byte B, char c, Character C, short s, Short S,
+        int i, Integer I, long l, Long L, float f, Float F, double d, Double D);
+  }
+
+  interface iz {
+    boolean f();
+  }
+
+  interface iZ {
+    Boolean f();
+  }
+
+  interface ib {
+    byte f();
+  }
+
+  interface iO {
+    Object f();
+  }
+
+  interface iN {
+    Number f();
+  }
+
+  interface iB {
+    Byte f();
+  }
+
+  interface ic {
+    char f();
+  }
+
+  interface iC {
+    Character f();
+  }
+
+  interface is {
+    short f();
+  }
+
+  interface iS {
+    Short f();
+  }
+
+  interface ii {
+    int f();
+  }
+
+  interface iI {
+    Integer f();
+  }
+
+  interface ij {
+    long f();
+  }
+
+  interface iJ {
+    Long f();
+  }
+
+  interface if_ {
+    float f();
+  }
+
+  interface iF {
+    Float f();
+  }
+
+  interface id {
+    double f();
+  }
+
+  interface iD {
+    Double f();
+  }
+
+  private static void checkObject(StringBuffer builder) {
+    builder
+        .append(((iO) ValueAdjustments::z).f()).append(' ')
+        .append(((iO) ValueAdjustments::Z).f()).append(' ')
+        .append(((iO) ValueAdjustments::b).f()).append(' ')
+        .append(((iO) ValueAdjustments::B).f()).append(' ')
+        .append(((iO) ValueAdjustments::c).f()).append(' ')
+        .append(((iO) ValueAdjustments::C).f()).append(' ')
+        .append(((iO) ValueAdjustments::s).f()).append(' ')
+        .append(((iO) ValueAdjustments::S).f()).append(' ')
+        .append(((iO) ValueAdjustments::i).f()).append(' ')
+        .append(((iO) ValueAdjustments::I).f()).append(' ')
+        .append(((iO) ValueAdjustments::j).f()).append(' ')
+        .append(((iO) ValueAdjustments::J).f()).append(' ')
+        .append(((iO) ValueAdjustments::f).f()).append(' ')
+        .append(((iO) ValueAdjustments::F).f()).append(' ')
+        .append(((iO) ValueAdjustments::d).f()).append(' ')
+        .append(((iO) ValueAdjustments::D).f()).append('\n');
+  }
+
+  private static void checkNumber(StringBuffer builder) {
+    builder
+        .append(((iN) ValueAdjustments::b).f()).append(' ')
+        .append(((iN) ValueAdjustments::B).f()).append(' ')
+        .append(((iN) ValueAdjustments::s).f()).append(' ')
+        .append(((iN) ValueAdjustments::S).f()).append(' ')
+        .append(((iN) ValueAdjustments::i).f()).append(' ')
+        .append(((iN) ValueAdjustments::I).f()).append(' ')
+        .append(((iN) ValueAdjustments::j).f()).append(' ')
+        .append(((iN) ValueAdjustments::J).f()).append(' ')
+        .append(((iN) ValueAdjustments::f).f()).append(' ')
+        .append(((iN) ValueAdjustments::F).f()).append(' ')
+        .append(((iN) ValueAdjustments::d).f()).append(' ')
+        .append(((iN) ValueAdjustments::D).f()).append('\n');
+  }
+
+  private static void checkBoxes(StringBuffer builder) {
+    builder
+        .append(((iZ) ValueAdjustments::z).f()).append(' ')
+        .append(((iB) ValueAdjustments::b).f()).append(' ')
+        .append(((iC) ValueAdjustments::c).f()).append(' ')
+        .append(((iS) ValueAdjustments::s).f()).append(' ')
+        .append(((iI) ValueAdjustments::i).f()).append(' ')
+        .append(((iJ) ValueAdjustments::j).f()).append(' ')
+        .append(((iF) ValueAdjustments::f).f()).append(' ')
+        .append(((iD) ValueAdjustments::d).f()).append('\n');
+  }
+
+  private static void checkDouble(StringBuffer builder) {
+    builder
+        .append(((id) ValueAdjustments::b).f()).append(' ')
+        .append(((id) ValueAdjustments::B).f()).append(' ')
+        .append(((id) ValueAdjustments::s).f()).append(' ')
+        .append(((id) ValueAdjustments::S).f()).append(' ')
+        .append(((id) ValueAdjustments::c).f()).append(' ')
+        .append(((id) ValueAdjustments::C).f()).append(' ')
+        .append(((id) ValueAdjustments::i).f()).append(' ')
+        .append(((id) ValueAdjustments::I).f()).append(' ')
+        .append(((id) ValueAdjustments::j).f()).append(' ')
+        .append(((id) ValueAdjustments::J).f()).append(' ')
+        .append(((id) ValueAdjustments::f).f()).append(' ')
+        .append(((id) ValueAdjustments::F).f()).append(' ')
+        .append(((id) ValueAdjustments::d).f()).append(' ')
+        .append(((id) ValueAdjustments::D).f()).append('\n');
+  }
+
+  private static void checkFloat(StringBuffer builder) {
+    builder
+        .append(((if_) ValueAdjustments::b).f()).append(' ')
+        .append(((if_) ValueAdjustments::B).f()).append(' ')
+        .append(((if_) ValueAdjustments::s).f()).append(' ')
+        .append(((if_) ValueAdjustments::S).f()).append(' ')
+        .append(((if_) ValueAdjustments::c).f()).append(' ')
+        .append(((if_) ValueAdjustments::C).f()).append(' ')
+        .append(((if_) ValueAdjustments::i).f()).append(' ')
+        .append(((if_) ValueAdjustments::I).f()).append(' ')
+        .append(((if_) ValueAdjustments::j).f()).append(' ')
+        .append(((if_) ValueAdjustments::J).f()).append(' ')
+        .append(((if_) ValueAdjustments::f).f()).append(' ')
+        .append(((if_) ValueAdjustments::F).f()).append('\n');
+  }
+
+  private static void checkLong(StringBuffer builder) {
+    builder
+        .append(((ij) ValueAdjustments::b).f()).append(' ')
+        .append(((ij) ValueAdjustments::B).f()).append(' ')
+        .append(((ij) ValueAdjustments::s).f()).append(' ')
+        .append(((ij) ValueAdjustments::S).f()).append(' ')
+        .append(((ij) ValueAdjustments::c).f()).append(' ')
+        .append(((ij) ValueAdjustments::C).f()).append(' ')
+        .append(((ij) ValueAdjustments::i).f()).append(' ')
+        .append(((ij) ValueAdjustments::I).f()).append(' ')
+        .append(((ij) ValueAdjustments::j).f()).append(' ')
+        .append(((ij) ValueAdjustments::J).f()).append('\n');
+  }
+
+  private static void checkInt(StringBuffer builder) {
+    builder
+        .append(((ii) ValueAdjustments::b).f()).append(' ')
+        .append(((ii) ValueAdjustments::B).f()).append(' ')
+        .append(((ii) ValueAdjustments::s).f()).append(' ')
+        .append(((ii) ValueAdjustments::S).f()).append(' ')
+        .append(((ii) ValueAdjustments::c).f()).append(' ')
+        .append(((ii) ValueAdjustments::C).f()).append(' ')
+        .append(((ii) ValueAdjustments::i).f()).append(' ')
+        .append(((ii) ValueAdjustments::I).f()).append('\n');
+  }
+
+  private static void checkShort(StringBuffer builder) {
+    builder
+        .append(((is) ValueAdjustments::b).f()).append(' ')
+        .append(((is) ValueAdjustments::B).f()).append(' ')
+        .append(((is) ValueAdjustments::s).f()).append(' ')
+        .append(((is) ValueAdjustments::S).f()).append('\n');
+  }
+
+  private static void checkChar(StringBuffer builder) {
+    builder
+        .append(((ic) ValueAdjustments::c).f()).append(' ')
+        .append(((ic) ValueAdjustments::C).f()).append('\n');
+  }
+
+  private static void checkByte(StringBuffer builder) {
+    builder
+        .append(((ib) ValueAdjustments::b).f()).append(' ')
+        .append(((ib) ValueAdjustments::B).f()).append('\n');
+  }
+
+  private static void checkBoolean(StringBuffer builder) {
+    builder
+        .append(((iz) ValueAdjustments::z).f()).append(' ')
+        .append(((iz) ValueAdjustments::Z).f()).append('\n');
+  }
+
+  private static void checkMisc(StringBuffer builder) {
+    builder
+        .append(((BnUnB) ValueAdjustments::boxingAndUnboxing).foo(true, false, (byte) 1, (byte) 2,
+            (char) 33, (char) 44, (short) 5, (short) 6, 7, 8, 9, 10L, 11, 12f, 13, 14d))
+        .append('\n')
+        .append(((BnUnB) ValueAdjustments::boxingAndUnboxingW).foo(true, false, (byte) 1, (byte) 2,
+            (char) 33, (char) 44, (short) 5, (short) 6, 7, 8, 9, 10L, 11, 12f, 13, 14d))
+        .append('\n')
+        .append(((B2i) (Integer::new)).foo(Byte.valueOf((byte) 44))).append('\n');
+  }
+
+  static String boxingAndUnboxing(Boolean Z, boolean z, Byte B, byte b, Character C, char c,
+      Short S, short s, Integer I, int i, Long L, long l, Float F, float f, Double D, double d) {
+    return "" + Z + ":" + z + ":" + B + ":" + b + ":" + C + ":" + c + ":" + S + ":" + s
+        + ":" + I + ":" + i + ":" + L + ":" + l + ":" + F + ":" + f + ":" + D + ":" + d;
+  }
+
+  static String boxingAndUnboxingW(boolean Z, boolean z, double B, double b,
+      double C, double c, double S, double s, double I, double i, double L, double l,
+      double F, double f, double D, double d) {
+    return "" + Z + ":" + z + ":" + B + ":" + b + ":" + C + ":" + c + ":" + S + ":" + s
+        + ":" + I + ":" + i + ":" + L + ":" + l + ":" + F + ":" + f + ":" + D + ":" + d;
+  }
+
+  static boolean z() {
+    return true;
+  }
+
+  static byte b() {
+    return 8;
+  }
+
+  static char c() {
+    return 'c';
+  }
+
+  static short s() {
+    return 16;
+  }
+
+  static int i() {
+    return 32;
+  }
+
+  static long j() {
+    return 64;
+  }
+
+  static float f() {
+    return 0.32f;
+  }
+
+  static double d() {
+    return 0.64;
+  }
+
+  static Boolean Z() {
+    return false;
+  }
+
+  static Byte B() {
+    return -8;
+  }
+
+  static Character C() {
+    return 'C';
+  }
+
+  static Short S() {
+    return -16;
+  }
+
+  static Integer I() {
+    return -32;
+  }
+
+  static Long J() {
+    return -64L;
+  }
+
+  static Float F() {
+    return -0.32f;
+  }
+
+  static Double D() {
+    return -0.64;
+  }
+
+  public static void main(String[] args) {
+    StringBuffer builder = new StringBuffer();
+
+    checkBoolean(builder);
+    checkByte(builder);
+    checkChar(builder);
+    checkShort(builder);
+    checkInt(builder);
+    checkLong(builder);
+    checkFloat(builder);
+    checkDouble(builder);
+
+    checkBoxes(builder);
+    checkNumber(builder);
+    checkObject(builder);
+
+    checkMisc(builder);
+
+    System.out.println(builder.toString());
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java b/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java
new file mode 100644
index 0000000..aabd8d8
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/legacy/Legacy.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaring.legacy;
+
+public class Legacy {
+  public static class A {
+    private final String toString;
+
+    public A(String toString) {
+      this.toString = toString;
+    }
+
+    @Override
+    public String toString() {
+      return toString;
+    }
+  }
+
+  public static class B extends A {
+    public B(String toString) {
+      super(toString);
+    }
+  }
+
+  public static class C extends B {
+    public C(String toString) {
+      super(toString);
+    }
+  }
+
+  public static class D extends C {
+    public D(String toString) {
+      super(toString);
+    }
+  }
+
+  public interface BI<T> {
+    T foo(T o1);
+  }
+
+  public interface BH extends BI<String> {
+  }
+
+  public interface BK<T extends A> extends BI<T> {
+    T foo(T o1);
+  }
+
+  public interface BL<T extends B> extends BK<T> {
+    T foo(T o1);
+  }
+
+  public interface BM extends BK<C> {
+    C foo(C o1);
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaring/other/OtherRefs.java b/src/test/examplesAndroidO/lambdadesugaring/other/OtherRefs.java
new file mode 100644
index 0000000..e783be3
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaring/other/OtherRefs.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaring.other;
+
+public class OtherRefs {
+  public static class PublicInit {
+    public PublicInit() {
+    }
+
+    @Override
+    public String toString() {
+      return "OtherRefs::PublicInit::init()";
+    }
+  }
+
+  protected String fooOtherProtected() {
+    return "OtherRefs::fooOtherProtected()";
+  }
+
+  public String fooOtherPublic() {
+    return "OtherRefs::fooOtherPublic()";
+  }
+
+  protected static String staticOtherProtected() {
+    return "OtherRefs::staticOtherProtected()";
+  }
+
+  public static String staticOtherPublic() {
+    return "OtherRefs::staticOtherPublic()";
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
new file mode 100644
index 0000000..d2e738d
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -0,0 +1,405 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaringnplus;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import lambdadesugaringnplus.other.ClassWithDefaultPackagePrivate;
+import lambdadesugaringnplus.other.InterfaceWithDefaultPackagePrivate;
+
+public class LambdasWithStaticAndDefaultMethods {
+  interface I {
+    String iRegular();
+
+    static String iStatic() {
+      return "I::iStatic()";
+    }
+
+    default String iDefault() {
+      return "I::iDefault()";
+    }
+
+    default String iDefaultOverridden() {
+      return "I::iDefaultOverridden()";
+    }
+
+    default II stateless() {
+      return () -> "I::stateless()";
+    }
+
+    default II stateful() {
+      return () -> "I::captureThis(" + stateless().iRegular() + ")";
+    }
+  }
+
+  static class C implements I {
+    @Override
+    public String iRegular() {
+      return "C::iRegular()";
+    }
+
+    @Override
+    public String iDefaultOverridden() {
+      return "C::iDefaultOverridden()";
+    }
+  }
+
+  interface II extends I {
+    static String iStatic() {
+      II ii = I::iStatic;
+      return "II::iStatic(" + ((I) I::iStatic).iRegular() +
+          "|" + ((II) ii::iDefaultOverridden).iRegular() +
+          "|" + ((II) String::new).iRegular() +
+          "|" + ((II) ii::iRegular).iRegular() + ")";
+    }
+
+    default String iDefaultOverridden() {
+      return "II::iDefault(" + ((I) this::iDefault).iRegular() +
+          "|" + ((II) "One-Two-Three"::intern).iRegular() +
+          "|" + ((II) this::iDefault).iRegular() + ")";
+    }
+  }
+
+  interface P {
+    String get();
+  }
+
+  static void p(P p) {
+    System.out.println(p.get());
+  }
+
+  interface X<T> {
+    String foo(T t);
+  }
+
+  interface Y<T extends I> extends X<T> {
+    String foo(T t);
+  }
+
+  interface Z extends Y<II> {
+    String foo(II t);
+  }
+
+  interface G<T> {
+    T foo(T t);
+  }
+
+  interface B38257361_I1<T extends Number> {
+    default T copy(T t) {
+      return t;
+    }
+  }
+
+  interface B38257361_I2 extends B38257361_I1<Integer> {
+    @Override
+    default Integer copy(Integer t) {
+      return B38257361_I1.super.copy(t);
+    }
+  }
+
+  static class B38257361_C implements B38257361_I2 {
+  }
+
+  static class B38257361 {
+    private B38257361_C c = new B38257361_C();
+
+    public Integer test(Integer i) {
+      return c.copy(i);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public Number test(Number n) {
+      return ((B38257361_I1) c).copy(n);
+    }
+
+    public static void test() {
+      B38257361 l = new B38257361();
+      Integer i = new Integer(1);
+      if (i.equals(l.test(i))) {
+        System.out.println("Check 1: OK");
+      } else {
+        System.out.println("Check 1: NOT OK");
+      }
+      if (i.equals(l.test((Number) i))) {
+        System.out.println("Check 2: OK");
+      } else {
+        System.out.println("Check 2: NOT OK");
+      }
+      try {
+        Double d = new Double(1);
+        if (d.equals(l.test((Number) d))) {
+          System.out.println("Check 3: NOT OK, classCastException expected");
+        } else {
+          System.out.println("Check 3: NOT OK, classCastException expected");
+        }
+        System.out.println("Error, ClassCastException is expected");
+      } catch (ClassCastException e) {
+        // Class cast into the bridge method is expected
+        System.out.println("OK, ClassCastException is expected");
+      }
+    }
+  }
+
+  interface B38257037_I1 {
+    default Number getNumber() {
+      return new Integer(1);
+    }
+  }
+
+  interface B38257037_I2 extends B38257037_I1 {
+    @Override
+    default Double getNumber() {
+      return new Double(2.3);
+    }
+  }
+
+  static class B38257037_C implements B38257037_I2 {
+  }
+
+  /**
+   * Check that bridges are generated.
+   */
+  static class B38257037 {
+    private B38257037_C c = new B38257037_C();
+
+    public Double test1() {
+      return c.getNumber();
+    }
+
+    public Number test2() {
+      return ((B38257037_I1) c).getNumber();
+    }
+
+    public static void test() {
+      B38257037 l = new B38257037();
+      if (l.test1() == 2.3) {
+        System.out.println("Check 1: OK");
+      } else {
+        System.out.println("Check 1: NOT OK");
+      }
+      if (l.test2().equals(new Double(2.3))) {
+        System.out.println("Check 2: OK");
+      } else {
+        System.out.println("Check 2: NOT OK");
+      }
+    }
+  }
+
+  interface B38306708_I {
+    class $CC{
+      static void print() {
+        System.out.println("$CC");
+      }
+    }
+
+    default String m() {
+      return "ITop.m()";
+    }
+  }
+
+  static class B38306708 {
+    public static void test() {
+      B38306708_I.$CC.print();
+    }
+  }
+
+  interface B38308515_I {
+    default String m() {
+      return "m instance";
+    }
+
+    static String m(B38308515_I i) {
+      return "m static";
+    }
+  }
+
+  static class B38308515_C implements B38308515_I {
+  }
+
+  static class B38308515 {
+    static void test() {
+      B38308515_C c = new B38308515_C();
+      System.out.println(c.m());
+      System.out.println(B38308515_I.m(c));
+    }
+  }
+
+  static class B38302860 {
+
+    @SomeAnnotation(1)
+    private interface AnnotatedInterface {
+
+      @SomeAnnotation(2)
+      void annotatedAbstractMethod();
+
+      @SomeAnnotation(3)
+      default void annotatedDefaultMethod() {
+      }
+
+      @SomeAnnotation(4)
+      static void annotatedStaticMethod() {
+      }
+    }
+
+    @Retention(value = RetentionPolicy.RUNTIME)
+    private @interface SomeAnnotation {
+      int value();
+    }
+
+    private static boolean checkAnnotationValue(Annotation[] annotations, int value) {
+      if (annotations.length != 1) {
+        return false;
+      }
+      return annotations[0] instanceof SomeAnnotation
+          && ((SomeAnnotation) annotations[0]).value() == value;
+    }
+
+    @SuppressWarnings("unchecked")
+    static void test() throws Exception {
+      if (checkAnnotationValue(AnnotatedInterface.class.getAnnotations(), 1)) {
+        System.out.println("Check 1: OK");
+      } else {
+        System.out.println("Check 1: NOT OK");
+      }
+
+      if (checkAnnotationValue(
+          AnnotatedInterface.class.getMethod("annotatedAbstractMethod").getAnnotations(), 2)) {
+        System.out.println("Check 2: OK");
+      } else {
+        System.out.println("Check 2: NOT OK");
+      }
+
+      if (checkAnnotationValue(
+          AnnotatedInterface.class.getMethod("annotatedDefaultMethod").getAnnotations(), 3)) {
+        System.out.println("Check 3: OK");
+      } else {
+        System.out.println("Check 3: NOT OK");
+      }
+
+      if (checkAnnotationValue(
+          getCompanionClassOrInterface().getMethod("annotatedStaticMethod").getAnnotations(), 4)) {
+        System.out.println("Check 4: OK");
+      } else {
+        System.out.println("Check 4: NOT OK");
+      }
+    }
+
+    private static Class getCompanionClassOrInterface() {
+      try {
+        return Class.forName("lambdadesugaringnplus."
+            + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface-CC");
+      } catch (Exception e) {
+        return AnnotatedInterface.class;
+      }
+    }
+  }
+
+  static void z(Z p) {
+    System.out.println(p.foo(null));
+  }
+
+  static void g(G<String[]> g) {
+    StringBuilder builder = new StringBuilder("{");
+    String sep = "";
+    for (String s : g.foo(new String[] { "Arg0", "Arg1", "Arg2" })) {
+      builder.append(sep).append(s);
+      sep = ", ";
+    }
+    builder.append("}");
+    System.out.println(builder.toString());
+  }
+
+  interface SuperChain {
+    default String iMain() {
+      return "SuperChain::iMain()";
+    }
+  }
+
+  interface SuperChainDerived extends SuperChain {
+    default String iMain() {
+      return "SuperChainDerived::iMain(" + SuperChain.super.iMain() + ")";
+    }
+  }
+
+  interface OtherSuperChain {
+    default String iMain() {
+      return "OtherSuperChain::iMain()";
+    }
+  }
+
+  static class ClassWithSuperChain implements SuperChainDerived, OtherSuperChain {
+    public String iMain() {
+      return "ClassWithSuperChain::iMain(" + SuperChainDerived.super.iMain() + ")" + iMainImpl();
+    }
+
+    public String iMainImpl() {
+      return "ClassWithSuperChain::iMain(" + SuperChainDerived.super.iMain() +
+          " + " + OtherSuperChain.super.iMain() + ")";
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    C c = new C();
+    I i = c;
+
+    c.iRegular();
+    c.iDefault();
+    c.iDefaultOverridden();
+    I.iStatic();
+    i.iRegular();
+    i.iDefault();
+    i.iDefaultOverridden();
+
+    p(i.stateless()::iRegular);
+    p(i.stateful()::iRegular);
+
+    g(a -> a);
+    g(a -> {
+      int size = a.length;
+      for (int x = 0; x < size / 2; x++) {
+        String t = a[x];
+        a[x] = a[size - 1 - x];
+        a[size - 1 - x] = t;
+      }
+      return a;
+    });
+
+    p(c::iRegular);
+    p(c::iDefault);
+    p(c::iDefaultOverridden);
+    p(I::iStatic);
+    p(i::iRegular);
+    p(i::iDefault);
+    p(i::iDefaultOverridden);
+
+    II ii = i::iRegular;
+    p(II::iStatic);
+    p(ii::iRegular);
+    p(ii::iDefault);
+    p(ii::iDefaultOverridden);
+
+    z(s -> "From Interface With Bridges");
+
+    System.out.println(new ClassWithSuperChain().iMain());
+
+    ClassWithDefaultPackagePrivate c2 = new ClassWithDefaultPackagePrivate();
+    InterfaceWithDefaultPackagePrivate i2 = c2;
+
+    c2.defaultFoo();
+    i2.defaultFoo();
+    InterfaceWithDefaultPackagePrivate.staticFoo();
+
+    p(c2::defaultFoo);
+    p(i2::defaultFoo);
+    p(InterfaceWithDefaultPackagePrivate::staticFoo);
+    p(c2.lambda()::foo);
+
+    B38257361.test();
+    B38257037.test();
+    B38306708.test();
+    B38308515.test();
+    B38302860.test();
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/other/ClassWithDefaultPackagePrivate.java b/src/test/examplesAndroidO/lambdadesugaringnplus/other/ClassWithDefaultPackagePrivate.java
new file mode 100644
index 0000000..102b13b
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/other/ClassWithDefaultPackagePrivate.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaringnplus.other;
+
+public class ClassWithDefaultPackagePrivate implements InterfaceWithDefaultPackagePrivate {
+  public InterfaceWithDefaultPackagePrivate lambda() {
+    return () -> ("lambda(" + InterfaceWithDefaultPackagePrivate.super.defaultFoo() + ")");
+  }
+
+  @Override
+  public String foo() {
+    throw new RuntimeException("Don't call me!");
+  }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/other/InterfaceWithDefaultPackagePrivate.java b/src/test/examplesAndroidO/lambdadesugaringnplus/other/InterfaceWithDefaultPackagePrivate.java
new file mode 100644
index 0000000..536bc29
--- /dev/null
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/other/InterfaceWithDefaultPackagePrivate.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package lambdadesugaringnplus.other;
+
+class PackagePrivate {
+  @Override
+  public String toString() {
+    return "PackagePrivate::toString()";
+  }
+}
+
+public interface InterfaceWithDefaultPackagePrivate {
+  String foo();
+
+  default String defaultFoo() {
+    return "defaultFoo: " + new PackagePrivate().toString();
+  }
+
+  static String staticFoo() {
+    return "staticFoo: " + new PackagePrivate().toString();
+  }
+}
diff --git a/src/test/examplesAndroidO/paramnames/ParameterNames.java b/src/test/examplesAndroidO/paramnames/ParameterNames.java
new file mode 100644
index 0000000..e9d9334
--- /dev/null
+++ b/src/test/examplesAndroidO/paramnames/ParameterNames.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package paramnames;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+public class ParameterNames {
+
+  private static final int MODIFIER_NONE = 0;
+  private static final int MODIFIER_FINAL = 0X10;
+
+  public ParameterNames(int a, final int b) {
+  }
+
+  public static void check(String expected, String checked) {
+    if (!expected.equals(checked)) {
+      throw new RuntimeException("Found '" + checked + "' while expecting '" + expected + "'");
+    }
+  }
+
+  public static void check(int expected, int checked) {
+    if (expected != checked) {
+      throw new RuntimeException("Found '" + checked + "' while expecting '" + expected + "'");
+    }
+  }
+
+  public static void myMethod(int a, final int b) throws NoSuchMethodException {
+    Class<ParameterNames> clazz = ParameterNames.class;
+    Method myMethod = clazz.getDeclaredMethod("myMethod", int.class, int.class);
+    Parameter[] parameters = myMethod.getParameters();
+    check(2, parameters.length);
+    check("a", parameters[0].getName());
+    check("b", parameters[1].getName());
+    check(MODIFIER_NONE, parameters[0].getModifiers());
+    check(MODIFIER_FINAL, parameters[1].getModifiers());
+  }
+
+  public static void myConstructor() throws NoSuchMethodException {
+    Class<ParameterNames> clazz = ParameterNames.class;
+    Constructor<?> myConstructor = clazz.getDeclaredConstructor(int.class, int.class);
+    Parameter[] parameters = myConstructor.getParameters();
+    check(2, parameters.length);
+    check("a", parameters[0].getName());
+    check("b", parameters[1].getName());
+    check(MODIFIER_NONE, parameters[0].getModifiers());
+    check(MODIFIER_FINAL, parameters[1].getModifiers());
+  }
+
+  public static void main(String[] args) throws NoSuchMethodException {
+    myMethod(0, 1);
+    myConstructor();
+  }
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/NumberAnnotation.java b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotation.java
new file mode 100644
index 0000000..7e304b9
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotation.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package repeat_annotations;
+
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(NumberAnnotations.class)
+public @interface NumberAnnotation {
+  int number() default 32;
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/NumberAnnotations.java b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotations.java
new file mode 100644
index 0000000..a70587a
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/NumberAnnotations.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package repeat_annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NumberAnnotations {
+  NumberAnnotation[] value();
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotations.java b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotations.java
new file mode 100644
index 0000000..d55de94
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotations.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package repeat_annotations;
+
+// Simple test of Java 8 repeated annotations.
+public class RepeatAnnotations {
+
+  @NumberAnnotation(number = 1)
+  @NumberAnnotation(number = 2)
+  @NumberAnnotation(number = 3)
+  class Inner {
+  }
+
+  public static void main(String[] args) {
+    NumberAnnotations annotations = Inner.class.getAnnotation(NumberAnnotations.class);
+    System.out.println(annotations.value().length);
+    for (NumberAnnotation annotation : annotations.value()) {
+      System.out.println("Number annotation value: " + annotation.number());
+    }
+  }
+}
diff --git a/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotationsNewApi.java b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotationsNewApi.java
new file mode 100644
index 0000000..6d23706
--- /dev/null
+++ b/src/test/examplesAndroidO/repeat_annotations/RepeatAnnotationsNewApi.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package repeat_annotations;
+
+// Simple test of Java 8 repeated annotations using the new getAnnotationsByType
+// API to access them.
+public class RepeatAnnotationsNewApi {
+
+  @NumberAnnotation(number = 1)
+  @NumberAnnotation(number = 2)
+  @NumberAnnotation(number = 3)
+  class Inner {
+  }
+
+  public static void main(String[] args) {
+    NumberAnnotation[] annotations = Inner.class.getAnnotationsByType(NumberAnnotation.class);
+    System.out.println(annotations.length);
+    for (NumberAnnotation annotation : annotations) {
+      System.out.println("Number annotation value: " + annotation.number());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
new file mode 100644
index 0000000..978f9de
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.errors.CompilationError;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.UnaryOperator;
+
+public class D8IncrementalRunExamplesAndroidOTest
+    extends RunExamplesAndroidOTest<D8Command.Builder> {
+
+  class D8IncrementalTestRunner extends TestRunner {
+
+    D8IncrementalTestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
+    }
+
+    @Override
+    TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    }
+
+    @Override
+    void build(Path testJarFile, Path out) throws Throwable {
+      // Collect classes and compile separately.
+      List<String> classFiles = collectClassFiles(testJarFile);
+      List<String> dexFiles = new ArrayList<>();
+      for (int i = 0; i < classFiles.size(); i++) {
+        Path indexedOut = Paths.get(
+            out.getParent().toString(), out.getFileName() + "." + i + ".zip");
+        compile(testJarFile.toString(), Collections.singletonList(classFiles.get(i)), indexedOut);
+        dexFiles.add(indexedOut.toString());
+      }
+
+      // When compiled add files separately, merge them.
+      compile(null, dexFiles, out);
+    }
+
+    private List<String> collectClassFiles(Path testJarFile) {
+      List<String> result = new ArrayList<>();
+      // Collect Java 8 classes.
+      Path parent = testJarFile.getParent();
+      File packageDir = parent.resolve(Paths.get("classes", packageName)).toFile();
+      collectClassFiles(packageDir, result);
+      // Collect legacy classes.
+      Path legacyPath = Paths.get("..",
+          parent.getFileName().toString() + "Legacy", "classes", packageName);
+      packageDir = parent.resolve(legacyPath).toFile();
+      collectClassFiles(packageDir, result);
+      Collections.sort(result);
+      return result;
+    }
+
+    private void collectClassFiles(File dir, List<String> result) {
+      File[] files = dir.listFiles();
+      if (files != null) {
+        for (File file : files) {
+          if (file.isDirectory()) {
+            collectClassFiles(file, result);
+          } else {
+            result.add(file.getAbsolutePath());
+          }
+        }
+      }
+    }
+
+    private void compile(String classpath, List<String> inputFiles, Path out) throws Throwable {
+      D8Command.Builder builder = D8Command.builder();
+      if (classpath != null) {
+        builder.addClasspathFiles(Paths.get(classpath));
+      }
+      for (String inputFile : inputFiles) {
+        builder.addProgramFiles(Paths.get(inputFile));
+      }
+      for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
+      }
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      D8Command command = builder.setOutputPath(out).build();
+      try {
+        ToolHelper.runD8(command, this::combinedOptionConsumer);
+      } catch (RuntimeException re) {
+        throw re instanceof CompilationError ? re : re.getCause();
+      }
+    }
+  }
+
+  @Override
+  TestRunner test(String testName, String packageName, String mainClass) {
+    return new D8IncrementalTestRunner(testName, packageName, mainClass);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
new file mode 100644
index 0000000..ea94040
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.errors.CompilationError;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.UnaryOperator;
+
+public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> {
+
+  class D8TestRunner extends TestRunner {
+
+    D8TestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
+    }
+
+    @Override
+    TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    }
+
+    @Override
+    void build(Path inputFile, Path out) throws Throwable {
+      D8Command.Builder builder = D8Command.builder();
+      for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
+        builder = transformation.apply(builder);
+      }
+      builder.addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(builder.getMinApiLevel())));
+      D8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+      try {
+        ToolHelper.runD8(command, this::combinedOptionConsumer);
+      } catch (RuntimeException re) {
+        throw re instanceof CompilationError ? re : re.getCause();
+      }
+    }
+  }
+
+  @Override
+  TestRunner test(String testName, String packageName, String mainClass) {
+    return new D8TestRunner(testName, packageName, mainClass);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
new file mode 100644
index 0000000..7f9ecb0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -0,0 +1,4833 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static com.android.tools.r8.TestCondition.D8_COMPILER;
+import static com.android.tools.r8.TestCondition.R8_COMPILER;
+import static com.android.tools.r8.TestCondition.any;
+import static com.android.tools.r8.TestCondition.match;
+import static com.android.tools.r8.TestCondition.runtimes;
+
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.R8RunArtTestsTest.DexTool;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+
+public class JctfTestSpecifications {
+
+  public enum Outcome {
+    PASSES,
+    FAILS_WITH_ART,
+    TIMEOUTS_WITH_ART,
+    FLAKY_WITH_ART
+  }
+
+  public static final Multimap<String, TestCondition> failuresToTriage =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+
+          .put("math.BigInteger.nextProbablePrime.BigInteger_nextProbablePrime_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.ArithmeticException
+
+          .put("math.BigInteger.ConstructorLjava_lang_String.BigInteger_Constructor_A02", any())
+          // 1) t03
+          // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+          .put("lang.StringBuffer.insertILjava_lang_Object.StringBuffer_insert_A01",
+              match(runtimes(DexVm.ART_DEFAULT)))
+          // 1) t01
+          // java.lang.StringIndexOutOfBoundsException: length=21; regionStart=0; regionLength=42
+
+          .put("lang.StringBuffer.serialization.StringBuffer_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StringBuffer/serialization/StringBuffer_serialization_A01.golden.ser
+
+          .put(
+              "lang.CloneNotSupportedException.serialization.CloneNotSupportedException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/CloneNotSupportedException/serialization/CloneNotSupportedException_serialization_A01.golden.0.ser
+
+          .put("lang.NumberFormatException.serialization.NumberFormatException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NumberFormatException/serialization/NumberFormatException_serialization_A01.golden.0.ser
+
+          .put("lang.StrictMath.roundF.StrictMath_round_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t01
+          // java.lang.AssertionError: Wrong result produced for argument: 0.49999997 expected:<1.0> but was:<0.0>
+
+          .put("lang.StrictMath.roundD.StrictMath_round_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t01
+          // java.lang.AssertionError: Wrong result produced for argument: 0.49999999999999994 expected:<1.0> but was:<0.0>
+
+          .put("lang.StrictMath.atan2DD.StrictMath_atan2_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad value returned for arguments: 2.225073858507201E-308 0.9999999999999999 expected:<2.2250738585072014E-308> but was:<2.225073858507201E-308>
+
+          .put("lang.Thread.stop.Thread_stop_A05", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.resume.Thread_resume_A02", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.suspend.Thread_suspend_A02", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+          // 3) t03
+          // java.lang.UnsupportedOperationException
+          // 4) t04
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.stop.Thread_stop_A03", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+          // 3) t03
+          // java.lang.UnsupportedOperationException
+          // 4) t04
+          // java.lang.UnsupportedOperationException
+          // 5) t05
+          // java.lang.UnsupportedOperationException
+          // 6) t06
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.interrupt.Thread_interrupt_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put("lang.Thread.stop.Thread_stop_A04", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1)))
+          // 1) t01
+          // java.lang.OutOfMemoryError: pthread_create (-8589934591GB stack) failed: Resource temporarily unavailable
+
+          .put("lang.Thread.getUncaughtExceptionHandler.Thread_getUncaughtExceptionHandler_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: expected null, but was:<java.lang.ThreadGroup[name=main,maxpri=10]>
+
+          .put("lang.Thread.getStackTrace.Thread_getStackTrace_A02", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<get[]StackTrace> but was:<get[Thread]StackTrace>
+
+          .put("lang.Thread.enumerate$Ljava_lang_Thread.Thread_enumerate_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: test failed with error:java.lang.SecurityException
+
+          .put("lang.Thread.countStackFrames.Thread_countStackFrames_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+          // 4) t04
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalThreadStateException
+
+          .put("lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put("lang.Thread.destroy.Thread_destroy_A01", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NoSuchMethodError> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NoSuchMethodError> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.isAlive.Thread_isAlive_A01", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A04", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A03", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+          // 3) t03
+          // java.lang.UnsupportedOperationException
+          // 4) t05
+          // java.lang.UnsupportedOperationException
+          // 5) t06
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A05", any())
+          // 1) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.getPriority.Thread_getPriority_A01", any())
+          // 1) t02
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Thread.getContextClassLoader.Thread_getContextClassLoader_A03",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t01
+          // java.lang.AssertionError: improper ClassLoader expected same:<null> was not:<dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit7794202178392390143/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]]>
+
+          .put("lang.OutOfMemoryError.serialization.OutOfMemoryError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/OutOfMemoryError/serialization/OutOfMemoryError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.RuntimePermission.ConstructorLjava_lang_StringLjava_lang_String.RuntimePermission_Constructor_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<a> but was:<null>
+          // 2) t02
+          // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+          .put("lang.RuntimePermission.serialization.RuntimePermission_serialization_A01", any())
+          // 1) t01
+          // java.lang.RuntimeException: Failed to detect comparator
+          // 2) t02
+          // java.lang.RuntimeException: Failed to detect comparator
+
+          .put(
+              "lang.RuntimePermission.ConstructorLjava_lang_StringLjava_lang_String.RuntimePermission_Constructor_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.RuntimePermission.ConstructorLjava_lang_StringLjava_lang_String.RuntimePermission_Constructor_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A17", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put(
+              "lang.RuntimePermission.ConstructorLjava_lang_String.RuntimePermission_Constructor_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.RuntimePermission.ConstructorLjava_lang_String.RuntimePermission_Constructor_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put(
+              "lang.RuntimePermission.ConstructorLjava_lang_String.RuntimePermission_Constructor_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<a> but was:<null>
+          // 2) t02
+          // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A26", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A04", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String/Thread_Constructor_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A04" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ/Thread_Constructor_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A05" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String/Thread_Constructor_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A04" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ/Thread_Constructor_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A05" on path: DexPathList[[dex file "/tmp/junit4094594533964383293/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A03", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setContextClassLoaderLjava_lang_ClassLoader/Thread_setContextClassLoader_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setContextClassLoaderLjava_lang_ClassLoader.Thread_setContextClassLoader_A02" on path: DexPathList[[dex file "/tmp/junit8983002984475576815/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setContextClassLoaderLjava_lang_ClassLoader/Thread_setContextClassLoader_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setContextClassLoaderLjava_lang_ClassLoader.Thread_setContextClassLoader_A02" on path: DexPathList[[dex file "/tmp/junit8983002984475576815/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A25", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDefaultUncaughtExceptionHandler/Thread_setDefaultUncaughtExceptionHandler_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDefaultUncaughtExceptionHandler.Thread_setDefaultUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1629291049961551929/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDefaultUncaughtExceptionHandler/Thread_setDefaultUncaughtExceptionHandler_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDefaultUncaughtExceptionHandler.Thread_setDefaultUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1629291049961551929/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A06", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/Constructor/SecurityManager_Constructor_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.Constructor.SecurityManager_Constructor_A01" on path: DexPathList[[dex file "/tmp/junit4316252965733666132/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/Constructor/SecurityManager_Constructor_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.Constructor.SecurityManager_Constructor_A01" on path: DexPathList[[dex file "/tmp/junit4316252965733666132/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A21", any())
+          // 1) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkPackageDefinitionLjava_lang_String/SecurityManager_checkPackageDefinition_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkPackageDefinitionLjava_lang_String.SecurityManager_checkPackageDefinition_A01" on path: DexPathList[[dex file "/tmp/junit622469116149972008/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A22", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t05
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 6) t06
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 7) t07
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 8) t08
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 9) t09
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 10) t10
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 11) t11
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 12) t12
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 13) t13
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 14) t14
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 15) t15
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 16) t16
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 17) t17
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 18) t18
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 19) t19
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 20) t20
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 21) t21
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 22) t22
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 23) t23
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 24) t24
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 25) t25
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 26) t26
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 27) t27
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 28) t28
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 29) t29
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 30) t30
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 31) t31
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkMemberAccessLjava_lang_ClassI/SecurityManager_checkMemberAccess_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 32) t32
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkMemberAccessLjava_lang_ClassI/SecurityManager_checkMemberAccess_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A02" on path: DexPathList[[dex file "/tmp/junit2603421343038865741/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A11", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setInLjava_io_InputStream/System_setIn_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setInLjava_io_InputStream.System_setIn_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setOutLjava_io_PrintStream/System_setOut_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setOutLjava_io_PrintStream.System_setOut_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setErrLjava_io_PrintStream/System_setErr_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setErrLjava_io_PrintStream.System_setErr_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setInLjava_io_InputStream/System_setIn_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setInLjava_io_InputStream.System_setIn_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t05
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setOutLjava_io_PrintStream/System_setOut_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setOutLjava_io_PrintStream.System_setOut_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 6) t06
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setErrLjava_io_PrintStream/System_setErr_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setErrLjava_io_PrintStream.System_setErr_A02" on path: DexPathList[[dex file "/tmp/junit8702631411569316964/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A08", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/exitI/Runtime_exit_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/haltI/Runtime_halt_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/exitI/Runtime_exit_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/haltI/Runtime_halt_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03" on path: DexPathList[[dex file "/tmp/junit161743829053407699/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A16", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A12", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/interrupt/Thread_interrupt_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.interrupt.Thread_interrupt_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/stop/Thread_stop_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.stop.Thread_stop_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/suspend/Thread_suspend_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.suspend.Thread_suspend_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/resume/Thread_resume_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.resume.Thread_resume_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t05
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDaemonZ/Thread_setDaemon_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDaemonZ.Thread_setDaemon_A03" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 6) t06
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setPriorityI/Thread_setPriority_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setPriorityI.Thread_setPriority_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 7) t07
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setNameLjava_lang_String/Thread_setName_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setNameLjava_lang_String.Thread_setName_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 8) t08
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setUncaughtExceptionHandler/Thread_setUncaughtExceptionHandler_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setUncaughtExceptionHandler.Thread_setUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 9) t09
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/interrupt/Thread_interrupt_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.interrupt.Thread_interrupt_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 10) t10
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/suspend/Thread_suspend_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.suspend.Thread_suspend_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 11) t11
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/stop/Thread_stop_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.stop.Thread_stop_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 12) t12
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/resume/Thread_resume_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.resume.Thread_resume_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 13) t13
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setDaemonZ/Thread_setDaemon_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setDaemonZ.Thread_setDaemon_A03" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 14) t14
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setPriorityI/Thread_setPriority_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setPriorityI.Thread_setPriority_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 15) t15
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setNameLjava_lang_String/Thread_setName_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setNameLjava_lang_String.Thread_setName_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 16) t16
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/setUncaughtExceptionHandler/Thread_setUncaughtExceptionHandler_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.setUncaughtExceptionHandler.Thread_setUncaughtExceptionHandler_A02" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 17) t17
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_Thread/SecurityManager_checkAccess_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 18) t18
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_Thread/SecurityManager_checkAccess_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit1615879415958114883/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A24", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getAllStackTraces/Thread_getAllStackTraces_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A02" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getStackTrace/Thread_getStackTrace_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getStackTrace.Thread_getStackTrace_A03" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getStackTrace/Thread_getStackTrace_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getStackTrace.Thread_getStackTrace_A03" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Thread/getAllStackTraces/Thread_getAllStackTraces_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A02" on path: DexPathList[[dex file "/tmp/junit6143640280960303446/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A23", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A18", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A19", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLjava_lang_String/Runtime_load_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLjava_lang_String.Runtime_load_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLibraryLjava_lang_String/Runtime_loadLibrary_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLjava_lang_String/Runtime_load_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLjava_lang_String.Runtime_load_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/loadLibraryLjava_lang_String/Runtime_loadLibrary_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A03" on path: DexPathList[[dex file "/tmp/junit3066867775929885441/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A07", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenv/System_getenv_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenv.System_getenv_A04" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenvLjava_lang_String/System_getenv_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenvLjava_lang_String.System_getenv_A03" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ProcessBuilder/environment/ProcessBuilder_environment_A07;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ProcessBuilder.environment.ProcessBuilder_environment_A07" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenv/System_getenv_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenv.System_getenv_A04" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t05
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/getenvLjava_lang_String/System_getenv_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.getenvLjava_lang_String.System_getenv_A03" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 6) t06
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ProcessBuilder/environment/ProcessBuilder_environment_A07;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ProcessBuilder.environment.ProcessBuilder_environment_A07" on path: DexPathList[[dex file "/tmp/junit8429314639688357329/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A20", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t05
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 6) t06
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 7) t07
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 8) t08
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredClasses/Class_getDeclaredClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 9) t09
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructors/Class_getDeclaredConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 10) t10
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredConstructor$Ljava_lang_Class/Class_getDeclaredConstructor_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 11) t11
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFields/Class_getDeclaredFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFields.Class_getDeclaredFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 12) t12
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredFieldLjava_lang_String/Class_getDeclaredField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 13) t13
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethods/Class_getDeclaredMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 14) t14
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getDeclaredMethodLjava_lang_String$Ljava_lang_Class/Class_getDeclaredMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 15) t15
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 16) t16
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/newInstance/Class_newInstance_A07;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.newInstance.Class_newInstance_A07" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 17) t17
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 18) t18
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 19) t19
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 20) t20
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 21) t21
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 22) t22
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 23) t23
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 24) t24
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getClasses/Class_getClasses_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getClasses.Class_getClasses_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 25) t25
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructors/Class_getConstructors_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructors.Class_getConstructors_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 26) t26
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getConstructor$Ljava_lang_Class/Class_getConstructor_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 27) t27
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFields/Class_getFields_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFields.Class_getFields_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 28) t28
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getFieldLjava_lang_String/Class_getField_A04;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A04" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 29) t29
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethods/Class_getMethods_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethods.Class_getMethods_A02" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 30) t30
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getMethodLjava_lang_String$Ljava_lang_Class/Class_getMethod_A05;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 31) t32
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkPackageAccessLjava_lang_String/SecurityManager_checkPackageAccess_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkPackageAccessLjava_lang_String.SecurityManager_checkPackageAccess_A01" on path: DexPathList[[dex file "/tmp/junit7609456538458065688/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A15", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getProtectionDomain/Class_getProtectionDomain_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getProtectionDomain.Class_getProtectionDomain_A02" on path: DexPathList[[dex file "/tmp/junit2159138358960857439/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Class/getProtectionDomain/Class_getProtectionDomain_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.getProtectionDomain.Class_getProtectionDomain_A02" on path: DexPathList[[dex file "/tmp/junit2159138358960857439/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A05", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setSecurityManagerLjava_lang_SecurityManager/System_setSecurityManager_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A01" on path: DexPathList[[dex file "/tmp/junit5991060062859827030/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/System/setSecurityManagerLjava_lang_SecurityManager/System_setSecurityManager_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A01" on path: DexPathList[[dex file "/tmp/junit5991060062859827030/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A09", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/addShutdownHookLjava_lang_Thread/Runtime_addShutdownHook_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/removeShutdownHookLjava_lang_Thread/Runtime_removeShutdownHook_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/addShutdownHookLjava_lang_Thread/Runtime_addShutdownHook_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/Runtime/removeShutdownHookLjava_lang_Thread/Runtime_removeShutdownHook_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A01" on path: DexPathList[[dex file "/tmp/junit719694264001364171/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A10", any())
+          // 1) t01
+          // java.lang.AssertionError: Not implemented
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A01", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/Constructor/ClassLoader_Constructor_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.Constructor.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/ConstructorLjava_lang_ClassLoader/ClassLoader_Constructor_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/Constructor/ClassLoader_Constructor_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.Constructor.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/ConstructorLjava_lang_ClassLoader/ClassLoader_Constructor_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A02" on path: DexPathList[[dex file "/tmp/junit6765412840574788386/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A06",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A14", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/interrupt/ThreadGroup_interrupt_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.interrupt.ThreadGroup_interrupt_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/stop/ThreadGroup_stop_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.stop.ThreadGroup_stop_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/suspend/ThreadGroup_suspend_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.suspend.ThreadGroup_suspend_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/resume/ThreadGroup_resume_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.resume.ThreadGroup_resume_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t05
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setDaemonZ/ThreadGroup_setDaemon_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setDaemonZ.ThreadGroup_setDaemon_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 6) t06
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setMaxPriorityI/ThreadGroup_setMaxPriority_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 7) t07
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/getParent/ThreadGroup_getParent_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.getParent.ThreadGroup_getParent_A03" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 8) t08
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/enumerate$ThreadGroup/ThreadGroup_enumerate_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.enumerate$ThreadGroup.ThreadGroup_enumerate_A03" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 9) t09
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/interrupt/ThreadGroup_interrupt_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.interrupt.ThreadGroup_interrupt_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 10) t10
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/suspend/ThreadGroup_suspend_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.suspend.ThreadGroup_suspend_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 11) t11
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/stop/ThreadGroup_stop_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.stop.ThreadGroup_stop_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 12) t12
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/resume/ThreadGroup_resume_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.resume.ThreadGroup_resume_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 13) t13
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setDaemonZ/ThreadGroup_setDaemon_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setDaemonZ.ThreadGroup_setDaemon_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 14) t14
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/setMaxPriorityI/ThreadGroup_setMaxPriority_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 15) t15
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/getParent/ThreadGroup_getParent_A02;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.getParent.ThreadGroup_getParent_A02" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 16) t16
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ThreadGroup/enumerate$ThreadGroup/ThreadGroup_enumerate_A03;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ThreadGroup.enumerate$ThreadGroup.ThreadGroup_enumerate_A03" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 17) t17
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_ThreadGroup/SecurityManager_checkAccess_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 18) t18
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/SecurityManager/checkAccessLjava_lang_ThreadGroup/SecurityManager_checkAccess_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A01" on path: DexPathList[[dex file "/tmp/junit7453598412317397853/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A05",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A02",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A04",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A03",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClassLjava_lang_String$BII.ClassLoader_defineClass_A07",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+          .put(
+              "lang.ClassLoader.setPackageAssertionStatusLjava_lang_StringZ.ClassLoader_setPackageAssertionStatus_A02",
+              any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.setPackageAssertionStatusLjava_lang_StringZ.ClassLoader_setPackageAssertionStatus_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.setPackageAssertionStatusLjava_lang_StringZ.ClassLoader_setPackageAssertionStatus_A03",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A03", any())
+          // 1) t03
+          // java.lang.AssertionError: ClassNotFoundException expected for class: java/lang/Object
+
+          .put("lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A04", any())
+          // 1) t01(com.google.jctf.test.lib.java.lang.ClassLoader.loadClassLjava_lang_StringZ.ClassLoader_loadClass_A04)
+          // java.lang.AssertionError: NoClassDefFoundError expected for class: com/google/jctf/test/lib/java/lang/ClassLoader/loadClassLjava_lang_StringZ/ClassLoader_loadClass_A04
+
+          .put(
+              "lang.ClassLoader.definePackageLjava_lang_String6Ljava_net_URL.ClassLoader_definePackage_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+          // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+          // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+          // Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
+
+          .put(
+              "lang.ClassLoader.definePackageLjava_lang_String6Ljava_net_URL.ClassLoader_definePackage_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.ClassLoader.definePackageLjava_lang_String6Ljava_net_URL.ClassLoader_definePackage_A03",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: IllegalArgumentException expected for package name: name
+          // 2) t03
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A05",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.getResourceLjava_lang_String.ClassLoader_getResource_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+          // 2) t02
+          // java.lang.AssertionError: Resource not found:
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A02",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 4) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A06",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A03",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A07",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_StringLjava_nio_ByteBufferLjava_security_ProtectionDomain.ClassLoader_defineClass_A04",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.setSignersLjava_lang_Class$Ljava_lang_Object.ClassLoader_setSigners_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t04
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.ClassLoader.clearAssertionStatus.ClassLoader_clearAssertionStatus_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 4) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.Constructor.ClassLoader_Constructor_A02", any())
+          // 1) t02
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+
+          .put(
+              "lang.ClassLoader.getSystemResourceLjava_lang_String.ClassLoader_getSystemResource_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+          // 2) t02
+          // java.lang.AssertionError: Resource not found:
+
+          .put("lang.ClassLoader.getResourcesLjava_lang_String.ClassLoader_getResources_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+          // 2) t04
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.ClassLoader.resolveClassLjava_lang_Class.ClassLoader_resolveClass_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.ClassLoader.getResourceAsStreamLjava_lang_String.ClassLoader_getResourceAsStream_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+          // 2) t04
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.ClassLoader.findLoadedClassLjava_lang_String.ClassLoader_findLoadedClass_A01",
+              any())
+          // 1) initializationError
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/ClassLoader/findLoadedClassLjava_lang_String/TestLoader;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.ClassLoader.findLoadedClassLjava_lang_String.TestLoader" on path: DexPathList[[dex file "/tmp/junit1789265657215742712/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A02",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.resolveClassLjava_lang_Class.ClassLoader_resolveClass_A01", any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.setDefaultAssertionStatusZ.ClassLoader_setDefaultAssertionStatus_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A05",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A06",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A08",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A03",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 4) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.getSystemResourcesLjava_lang_String.ClassLoader_getSystemResources_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Resource not found: java/lang/ClassLoader.class
+          // 2) t02
+          // java.lang.AssertionError: Resource not found:
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A07",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A09",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+          .put(
+              "lang.ClassLoader.defineClassLjava_lang_String$BIILjava_security_ProtectionDomain.ClassLoader_defineClass_A04",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A02", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.getSystemResourceAsStreamLjava_lang_String.ClassLoader_getSystemResourceAsStream_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.getPackages.ClassLoader_getPackages_A01", any())
+          // 1) t02
+          // java.lang.AssertionError:
+          // Expected: (an array containing <package java.lang, Unknown, version 0.0> and an array containing <package java.security, Unknown, version 0.0> and an array containing <package java.util, Unknown, version 0.0>)
+          // but: an array containing <package java.lang, Unknown, version 0.0> was []
+          // 2) t04
+          // java.lang.AssertionError:
+          // Expected: an array containing <package com.google.jctf.test.lib.java.lang.ClassLoader.getPackages, Unknown, version 0.0>
+          // but: was []
+          // 3) t05
+          // java.lang.AssertionError: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+          // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+          // Caused by: java.lang.ClassNotFoundException: com.android.okhttp.HttpHandler
+          // Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available
+
+          .put(
+              "lang.ClassLoader.setClassAssertionStatusLjava_lang_StringZ.ClassLoader_setClassAssertionStatus_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A03", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.defineClass$BII.ClassLoader_defineClass_A04", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.UnsupportedOperationException>
+          // Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
+
+          .put("lang.ClassLoader.getParent.ClassLoader_getParent_A01", any())
+          // 1) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.setClassAssertionStatusLjava_lang_StringZ.ClassLoader_setClassAssertionStatus_A04",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.ClassLoader.setClassAssertionStatusLjava_lang_StringZ.ClassLoader_setClassAssertionStatus_A02",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.ClassLoader.getParent.ClassLoader_getParent_A02", any())
+          // 1) t02
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.ClassLoader.getSystemClassLoader.ClassLoader_getSystemClassLoader_A02", any())
+          // 1) t02
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+
+          .put("lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A02",
+              any())
+          // 1) t02
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+
+          .put("lang.ClassLoader.findSystemClassLjava_lang_String.ClassLoader_findSystemClass_A04",
+              any())
+          // 1) t01
+          // java.lang.ClassNotFoundException: Invalid name: com/google/jctf/test/lib/java/lang/ClassLoader/findSystemClassLjava_lang_String/ClassLoader_findSystemClass_A04
+
+          .put("lang.ClassLoader.getPackageLjava_lang_String.ClassLoader_getPackage_A01", any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Package.getName()' on a null object reference
+          // 2) t04
+          // java.lang.AssertionError: expected:<package com.google.jctf.test.lib.java.lang.ClassLoader.getPackageLjava_lang_String, Unknown, version 0.0> but was:<null>
+          // 3) t05
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.NoClassDefFoundError.serialization.NoClassDefFoundError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoClassDefFoundError/serialization/NoClassDefFoundError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.TypeNotPresentException.serialization.TypeNotPresentException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/TypeNotPresentException/serialization/TypeNotPresentException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.IndexOutOfBoundsException.serialization.IndexOutOfBoundsException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IndexOutOfBoundsException/serialization/IndexOutOfBoundsException_serialization_A01.golden.0.ser
+
+          .put("lang.Enum.serialization.Enum_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Enum/serialization/Enum_serialization_A01.golden.0.ser
+
+          .put("lang.Enum.ConstructorLjava_lang_StringI.Enum_Constructor_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put("lang.InternalError.serialization.InternalError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InternalError/serialization/InternalError_serialization_A01.golden.0.ser
+
+          .put("lang.Error.serialization.Error_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Error/serialization/Error_serialization_A01.golden.0.ser
+
+          .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A02", any())
+          // 1) t05
+          // java.lang.Exception: Unexpected exception, expected<java.lang.UnsatisfiedLinkError> but was<java.lang.AssertionError>
+          // Caused by: java.lang.AssertionError: Misconfiguration, java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit955082866383345627/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]] couldn't find "libstub.so"
+          // 2) t06
+          // java.lang.Exception: Unexpected exception, expected<java.lang.UnsatisfiedLinkError> but was<java.lang.AssertionError>
+          // Caused by: java.lang.AssertionError: Misconfiguration, java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit955082866383345627/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]] couldn't find "libstub.so"
+
+          .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A05", any())
+          // 1) t01
+          // java.lang.AssertionError
+          // 2) t02
+          // java.lang.AssertionError
+
+          .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A03", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.AssertionError
+
+          .put("lang.Runtime.loadLjava_lang_String.Runtime_load_A04", any())
+          // 1) t01
+          // java.lang.AssertionError: Misconfiguration, missing property: 'jctf.library.path'
+          // 2) t02
+          // java.lang.AssertionError
+
+          .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A02", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A02$T01
+          // ]>
+
+          .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A01", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+          // ]>
+
+          .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A04", any())
+          // 1) t01
+          // java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[dex file "/tmp/junit958205418142169834/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]] couldn't find "libstub.so"
+          // 2) t02
+          // java.lang.AssertionError
+
+          .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A05", any())
+          // 1) t01
+          // java.lang.AssertionError
+          // 2) t02
+          // java.lang.AssertionError
+
+          .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A03", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.AssertionError
+
+          .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A02", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.execLjava_lang_String.Runtime_exec_A02$T01
+          // ]>
+
+          .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Runtime.loadLibraryLjava_lang_String.Runtime_loadLibrary_A02", any())
+          // 1) t03
+          // java.lang.Exception: Unexpected exception, expected<java.lang.UnsatisfiedLinkError> but was<java.lang.AssertionError>
+          // Caused by: java.lang.AssertionError: Misconfiguration, missing property: 'jctf.library.path'
+
+          .put("lang.Runtime.traceMethodCallsZ.Runtime_traceMethodCalls_A01", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A08", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A08$T01
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A01", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+          // ]>
+
+          .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A03$T01
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A07", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A07$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A07$T02
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a string containing "TERMINATED_BY_EXCEPTION"
+          // but: was "Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T02
+          // "
+          // 3) t03
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T03
+          // expected:<0> but was:<1>
+          // 4) t04
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T04
+          // expected:<0> but was:<1>
+          // 5) t05
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T05
+          // expected:<0> but was:<1>
+          // 6) t06
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T06
+          // expected:<0> but was:<1>
+          // 7) t07
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T07
+          // expected:<0> but was:<1>
+          // 8) t08
+          // java.lang.AssertionError:
+          // Expected: a string containing "java.lang.UnknownError: Main Thread Exit"
+          // but: was "Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A05$T08
+          // "
+
+          .put("lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A06", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A06$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.addShutdownHookLjava_lang_Thread.Runtime_addShutdownHook_A06$T02
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A02", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+          // ]>
+          // 2) t02
+          // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A02$T02
+          // ]>
+
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A01", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+          // ]>
+
+          .put("lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A02$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A02$T02
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A01", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+          // ]>
+
+          .put("lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A01",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A02", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+          // ]>
+          // 2) t02
+          // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A02$T02
+          // ]>
+
+          .put("lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A03",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.removeShutdownHookLjava_lang_Thread.Runtime_removeShutdownHook_A03$T03
+          // expected:<0> but was:<1>
+
+          .put(
+              "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: actual=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+          // : array lengths differed, expected.length=8 actual.length=9
+
+          .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put(
+              "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02",
+              any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+          // ]>
+          // 2) t02
+          // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02$T02
+          // ]>
+
+          .put("lang.Runtime.haltI.Runtime_halt_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A02$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A02$T02
+          // expected:<0> but was:<1>
+
+          .put(
+              "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A03",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Runtime.haltI.Runtime_halt_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A03$T02
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T02
+          // expected:<0> but was:<1>
+          // 3) t03
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T03
+          // expected:<0> but was:<1>
+          // 4) t04
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T04
+          // expected:<0> but was:<1>
+          // 5) t06
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T06
+          // expected:<0> but was:<1>
+          // 6) t07
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T07
+          // expected:<0> but was:<1>
+          // 7) t08
+          // java.lang.AssertionError:
+          // Expected: a string containing "java.lang.UnknownError: Main Thread Exit"
+          // but: was "Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A01$T08
+          // "
+
+          .put("lang.Runtime.haltI.Runtime_halt_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A01$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.haltI.Runtime_halt_A01$T02
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A03", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A03$T03
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A03",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A01",
+              any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[com google jctf test lib java lang Runtime]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoArgs
+          // ]>
+
+          .put("lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.runFinalizersOnExitZ.Runtime_runFinalizersOnExit_A02$T01
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.exitI.Runtime_exit_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A03$T01
+          // expected:<123> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A03$T01
+          // 3) t03
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A03$T03
+
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02",
+              any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<[t01]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.EchoEnv
+          // ]>
+          // 2) t02
+          // org.junit.ComparisonFailure: expected:<[t02]> but was:<[Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A02$T02
+          // ]>
+
+          .put("lang.Runtime.exitI.Runtime_exit_A04", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A04$T02
+          // expected:<0> but was:<1>
+
+          .put("lang.NoSuchMethodException.serialization.NoSuchMethodException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchMethodException/serialization/NoSuchMethodException_serialization_A01.golden.0.ser
+
+          .put("lang.Runtime.exitI.Runtime_exit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T02
+          // expected:<0> but was:<1>
+          // 3) t03
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T03
+          // out=
+          // 4) t04
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T04
+          // expected:<-1> but was:<1>
+          // 5) t05
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A01$T05
+          // expected:<0> but was:<1>
+
+          .put("lang.Runtime.exitI.Runtime_exit_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T01
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T02
+          // out=
+          // 3) t03
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T03
+          // out=
+          // 4) t04
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T04
+          // expected:<0> but was:<1>
+          // 5) t05
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T05
+          // out=
+          // 6) t06
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T06
+          // expected:<0> but was:<1>
+          // 7) t07
+          // java.lang.AssertionError: Process did not block but exited with code 1;
+          // err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.Runtime.exitI.Runtime_exit_A02$T07
+          // out=
+
+          .put("lang.InstantiationException.serialization.InstantiationException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InstantiationException/serialization/InstantiationException_serialization_A01.golden.0.ser
+
+          .put("lang.Exception.serialization.Exception_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Exception/serialization/Exception_serialization_A01.golden.0.ser
+
+          .put("lang.StackOverflowError.serialization.StackOverflowError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StackOverflowError/serialization/StackOverflowError_serialization_A01.golden.0.ser
+
+          .put("lang.NoSuchFieldException.serialization.NoSuchFieldException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchFieldException/serialization/NoSuchFieldException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.NegativeArraySizeException.serialization.NegativeArraySizeException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NegativeArraySizeException/serialization/NegativeArraySizeException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.ArrayIndexOutOfBoundsException.serialization.ArrayIndexOutOfBoundsException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ArrayIndexOutOfBoundsException/serialization/ArrayIndexOutOfBoundsException_serialization_A01.golden.0.ser
+
+          .put("lang.VerifyError.serialization.VerifyError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/VerifyError/serialization/VerifyError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.IllegalArgumentException.serialization.IllegalArgumentException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalArgumentException/serialization/IllegalArgumentException_serialization_A01.golden.0.ser
+
+          .put("lang.IllegalStateException.serialization.IllegalStateException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalStateException/serialization/IllegalStateException_serialization_A01.golden.0.ser
+
+          .put("lang.Double.serialization.Double_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Double/serialization/Double_serialization_A01.golden.0.ser
+
+          .put("lang.Double.toStringD.Double_toString_A05", any())
+          // 1) t01
+          // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
+
+          .put("lang.ArithmeticException.serialization.ArithmeticException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ArithmeticException/serialization/ArithmeticException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.ExceptionInInitializerError.serialization.ExceptionInInitializerError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ExceptionInInitializerError/serialization/ExceptionInInitializerError_serialization_A01.golden.0.ser
+
+          .put("lang.ThreadLocal.Class.ThreadLocal_class_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Stale thread-local value was not finalized
+
+          .put("lang.Byte.serialization.Byte_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Byte/serialization/Byte_serialization_A01.golden.0.ser
+
+          .put("lang.Byte.parseByteLjava_lang_StringI.Byte_parseByte_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Parsed Byte instance from string:+1 radix:10
+
+          .put("lang.Byte.valueOfLjava_lang_StringI.Byte_valueOf_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Parsed Byte instance from string:+1 radix:10
+
+          .put("lang.Byte.valueOfLjava_lang_String.Byte_ValueOf_A02", any())
+          // 1) t02
+          // java.lang.AssertionError: Parsed Byte instance from string:+1
+
+          .put("lang.Byte.decodeLjava_lang_String.Byte_decode_A04", any())
+          // 1) t01
+          // java.lang.AssertionError: Decoded Byte instance from string:+1
+
+          .put("lang.LinkageError.serialization.LinkageError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/LinkageError/serialization/LinkageError_serialization_A01.golden.0.ser
+
+          .put("lang.ClassCastException.serialization.ClassCastException_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassCastException/serialization/ClassCastException_serialization_A01.golden.0.ser
+
+          .put("lang.Byte.ConstructorLjava_lang_String.Byte_Constructor_A02", any())
+          // 1) t02
+          // java.lang.AssertionError: Parsed Byte instance from string:+1
+
+          .put("lang.Byte.parseByteLjava_lang_String.Byte_parseByte_A02", any())
+          // 1) t02
+          // java.lang.AssertionError: Parsed Byte instance from string:+1
+
+          .put("lang.NoSuchFieldError.serialization.NoSuchFieldError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchFieldError/serialization/NoSuchFieldError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.UnsupportedOperationException.serialization.UnsupportedOperationException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnsupportedOperationException/serialization/UnsupportedOperationException_serialization_A01.golden.0.ser
+
+          .put("lang.NoSuchMethodError.serialization.NoSuchMethodError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NoSuchMethodError/serialization/NoSuchMethodError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.IllegalMonitorStateException.serialization.IllegalMonitorStateException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalMonitorStateException/serialization/IllegalMonitorStateException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.StringIndexOutOfBoundsException.serialization.StringIndexOutOfBoundsException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StringIndexOutOfBoundsException/serialization/StringIndexOutOfBoundsException_serialization_A01.golden.0.ser
+
+          .put("lang.SecurityException.serialization.SecurityException_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/SecurityException/serialization/SecurityException_serialization_A01.golden.0.ser
+
+          .put("lang.IllegalAccessError.serialization.IllegalAccessError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalAccessError/serialization/IllegalAccessError_serialization_A01.golden.0.ser
+
+          .put("lang.ArrayStoreException.serialization.ArrayStoreException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ArrayStoreException/serialization/ArrayStoreException_serialization_A01.golden.0.ser
+
+          .put("lang.UnknownError.serialization.UnknownError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnknownError/serialization/UnknownError_serialization_A01.golden.0.ser
+
+          .put("lang.Boolean.serialization.Boolean_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Boolean/serialization/Boolean_serialization_A01.golden.0.ser
+
+          .put("lang.Integer.valueOfLjava_lang_StringI.Integer_valueOf_A02", any())
+          // 1) t07
+          // java.lang.AssertionError: NumberFormatException expected for input: +1 and radix: 10
+
+          .put("lang.Integer.serialization.Integer_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Integer/serialization/Integer_serialization_A01.golden.0.ser
+
+          .put("lang.Integer.parseIntLjava_lang_String.Integer_parseInt_A02", any())
+          // 1) t06
+          // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+          .put("lang.Integer.getIntegerLjava_lang_StringI.Integer_getInteger_A02", any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<6031769> but was:<1>
+
+          .put("lang.Integer.valueOfLjava_lang_String.Integer_valueOf_A02", any())
+          // 1) t07
+          // java.lang.AssertionError: NumberFormatException expected for input: +1
+
+          .put("lang.Integer.decodeLjava_lang_String.Integer_decode_A04", any())
+          // 1) t06
+          // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+          .put("lang.Integer.parseIntLjava_lang_StringI.Integer_parseInt_A02", any())
+          // 1) t06
+          // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+          .put("lang.Integer.getIntegerLjava_lang_StringLjava_lang_Integer.Integer_getInteger_A02",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<6031769> but was:<1>
+
+          .put("lang.Integer.ConstructorLjava_lang_String.Integer_Constructor_A02", any())
+          // 1) t06
+          // java.lang.AssertionError: Expected exception: java.lang.NumberFormatException
+
+          .put("lang.Integer.getIntegerLjava_lang_String.Integer_getInteger_A02", any())
+          // 1) t03
+          // java.lang.AssertionError: expected null, but was:<1>
+
+          .put("lang.ref.PhantomReference.isEnqueued.PhantomReference_isEnqueued_A01", any())
+          // 1) t04
+          // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+          .put("lang.ref.SoftReference.isEnqueued.SoftReference_isEnqueued_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+          .put("lang.ref.SoftReference.get.SoftReference_get_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: expected null, but was:<[I@e2603b4>
+
+          .put("lang.ref.ReferenceQueue.poll.ReferenceQueue_poll_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+          .put("lang.StackTraceElement.serialization.StackTraceElement_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StackTraceElement/serialization/StackTraceElement_serialization_A01.golden.0.ser
+
+          .put("lang.ref.WeakReference.get.WeakReference_get_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: expected null, but was:<[I@1b32f32>
+
+          .put("lang.ref.WeakReference.isEnqueued.WeakReference_isEnqueued_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: reference is not enqueued after 2 sec
+
+          .put("lang.StackTraceElement.toString.StackTraceElement_toString_A01",
+              match(runtimes(DexVm.ART_DEFAULT)))
+          // 1) t03
+          // org.junit.ComparisonFailure: expected:<...ethod(Unknown Source[])> but was:<...ethod(Unknown Source[:1])>
+
+          .put("lang.NullPointerException.serialization.NullPointerException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/NullPointerException/serialization/NullPointerException_serialization_A01.golden.0.ser
+
+          .put("lang.VirtualMachineError.serialization.VirtualMachineError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/VirtualMachineError/serialization/VirtualMachineError_serialization_A01.golden.0.ser
+
+          .put("lang.ClassCircularityError.serialization.ClassCircularityError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassCircularityError/serialization/ClassCircularityError_serialization_A01.golden.0.ser
+
+          .put("lang.ThreadDeath.serialization.ThreadDeath_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ThreadDeath/serialization/ThreadDeath_serialization_A01.golden.0.ser
+
+          .put("lang.InstantiationError.serialization.InstantiationError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InstantiationError/serialization/InstantiationError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.IllegalThreadStateException.serialization.IllegalThreadStateException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalThreadStateException/serialization/IllegalThreadStateException_serialization_A01.golden.0.ser
+
+          .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A05", any())
+          // 1) t01
+          // java.lang.AssertionError: Input Stream should not be empty
+
+          .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A06", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ProcessBuilder.start.ProcessBuilder_start_A05", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ProcessBuilder.start.ProcessBuilder_start_A06", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ClassFormatError.serialization.ClassFormatError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassFormatError/serialization/ClassFormatError_serialization_A01.golden.0.ser
+
+          .put("lang.Math.cbrtD.Math_cbrt_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0, DexVm.ART_6_0_1)))
+          // 1) t01
+          // java.lang.AssertionError: cbrt(27.) expected:<3.0> but was:<3.0000000000000004>
+
+          .put("lang.Math.powDD.Math_pow_A08", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+          // 2) t02
+          // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+          // 3) t03
+          // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+          // 4) t04
+          // java.lang.AssertionError: expected:<NaN> but was:<1.0>
+
+          .put(
+              "lang.IncompatibleClassChangeError.serialization.IncompatibleClassChangeError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IncompatibleClassChangeError/serialization/IncompatibleClassChangeError_serialization_A01.golden.0.ser
+
+          .put("lang.Float.serialization.Float_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Float/serialization/Float_serialization_A01.golden.0.ser
+
+          .put("lang.Float.toStringF.Float_toString_A02", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
+          // 2) t04
+          // org.junit.ComparisonFailure: expected:<0.001[0]> but was:<0.001[]>
+
+          .put("lang.Short.valueOfLjava_lang_StringI.Short_valueOf_A02", any())
+          // 1) t03
+          // java.lang.AssertionError: Missing NumberFormatException for radix=10
+
+          .put("lang.Short.valueOfLjava_lang_String.Short_valueOf_A02", any())
+          // 1) t03
+          // java.lang.AssertionError: Missing NumberFormatException for arg="+1"
+
+          .put("lang.Short.serialization.Short_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Short/serialization/Short_serialization_A01.golden.0.ser
+
+          .put("lang.Short.parseShortLjava_lang_String.Short_parseShort_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Parsed Short instance from string:+1
+
+          .put("lang.Short.decodeLjava_lang_String.Short_decode_A04", any())
+          // 1) t01
+          // java.lang.AssertionError: Decoded Short instance from string:+1
+
+          .put("lang.Short.ConstructorLjava_lang_String.Short_Constructor_A02", any())
+          // 1) t02
+          // java.lang.AssertionError: Created Short instance from string:+1
+
+          .put("lang.ClassNotFoundException.serialization.ClassNotFoundException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/ClassNotFoundException/serialization/ClassNotFoundException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.annotation.AnnotationFormatError.serialization.AnnotationFormatError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/AnnotationFormatError/serialization/AnnotationFormatError_serialization_A01.golden.0.ser
+
+          .put("lang.Short.parseShortLjava_lang_StringI.Short_parseShort_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Parsed Short instance from string:+1 radix:10
+
+          .put(
+              "lang.annotation.IncompleteAnnotationException.ConstructorLjava_lang_ClassLjava_lang_String.IncompleteAnnotationException_Constructor_A01",
+              match(runtimes(DexVm.ART_DEFAULT)))
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
+          // 2) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.String.toString()' on a null object reference
+
+          .put("lang.InterruptedException.serialization.InterruptedException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/InterruptedException/serialization/InterruptedException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.annotation.IncompleteAnnotationException.Class.IncompleteAnnotationException_class_A01",
+              any())
+          // 1) t01
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.IncompleteAnnotationException.Class.IncompleteAnnotationClass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.IncompleteAnnotationException.Class.IncompleteAnnotationClass" on path: DexPathList[[dex file "/tmp/junit6988968562481945570/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.annotation.Annotation.Class.Annotation_class_A03", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.annotation.Annotation.serialization.Annotation_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/Annotation/serialization/Annotation_serialization_A01.golden.0.ser
+
+          .put("lang.annotation.Annotation.annotationType.Annotation_annotationType_A01", any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t03
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t04
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t05
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t06
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/annotation/Annotation/annotationType/Mark;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.annotationType.Mark" on path: DexPathList[[dex file "/tmp/junit2356208730386617024/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.annotation.IncompleteAnnotationException.serialization.IncompleteAnnotationException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/annotation/IncompleteAnnotationException/serialization/IncompleteAnnotationException_serialization_A01.golden.0.ser
+
+          .put("lang.annotation.Annotation.Class.Annotation_class_A02", any())
+          // 1) t04
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassValue
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t05
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassArrayValue
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingClassArrayValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t06
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumValue
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 4) t07
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumArrayValue
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.Annotation.Class.AnnotationMissingEnumArrayValue" on path: DexPathList[[dex file "/tmp/junit5702619070125761074/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 5) t08
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 6) t09
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.annotation.Retention.Retention_class_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeMismatchException_class_A01",
+              any())
+          // 1) t01
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeMismatchClass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeMismatchClass" on path: DexPathList[[dex file "/tmp/junit7410548264446836680/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeArrayMismatchClass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.annotation.AnnotationTypeMismatchException.Class.AnnotationTypeArrayMismatchClass" on path: DexPathList[[dex file "/tmp/junit7410548264446836680/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.Long.serialization.Long_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Long/serialization/Long_serialization_A01.golden.0.ser
+
+          .put("lang.ThreadGroup.resume.ThreadGroup_resume_A01", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.AbstractMethodError.serialization.AbstractMethodError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/AbstractMethodError/serialization/AbstractMethodError_serialization_A01.golden.0.ser
+
+          .put("lang.RuntimeException.serialization.RuntimeException_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/RuntimeException/serialization/RuntimeException_serialization_A01.golden.0.ser
+
+          .put("lang.ThreadGroup.suspend.ThreadGroup_suspend_A01", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+
+          .put(
+              "lang.ThreadGroup.ConstructorLjava_lang_ThreadGroupLjava_lang_String.ThreadGroup_Constructor_A03",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ThreadGroup.stop.ThreadGroup_stop_A01", any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t02
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.ThreadGroup.enumerate$Thread.ThreadGroup_enumerate_A01", any())
+          // 1) t05
+          // java.lang.UnsupportedOperationException
+
+          .put(
+              "lang.ThreadGroup.ConstructorLjava_lang_ThreadGroupLjava_lang_String.ThreadGroup_Constructor_A04",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ThreadGroup.parentOfLjava_lang_ThreadGroup.ThreadGroup_parentOf_A01", any())
+          // 1) t05
+          // java.lang.SecurityException
+
+          .put("lang.ThreadGroup.getMaxPriority.ThreadGroup_getMaxPriority_A02", any())
+          // 1) t02
+          // java.lang.AssertionError: expected:<1> but was:<5>
+
+          .put("lang.ThreadGroup.checkAccess.ThreadGroup_checkAccess_A03", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ThreadGroup.enumerate$ThreadZ.ThreadGroup_enumerate_A01", any())
+          // 1) t06
+          // java.lang.UnsupportedOperationException
+
+          .put(
+              "lang.ThreadGroup.uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable.ThreadGroup_uncaughtException_A01",
+              any())
+          // 1) t01
+          // java.lang.UnsupportedOperationException
+          // 2) t01
+          // java.lang.IllegalThreadStateException
+
+          .put("lang.ThreadGroup.checkAccess.ThreadGroup_checkAccess_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.ThreadGroup.ConstructorLjava_lang_String.ThreadGroup_Constructor_A04", any())
+          // 1) t01
+          // java.lang.AssertionError: test failed with error:java.lang.SecurityException
+
+          .put("lang.ThreadGroup.activeCount.ThreadGroup_activeCount_A01", any())
+          // 1) t04
+          // java.lang.UnsupportedOperationException
+
+          .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Maximum priority should not be changed. expected same:<10> was not:<1>
+
+          .put("lang.ThreadGroup.ConstructorLjava_lang_String.ThreadGroup_Constructor_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: test failed with error:java.lang.SecurityException
+
+          .put("lang.ThreadGroup.getParent.ThreadGroup_getParent_A03", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.Class.getDeclaredConstructors.Class_getDeclaredConstructors_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.AssertionError.serialization.AssertionError_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/AssertionError/serialization/AssertionError_serialization_A01.golden.0.ser
+
+          .put("lang.Class.getClassLoader.Class_getClassLoader_A01", any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t03
+          // java.lang.AssertionError: expected null, but was:<java.lang.BootClassLoader@d17167d>
+
+          .put("lang.Class.getDeclaringClass.Class_getDeclaringClass_A01", any())
+          // 1) t04
+          // java.lang.AssertionError: expected null, but was:<class com.google.jctf.test.lib.java.lang.Class.getDeclaringClass.Class_getDeclaringClass_A01>
+
+          .put("lang.Class.getDeclaredFields.Class_getDeclaredFields_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: array lengths differed, expected.length=0 actual.length=2
+
+          .put("lang.Class.getClassLoader.Class_getClassLoader_A02", any())
+          // 1) t02
+          // java.lang.AssertionError: ClassLoader of int[] expected null, but was:<java.lang.BootClassLoader@34f2660>
+
+          .put("lang.Class.getClassLoader.Class_getClassLoader_A03", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.SecurityException
+
+          .put("lang.Class.getDeclaredFields.Class_getDeclaredFields_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getResourceLjava_lang_String.Class_getResource_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+          // 4) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+          // 5) t06
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getPath()' on a null object reference
+          // 6) t07
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A03", any())
+          // 1) t03
+          // java.lang.AssertionError: Vague error message
+
+          .put("lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.LinkageError
+
+          .put("lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A07",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+
+          .put("lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A01",
+              any())
+          // 1) t05
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A01$TestFixture
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t06
+          // java.lang.ClassNotFoundException: [[[Lcom.google.jctf.test.lib.java.lang.Class.forNameLjava_lang_StringZLjava_lang_ClassLoader.Class_forName_A01$TestFixture;
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.Class.getConstructor$Ljava_lang_Class.Class_getConstructor_A04", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.serialization.Class_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Class/serialization/Class_serialization_A01-Object.golden.ser
+
+          .put("lang.Class.getMethods.Class_getMethods_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put(
+              "lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A05",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getClasses.Class_getClasses_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put(
+              "lang.Class.getDeclaredMethodLjava_lang_String$Ljava_lang_Class.Class_getDeclaredMethod_A03",
+              any())
+          // 1) t05
+          // java.lang.AssertionError: Vague error message
+
+          .put("lang.Class.getClasses.Class_getClasses_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t03
+          // java.lang.AssertionError: Array lengths expected:<2> but was:<3>
+
+          .put("lang.Class.getProtectionDomain.Class_getProtectionDomain_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: unexpected null
+          // 2) t02
+          // java.lang.AssertionError
+
+          .put("lang.Class.getProtectionDomain.Class_getProtectionDomain_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t03
+          // java.lang.AssertionError: Array lengths expected:<1> but was:<3>
+
+          .put("lang.Class.getMethods.Class_getMethods_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t03
+          // java.lang.AssertionError: Array lengths expected:<1> but was:<3>
+
+          .put("lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A04", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.BadSignatureClass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.BadSignatureClass" on path: DexPathList[[dex file "/tmp/junit3946430209802684584/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.Class.getDeclaredFieldLjava_lang_String.Class_getDeclaredField_A04", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getDeclaredMethods.Class_getDeclaredMethods_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getResourceAsStreamLjava_lang_String.Class_getResourceAsStream_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+          // 4) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.InputStream.close()' on a null object reference
+          // 5) t06
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 6) t07
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface" on path: DexPathList[[dex file "/tmp/junit4997784696243614791/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass" on path: DexPathList[[dex file "/tmp/junit4997784696243614791/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.Class.getAnnotationLjava_lang_Class.Class_getAnnotation_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put("lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Should throw TypeNotPresentException for class: interface com.google.jctf.test.lib.java.lang.Class.getGenericInterfaces.Class_getGenericInterfaces_A03$I01
+
+          .put("lang.Class.getDeclaredClasses.Class_getDeclaredClasses_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.desiredAssertionStatus.Class_desiredAssertionStatus_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 3) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 4) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 5) t05
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 6) t06
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 7) t07
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 8) t08
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.Class.getPackage.Class_getPackage_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected same:<package java.lang, Unknown, version 0.0> was not:<package java.lang, Unknown, version 0.0>
+          // 2) t03
+          // java.lang.AssertionError: expected null, but was:<package [Ljava.lang, Unknown, version 0.0>
+
+          .put("lang.Class.getFieldLjava_lang_String.Class_getField_A04", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getTypeParameters.Class_getTypeParameters_A02", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.BadSignatureClass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.BadSignatureClass" on path: DexPathList[[dex file "/tmp/junit8470679547599572122/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.Class.getDeclaredAnnotations.Class_getDeclaredAnnotations_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Class.getConstructors.Class_getConstructors_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.isAnnotationPresentLjava_lang_Class.Class_isAnnotationPresent_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t04
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put("lang.Class.getFields.Class_getFields_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A03", any())
+          // 1) t01
+          // java.lang.AssertionError: Should throw TypeNotPresentException for class: class com.google.jctf.test.lib.java.lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A03$C01
+
+          .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A04", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperclass" on path: DexPathList[[dex file "/tmp/junit552939904349045383/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.MalformedSuperinterface" on path: DexPathList[[dex file "/tmp/junit552939904349045383/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.Class.getSigners.Class_getSigners_A01", any())
+          // 1) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
+          // 2) t04
+          // java.lang.AssertionError: Unable to configure default providers
+          // 3) t05
+          // java.lang.NoClassDefFoundError: sun.security.jca.Providers
+          // Caused by: java.lang.AssertionError: Unable to configure default providers
+
+          .put("lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) t04
+          // java.lang.AssertionError: expected:<interface com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01$I1> but was:<interface com.google.jctf.test.lib.java.lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A01$I2>
+
+          .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: expected same:<class java.lang.reflect.AccessibleObject> was not:<class java.lang.reflect.Executable>
+
+          .put("lang.Class.getGenericSuperclass.Class_getGenericSuperclass_A02", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.Class.BadSignatureClass
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.Class.BadSignatureClass" on path: DexPathList[[dex file "/tmp/junit6101943207514034648/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.Class.newInstance.Class_newInstance_A07", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put(
+              "lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A02",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: Vague error message
+
+          .put("lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A05", any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.forNameLjava_lang_String.Class_forName_A01", any())
+          // 1) t05
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t06
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.Class.getDeclaredConstructor$Ljava_lang_Class.Class_getDeclaredConstructor_A03",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+          // 2) t03
+          // java.lang.SecurityException
+          // 3) t04
+          // java.lang.SecurityException
+
+          .put("lang.Class.getMethodLjava_lang_String$Ljava_lang_Class.Class_getMethod_A03", any())
+          // 1) t03
+          // java.lang.AssertionError: Vague error message
+
+          .put("lang.Class.forNameLjava_lang_String.Class_forName_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.LinkageError
+
+          .put("lang.UnsatisfiedLinkError.serialization.UnsatisfiedLinkError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnsatisfiedLinkError/serialization/UnsatisfiedLinkError_serialization_A01.golden.0.ser
+
+          .put("lang.Class.getAnnotations.Class_getAnnotations_A01", any())
+          // 1) t04
+          // java.lang.AssertionError: expected:<0> but was:<1>
+          // 2) t06
+          // java.lang.AssertionError: Misconfigured test
+
+          .put(
+              "lang.EnumConstantNotPresentException.serialization.EnumConstantNotPresentException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/EnumConstantNotPresentException/serialization/EnumConstantNotPresentException_serialization_A01.golden.0.ser
+
+          .put("lang.String.toLowerCase.String_toLowerCase_A01", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<i[]> but was:<i[̇]>
+
+          .put("lang.String.splitLjava_lang_StringI.String_split_A01", any())
+          // 1) t06
+          // java.lang.AssertionError: array lengths differed, expected.length=1 actual.length=2
+
+          .put("lang.String.serialization.String_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/String/serialization/String_serialization_A01.golden.0.ser
+
+          .put("lang.String.regionMatchesZILjava_lang_StringII.String_regionMatches_A01", any())
+          // 1) t07
+          // java.lang.AssertionError
+
+          .put("lang.String.valueOfF.String_valueOf_A01", any())
+          // 1) t09
+          // org.junit.ComparisonFailure: Incorrect double string returned expected:<0.001[0]> but was:<0.001[]>
+
+          .put("lang.String.Constructor$BLjava_nio_charset_Charset.String_Constructor_A01", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<�[]> but was:<�[�]>
+          // 2) t03
+          // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+          // 3) t04
+          // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+          // 4) t05
+          // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+
+          .put("lang.String.concatLjava_lang_String.String_concat_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: expected not same
+
+          .put("lang.String.matchesLjava_lang_String.String_matches_A01", any())
+          // 1) t15
+          // java.lang.AssertionError: pattern: [^a-d[^m-p]]*abb input: admpabb
+          // 2) t19
+          // java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 11
+          // .*(?<=abc)*\.log$
+          // ^
+
+          .put("lang.String.CASE_INSENSITIVE_ORDER.serialization.String_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/String/CASE_INSENSITIVE_ORDER/serialization/String_serialization_A01.golden.0.ser
+
+          .put("lang.String.getBytesLjava_lang_String.String_getBytes_A14", any())
+          // 1) t07
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 2) t12
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 3) t22
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 4) t32
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 5) t42
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 6) t52
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 7) t62
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+
+          .put("lang.String.splitLjava_lang_String.String_split_A01", any())
+          // 1) t06
+          // java.lang.AssertionError: array lengths differed, expected.length=1 actual.length=2
+
+          .put("lang.String.getBytesII$BI.String_getBytes_A03", any())
+          // 1) t04
+          // java.lang.AssertionError: Should throws IndexOutOfBoundsException: 0
+          // 2) t05
+          // java.lang.AssertionError: Should throws IndexOutOfBoundsException: 0
+
+          .put("lang.String.getBytesII$BI.String_getBytes_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.String.toLowerCaseLjava_util_Locale.String_toLowerCase_A01", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<i[]> but was:<i[̇]>
+
+          .put("lang.String.Constructor$BIILjava_nio_charset_Charset.String_Constructor_A01", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<�[]> but was:<�[�]>
+          // 2) t03
+          // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+          // 3) t04
+          // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+          // 4) t05
+          // org.junit.ComparisonFailure: expected:<[�]> but was:<[�]>
+
+          .put("lang.String.getBytesLjava_nio_charset_Charset.String_getBytes_A01", any())
+          // 1) t05
+          // arrays first differed at element [0]; expected:<-40> but was:<-37>
+          // Caused by: java.lang.AssertionError: expected:<-40> but was:<-37>
+
+          .put("lang.String.valueOfD.String_valueOf_A01", any())
+          // 1) t09
+          // org.junit.ComparisonFailure: Incorrect double string returned expected:<0.001[0]> but was:<0.001[]>
+
+          .put("lang.String.getBytesLjava_nio_charset_Charset.String_getBytes_A14", any())
+          // 1) t07
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 2) t12
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 3) t22
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 4) t32
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 5) t42
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 6) t52
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+          // 7) t62
+          // arrays first differed at element [0]; expected:<-2> but was:<-1>
+          // Caused by: java.lang.AssertionError: expected:<-2> but was:<-1>
+
+          .put("lang.Package.isSealed.Package_isSealed_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getSpecificationVersion.Package_getSpecificationVersion_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getAnnotationLjava_lang_Class.Package_getAnnotation_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 3) t05
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.isAnnotationPresentLjava_lang_Class.Package_isAnnotationPresent_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) testIsAnnotationPresent_Null2
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getName.Package_getName_A01", any())
+          // 1) t03
+          // java.lang.ClassNotFoundException: com.simpleClass1
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getImplementationVersion.Package_getImplementationVersion_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getDeclaredAnnotations.Package_getDeclaredAnnotations_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+          // 2) t03
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.Package.getSpecificationVendor.Package_getSpecificationVendor_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getAnnotationLjava_lang_Class.Package_getAnnotation_A02",
+              match(runtimes(DexVm.ART_DEFAULT, DexVm.ART_7_0_0)))
+          // 1) testGetAnnotation_Null2
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.isCompatibleWithLjava_lang_String.Package_isCompatibleWith_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.toString.Package_toString_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expect: package com.google.jctf.test.lib.java.lang.Package.toString, Unknown, version 0.0, actual: package com.google.jctf.test.lib.java.lang.Package.toString
+          // 2) t03
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getAnnotations.Package_getAnnotations_A01", any())
+          // 1) t03
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+          // 2) t04
+          // java.lang.AssertionError: expected:<0> but was:<1>
+          // 3) t06
+          // java.lang.AssertionError: Misconfigured test
+
+          .put("lang.Package.isAnnotationPresentLjava_lang_Class.Package_isAnnotationPresent_A01",
+              any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 3) t04
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put("lang.Package.getSpecificationTitle.Package_getSpecificationTitle_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getImplementationTitle.Package_getImplementationTitle_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getPackages.Package_getPackages_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Package getPackages failed to retrieve a packages
+
+          .put("lang.Package.hashCode.Package_hashCode_A01", any())
+          // 1) t03
+          // java.lang.ClassNotFoundException: com.simpleClass1
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.getPackageLjava_lang_String.Package_getPackage_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Package getPackage failed for java.lang expected same:<package java.lang, Unknown, version 0.0> was not:<package java.lang, Unknown, version 0.0>
+
+          .put("lang.Package.getImplementationVendor.Package_getImplementationVendor_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.Package.isSealedLjava_net_URL.Package_isSealed_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.simpleClass
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.net.URL.getProtocol()' on a null object reference
+
+          .put("lang.StringBuilder.serialization.StringBuilder_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/StringBuilder/serialization/StringBuilder_serialization_A01.golden.ser
+
+          .put(
+              "lang.SecurityManager.checkReadLjava_io_FileDescriptor.SecurityManager_checkRead_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkAwtEventQueueAccess.SecurityManager_checkAwtEventQueueAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit5851789677967468571/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit5851789677967468571/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.SecurityManager.checkWriteLjava_lang_String.SecurityManager_checkWrite_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.inClassLoader.SecurityManager_inClassLoader_A01", any())
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put(
+              "lang.SecurityManager.checkPermissionLjava_security_PermissionLjava_lang_Object.SecurityManager_checkPermission_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkReadLjava_io_FileDescriptor.SecurityManager_checkRead_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@cf3e8f1>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.inCheck.SecurityManager_inCheck_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: inCheck field must always remain false
+          // 2) t03
+          // java.lang.AssertionError: inCheck field must always remain false
+
+          .put("lang.SecurityManager.currentClassLoader.SecurityManager_currentClassLoader_A02",
+              any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.SecurityManager.checkPrintJobAccess.SecurityManager_checkPrintJobAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@a49048c>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkWriteLjava_lang_String.SecurityManager_checkWrite_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.io.FilePermission@9656315>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkPackageAccessLjava_lang_String.SecurityManager_checkPackageAccess_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkAcceptLjava_lang_StringI.SecurityManager_checkAccept_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPermissionLjava_security_PermissionLjava_lang_Object.SecurityManager_checkPermission_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <com.google.jctf.test.lib.java.lang.SecurityManager.checkPermissionLjava_security_PermissionLjava_lang_Object.SecurityManager_checkPermission_A01$1@5d6dd39>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+          // 3) t03
+          // java.lang.AssertionError: SecurityException should be thrown for null context
+
+          .put("lang.SecurityManager.currentClassLoader.SecurityManager_currentClassLoader_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.SecurityManager.checkMulticastLjava_net_InetAddress.SecurityManager_checkMulticast_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkListenI.SecurityManager_checkListen_A01", any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@32bf8d4>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.getSecurityContext.SecurityManager_getSecurityContext_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<java.security.AccessControlContext@248575d> but was:<null>
+          // 2) t02
+          // java.lang.AssertionError: expected:<java.security.AccessControlContext@259b8d2> but was:<null>
+
+          .put(
+              "lang.SecurityManager.checkPackageAccessLjava_lang_String.SecurityManager_checkPackageAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@3230dd4>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@a1bab7d>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@bb64a72>
+          // but:
+          // 4) t05
+          // java.lang.AssertionError: SecurityException should be thrown for restricted package: 1234
+
+          .put(
+              "lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@81146f>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkMulticastLjava_net_InetAddressB.SecurityManager_checkMulticast_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkAcceptLjava_lang_StringI.SecurityManager_checkAccept_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@eb9d181>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@db9b226>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkMulticastLjava_net_InetAddress.SecurityManager_checkMulticast_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@dd6300a>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@891b07b>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.Constructor.SecurityManager_Constructor_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.SecurityManager.getClassContext.SecurityManager_getClassContext_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to read from null array
+
+          .put(
+              "lang.SecurityManager.checkMemberAccessLjava_lang_ClassI.SecurityManager_checkMemberAccess_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkDeleteLjava_lang_String.SecurityManager_checkDelete_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.io.FilePermission@96408b7>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkReadLjava_lang_StringLjava_lang_Object.SecurityManager_checkRead_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkMulticastLjava_net_InetAddressB.SecurityManager_checkMulticast_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@79cc5c9>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@479a4ce>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkListenI.SecurityManager_checkListen_A02", any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@6b92452>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@5d582db>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: SecurityException should be thrown
+
+          .put(
+              "lang.SecurityManager.checkWriteLjava_io_FileDescriptor.SecurityManager_checkWrite_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkDeleteLjava_lang_String.SecurityManager_checkDelete_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPropertiesAccess.SecurityManager_checkPropertiesAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.util.PropertyPermission@32a9e76>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkReadLjava_lang_StringLjava_lang_Object.SecurityManager_checkRead_A02",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: SecurityException should be thrown for null context
+
+          .put(
+              "lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkAccessLjava_lang_Thread.SecurityManager_checkAccess_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPackageDefinitionLjava_lang_String.SecurityManager_checkPackageDefinition_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkReadLjava_lang_String.SecurityManager_checkRead_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkWriteLjava_io_FileDescriptor.SecurityManager_checkWrite_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@cf13435>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkReadLjava_lang_StringLjava_lang_Object.SecurityManager_checkRead_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.io.FilePermission@c0d92be>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkExecLjava_lang_String.SecurityManager_checkExec_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPackageDefinitionLjava_lang_String.SecurityManager_checkPackageDefinition_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@8bc5330>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@a83ba9>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@785152e>
+          // but:
+          // 4) t05
+          // java.lang.AssertionError: SecurityException should be thrown for restricted package: 1234
+
+          .put("lang.SecurityManager.checkExecLjava_lang_String.SecurityManager_checkExec_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.io.FilePermission@79b6b6b>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkCreateClassLoader.SecurityManager_checkCreateClassLoader_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@6b7c9f4>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkReadLjava_lang_String.SecurityManager_checkRead_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.io.FilePermission@5d4287d>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkAccessLjava_lang_ThreadGroup.SecurityManager_checkAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@4f08706>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A03", any())
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A03",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: SecurityException should be thrown for null context
+
+          .put("lang.SecurityManager.checkExecLjava_lang_String.SecurityManager_checkExec_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.io.FilePermission@162012a>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkSetFactory.SecurityManager_checkSetFactory_A01", any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@162012a>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A04",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <com.google.jctf.test.lib.java.lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A01$2@f9abe3c>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Method from class: com.google.jctf.test.lib.java.lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A01 must be on execution stack.
+
+          .put("lang.SecurityManager.inClassLjava_lang_String.SecurityManager_inClass_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPropertyAccessLjava_lang_String.SecurityManager_checkPropertyAccess_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.checkExitI.SecurityManager_checkExit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@c0c3860>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@b2896e9>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@f796f6e>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringILjava_lang_Object.SecurityManager_checkConnect_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@96153fb>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@5296c18>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A02", any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.SecurityManager.classDepthLjava_lang_String.SecurityManager_classDepth_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkPropertyAccessLjava_lang_String.SecurityManager_checkPropertyAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.util.PropertyPermission@79a110d>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkPropertyAccessLjava_lang_String.SecurityManager_checkPropertyAccess_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringI.SecurityManager_checkConnect_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@4ef2ca8>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@b6163c1>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.SecurityManager.checkLinkLjava_lang_String.SecurityManager_checkLink_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.classLoaderDepth.SecurityManager_classLoaderDepth_A01", any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.SecurityManager.checkPermissionLjava_security_Permission.SecurityManager_checkPermission_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.currentLoadedClass.SecurityManager_currentLoadedClass_A01",
+              any())
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.SecurityManager.checkSecurityAccessLjava_lang_String.SecurityManager_checkSecurityAccess_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringI.SecurityManager_checkConnect_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.SecurityManager.checkConnectLjava_lang_StringI.SecurityManager_checkConnect_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@f9963de>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.net.SocketPermission@c7159bf>
+          // but:
+          // 3) t03
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put(
+              "lang.SecurityManager.checkTopLevelWindowLjava_lang_Object.SecurityManager_checkTopLevelWindow_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.SecurityManager.currentLoadedClass.SecurityManager_currentLoadedClass_A02",
+              any())
+          // 1) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.SecurityManager.classDepthLjava_lang_String.SecurityManager_classDepth_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<1> but was:<-1>
+
+          .put(
+              "lang.SecurityManager.checkSecurityAccessLjava_lang_String.SecurityManager_checkSecurityAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.security.SecurityPermission@a439b14>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.Throwable.serialization.Throwable_serialization_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Throwable/serialization/Throwable_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.SecurityManager.checkTopLevelWindowLjava_lang_Object.SecurityManager_checkTopLevelWindow_A01",
+              any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit9085263025235375443/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit9085263025235375443/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.SecurityManager.checkLinkLjava_lang_String.SecurityManager_checkLink_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError:
+          // Expected: a collection containing <java.lang.RuntimePermission@87c5826>
+          // but:
+          // 2) t02
+          // java.lang.AssertionError: Expected exception: java.lang.SecurityException
+
+          .put("lang.Throwable.getStackTrace.Throwable_getStackTrace_A01", any())
+          // 1) t04
+          // org.junit.ComparisonFailure: wrongly omit constructor frame for other exception expected:<[<init>]> but was:<[t04]>
+
+          .put(
+              "lang.SecurityManager.checkSystemClipboardAccess.SecurityManager_checkSystemClipboardAccess_A01",
+              any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit529120552959989860/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.SecurityException> but was<java.lang.NoClassDefFoundError>
+          // Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljava/awt/AWTPermission;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "java.awt.AWTPermission" on path: DexPathList[[dex file "/tmp/junit529120552959989860/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.SecurityManager.checkSecurityAccessLjava_lang_String.SecurityManager_checkSecurityAccess_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.reflect.ReflectPermission.Constructor_java_lang_String.ReflectPermission_Constructor_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put(
+              "lang.reflect.MalformedParameterizedTypeException.serialization.MalformedParameterizedTypeException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/MalformedParameterizedTypeException/serialization/MalformedParameterizedTypeException_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.reflect.ReflectPermission.Constructor_java_lang_StringLjava_lang_String.ReflectPermission_Constructor_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put(
+              "lang.UnsupportedClassVersionError.serialization.UnsupportedClassVersionError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/UnsupportedClassVersionError/serialization/UnsupportedClassVersionError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.reflect.ReflectPermission.Constructor_java_lang_String.ReflectPermission_Constructor_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Incorrect permission constructed
+          // 2) t02
+          // java.lang.AssertionError: expected:<a> but was:<null>
+          // 3) t03
+          // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+          .put("lang.reflect.ReflectPermission.Class.ReflectPermission_class_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.reflect.Proxy.serialization.Proxy_serialization_A01", any())
+          // 1) t01
+          // java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
+          // Caused by: java.lang.reflect.InvocationTargetException
+          // Caused by: java.lang.NullPointerException
+          // 2) t02
+          // java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
+          // Caused by: java.lang.reflect.InvocationTargetException
+          // Caused by: java.lang.NullPointerException
+
+          .put(
+              "lang.reflect.ReflectPermission.Constructor_java_lang_StringLjava_lang_String.ReflectPermission_Constructor_A03",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put(
+              "lang.reflect.ReflectPermission.Constructor_java_lang_String.ReflectPermission_Constructor_A02",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.NullPointerException
+
+          .put("lang.reflect.ReflectPermission.Class.ReflectPermission_class_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "lang.reflect.ReflectPermission.Constructor_java_lang_StringLjava_lang_String.ReflectPermission_Constructor_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Incorrect permission constructed
+          // 2) t04
+          // java.lang.AssertionError: expected:<a> but was:<null>
+          // 3) t05
+          // java.lang.AssertionError: expected:<2/3/2> but was:<null>
+
+          .put(
+              "lang.reflect.Proxy.getInvocationHandlerLjava_lang_Object.Proxy_getInvocationHandler_A02",
+              any())
+          // 1) t02
+          // java.lang.Exception: Unexpected exception, expected<java.lang.IllegalArgumentException> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException
+
+          .put("lang.reflect.Proxy.Class.Proxy_class_A01", any())
+          // 1) t04
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t05
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.reflect.Proxy.getProxyClassLjava_lang_ClassLoader$Ljava_lang_Class.Proxy_getProxyClass_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: expected same:<null> was not:<java.lang.BootClassLoader@ecc20b9>
+          // 2) t04
+          // java.lang.AssertionError: expected same:<null> was not:<java.lang.BootClassLoader@ecc20b9>
+
+          .put(
+              "lang.reflect.Proxy.getProxyClassLjava_lang_ClassLoader$Ljava_lang_Class.Proxy_getProxyClass_A03",
+              any())
+          // 1) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t05
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Proxy.h.Proxy_h_A01", match(runtimes(DexVm.ART_DEFAULT)))
+          // 1) t01
+          // java.lang.reflect.InvocationTargetException
+          // Caused by: java.lang.NullPointerException
+
+          .put("lang.reflect.Proxy.serialization.Proxy_serialization_A02", any())
+          // 1) t01
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/reflect/Proxy/serialization/Proxy_serialization_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Proxy.serialization.Proxy_serialization_A01" on path: DexPathList[[dex file "/tmp/junit3110030363172925878/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t02
+          // java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/jctf/test/lib/java/lang/reflect/Proxy/serialization/Proxy_serialization_A01;
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Proxy.serialization.Proxy_serialization_A01" on path: DexPathList[[dex file "/tmp/junit3110030363172925878/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 3) t03
+          // java.lang.AssertionError: Unable to configure default providers
+
+          .put(
+              "lang.reflect.GenericSignatureFormatError.serialization.GenericSignatureFormatError_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/GenericSignatureFormatError/serialization/GenericSignatureFormatError_serialization_A01.golden.0.ser
+
+          .put(
+              "lang.reflect.Proxy.newProxyInstanceLjava_lang_ClassLoader$Ljava_lang_ClassLjava_lang_reflect_InvocationHandler.Proxy_newProxyInstance_A02",
+              any())
+          // 1) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put(
+              "lang.reflect.Proxy.ConstructorLjava_lang_reflect_InvocationHandler.Proxy_Constructor_A01",
+              match(runtimes(DexVm.ART_DEFAULT)))
+          // 1) t01
+          // java.lang.NullPointerException
+
+          .put(
+              "lang.reflect.Proxy.newProxyInstanceLjava_lang_ClassLoader$Ljava_lang_ClassLjava_lang_reflect_InvocationHandler.Proxy_newProxyInstance_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Bad classloader expected:<null> but was:<java.lang.BootClassLoader@fda9ca7>
+
+          .put("lang.reflect.Modifier.isStrictI.Modifier_isStrict_A01", any())
+          // 1) t05
+          // java.lang.AssertionError
+
+          .put("lang.reflect.Method.getGenericReturnType.Method_getGenericReturnType_A03", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MissingReturnTypeMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingReturnTypeMethod" on path: DexPathList[[dex file "/tmp/junit7196800508165091862/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getGenericReturnType.Method_getGenericReturnType_A02", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit1047440880764311474/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getAnnotationLjava_lang_Class.Method_getAnnotation_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A02",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit2055893562046815723/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.isBridge.Method_isBridge_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BridgeTestMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BridgeTestMethod" on path: DexPathList[[dex file "/tmp/junit1244663784930832031/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.isSynthetic.Method_isSynthetic_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.SyntheticTestMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.SyntheticTestMethod" on path: DexPathList[[dex file "/tmp/junit5876026561576323251/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getGenericReturnType.Method_getGenericReturnType_A04", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MalformedReturnTypeMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MalformedReturnTypeMethod" on path: DexPathList[[dex file "/tmp/junit4310478819215974904/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01",
+              any())
+          // 1) t03
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Third
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Third" on path: DexPathList[[dex file "/tmp/junit8600081041276641493/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t04
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Fourth
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A01$Fourth" on path: DexPathList[[dex file "/tmp/junit8600081041276641493/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.invokeLjava_lang_Object$Ljava_lang_Object.Method_invoke_A07",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalAccessException
+
+          .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A04",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MalformedExceptionTypeMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MalformedExceptionTypeMethod" on path: DexPathList[[dex file "/tmp/junit1512043528789417983/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getTypeParameters.Method_getTypeParameters_A02", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit2853449662835100192/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getGenericExceptionTypes.Method_getGenericExceptionTypes_A03",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MissingExceptionTypeMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingExceptionTypeMethod" on path: DexPathList[[dex file "/tmp/junit1347702687417623444/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.getDeclaredAnnotations.Method_getDeclaredAnnotations_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.reflect.Method.getGenericParameterTypes.Method_getGenericParameterTypes_A04",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MalformedParameterTypeMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MalformedParameterTypeMethod" on path: DexPathList[[dex file "/tmp/junit2056931399679564203/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Method.toGenericString.Method_toGenericString_A01", any())
+          // 1) t03
+          // org.junit.ComparisonFailure: expected:<public static final [synchronized ]native void com.goog...> but was:<public static final []native void com.goog...>
+          // 2) t04
+          // org.junit.ComparisonFailure: expected:<..._toGenericString_A01[.com.google.jctf.test.lib.java.lang.reflect.Method.toGenericString.Method_toGenericString_A01]$GenericClass<java.l...> but was:<..._toGenericString_A01[]$GenericClass<java.l...>
+          // 3) t05
+          // org.junit.ComparisonFailure: expected:<..._toGenericString_A01[.com.google.jctf.test.lib.java.lang.reflect.Method.toGenericString.Method_toGenericString_A01]$GenericClass<java.l...> but was:<..._toGenericString_A01[]$GenericClass<java.l...>
+
+          .put("lang.reflect.Method.getGenericParameterTypes.Method_getGenericParameterTypes_A03",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.MissingParameterTypeMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.MissingParameterTypeMethod" on path: DexPathList[[dex file "/tmp/junit3534060116722105133/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.InvocationHandler.invokeLjava_lang_ObjectLjava_lang_reflect_Method$Ljava_lang_Object.InvocationHandler_invoke_A02",
+              any())
+          // 1) t04
+          // java.lang.AssertionError: ClassCastException should be thrown
+
+          .put("lang.reflect.Method.getDefaultValue.Method_getDefaultValue_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.TypeNotPresentException
+
+          .put("lang.reflect.Method.toString.Method_toString_A01", any())
+          // 1) t04
+          // org.junit.ComparisonFailure: expected:<public static final [synchronized ]native void com.goog...> but was:<public static final []native void com.goog...>
+
+          .put("lang.reflect.Method.getGenericParameterTypes.Method_getGenericParameterTypes_A02",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Method.BadSignatureMethod" on path: DexPathList[[dex file "/tmp/junit7973288126499824876/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Field.getFloatLjava_lang_Object.Field_getFloat_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getDeclaringClass.Field_getDeclaringClass_A01", any())
+          // 1) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getByteLjava_lang_Object.Field_getByte_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getCharLjava_lang_Object.Field_getChar_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getBooleanLjava_lang_Object.Field_getBoolean_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setByteLjava_lang_ObjectB.Field_setByte_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setByteLjava_lang_ObjectB.Field_setByte_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: bytePublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@b1b0f3d
+
+          .put("lang.reflect.Field.setBooleanLjava_lang_ObjectZ.Field_setBoolean_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: booleanPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@953cc4f
+
+          .put("lang.reflect.Field.setCharLjava_lang_ObjectC.Field_setChar_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.isSynthetic.Field_isSynthetic_A01", any())
+          // 1) t01
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestSyntheticField
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestSyntheticField" on path: DexPathList[[dex file "/tmp/junit8256784459468391222/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Field.setBooleanLjava_lang_ObjectZ.Field_setBoolean_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getType.Field_getType_A01", any())
+          // 1) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setCharLjava_lang_ObjectC.Field_setChar_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: charPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@95271f1
+
+          .put("lang.reflect.Field.getDoubleLjava_lang_Object.Field_getDouble_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setFloatLjava_lang_ObjectF.Field_setFloat_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: floatPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@3fca927
+
+          .put("lang.reflect.Field.getAnnotationLjava_lang_Class.Field_getAnnotation_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put("lang.reflect.Field.getIntLjava_lang_Object.Field_getInt_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setFloatLjava_lang_ObjectF.Field_setFloat_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getShortLjava_lang_Object.Field_getShort_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getGenericType.Field_getGenericType_A03", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestMissingTypeField
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestMissingTypeField" on path: DexPathList[[dex file "/tmp/junit1097443728550054537/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Field.getDeclaredAnnotations.Field_getDeclaredAnnotations_A01", any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put("lang.reflect.Field.getGenericType.Field_getGenericType_A01", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<...Field.TestOtherField[.com.google.jctf.test.lib.java.lang.reflect.Field.TestOtherField]$SomeClass<?>> but was:<...Field.TestOtherField[]$SomeClass<?>>
+          // 2) t03
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setIntLjava_lang_ObjectI.Field_setInt_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getGenericType.Field_getGenericType_A02", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestBadSignatureField
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestBadSignatureField" on path: DexPathList[[dex file "/tmp/junit8638189152422058286/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Field.toGenericString.Field_toGenericString_A01", any())
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<...Field.TestOtherField[.com.google.jctf.test.lib.java.lang.reflect.Field.TestOtherField]$SomeClass<?> com.go...> but was:<...Field.TestOtherField[]$SomeClass<?> com.go...>
+
+          .put("lang.reflect.Field.getGenericType.Field_getGenericType_A04", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Field.TestMalformedTypeField
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Field.TestMalformedTypeField" on path: DexPathList[[dex file "/tmp/junit1860681606366685174/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.Field.setIntLjava_lang_ObjectI.Field_setInt_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: intPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@94fbd35
+
+          .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setShortLjava_lang_ObjectS.Field_setShort_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: shortPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@7887a47
+
+          .put("lang.reflect.Field.setLongLjava_lang_ObjectJ.Field_setLong_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setLongLjava_lang_ObjectJ.Field_setLong_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: longPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@5c13759
+
+          .put("lang.reflect.Field.setDoubleLjava_lang_ObjectD.Field_setDouble_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: doublePublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@4dd95e2
+
+          .put("lang.reflect.Field.setShortLjava_lang_ObjectS.Field_setShort_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getLjava_lang_Object.Field_get_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.getLongLjava_lang_Object.Field_getLong_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setLjava_lang_ObjectLjava_lang_Object.Field_set_A05", any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.ExceptionInInitializerError> but was<java.lang.NullPointerException>
+          // Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+          // 2) t02
+          // java.lang.NullPointerException: Attempt to invoke virtual method 'int java.io.InputStream.available()' on a null object reference
+
+          .put("lang.reflect.Field.setLjava_lang_ObjectLjava_lang_Object.Field_set_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Exception is not thrown: field: shortPublicField, object: com.google.jctf.test.lib.java.lang.reflect.Field.TestStaticFinalPrimitiveField@bf7ecde
+
+          .put("lang.reflect.Constructor.newInstance$Ljava_lang_Object.Constructor_newInstance_A06",
+              any())
+          // 1) t05
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put("lang.reflect.Constructor.isSynthetic.Constructor_isSynthetic_A01", any())
+          // 1) t02
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.SyntheticConstructorTestData
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.SyntheticConstructorTestData" on path: DexPathList[[dex file "/tmp/junit1480674965150331230/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A03",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingExceptionTypeConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingExceptionTypeConstructor" on path: DexPathList[[dex file "/tmp/junit4009528913069484861/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A02",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor" on path: DexPathList[[dex file "/tmp/junit1321049867835327167/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01",
+              any())
+          // 1) t03
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Third
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Third" on path: DexPathList[[dex file "/tmp/junit8133550864036959380/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+          // 2) t04
+          // java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Fourth
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A01$Fourth" on path: DexPathList[[dex file "/tmp/junit8133550864036959380/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getAnnotationLjava_lang_Class.Constructor_getAnnotation_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put(
+              "lang.reflect.Constructor.getDeclaredAnnotations.Constructor_getDeclaredAnnotations_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put(
+              "lang.reflect.Constructor.getGenericExceptionTypes.Constructor_getGenericExceptionTypes_A04",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedExceptionTypeConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedExceptionTypeConstructor" on path: DexPathList[[dex file "/tmp/junit376255323471566097/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.InvocationTargetException.serialization.InvocationTargetException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/InvocationTargetException/serialization/InvocationTargetException_serialization_A01.golden.0.ser
+
+          .put("lang.reflect.Constructor.toGenericString.Constructor_toGenericString_A01", any())
+          // 1) t04
+          // org.junit.ComparisonFailure: expected:<..._toGenericString_A01[.com.google.jctf.test.lib.java.lang.reflect.Constructor.toGenericString.Constructor_toGenericString_A01]$GenericClass<java.l...> but was:<..._toGenericString_A01[]$GenericClass<java.l...>
+
+          .put("lang.reflect.Constructor.getTypeParameters.Constructor_getTypeParameters_A02",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor" on path: DexPathList[[dex file "/tmp/junit7135581864552916266/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getGenericParameterTypes.Constructor_getGenericParameterTypes_A03",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.TypeNotPresentException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingParameterTypeConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MissingParameterTypeConstructor" on path: DexPathList[[dex file "/tmp/junit1307676357171999053/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getGenericParameterTypes.Constructor_getGenericParameterTypes_A04",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.MalformedParameterizedTypeException> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedParameterTypeConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.MalformedParameterTypeConstructor" on path: DexPathList[[dex file "/tmp/junit4591001470670613975/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put(
+              "lang.reflect.Constructor.getGenericParameterTypes.Constructor_getGenericParameterTypes_A02",
+              any())
+          // 1) t01
+          // java.lang.Exception: Unexpected exception, expected<java.lang.reflect.GenericSignatureFormatError> but was<java.lang.ClassNotFoundException>
+          // Caused by: java.lang.ClassNotFoundException: com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor
+          // Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.jctf.test.lib.java.lang.reflect.Constructor.BadSignatureConstructor" on path: DexPathList[[dex file "/tmp/junit4070388768283971494/classes.dex"],nativeLibraryDirectories=[r8/tools/linux/art/bin/../lib, r8/tools/linux/art/bin/../lib]]
+
+          .put("lang.reflect.AccessibleObject.setAccessibleZ.AccessibleObject_setAccessible_A03",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "lang.reflect.UndeclaredThrowableException.serialization.UndeclaredThrowableException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/reflect/UndeclaredThrowableException/serialization/UndeclaredThrowableException_serialization_A01.golden.0.ser
+
+          .put("lang.reflect.AccessibleObject.setAccessibleZ.AccessibleObject_setAccessible_A02",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "lang.reflect.AccessibleObject.setAccessible$Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A03",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "lang.reflect.AccessibleObject.isAnnotationPresentLjava_lang_Class.AccessibleObject_isAnnotationPresent_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t04
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put(
+              "lang.reflect.AccessibleObject.setAccessible$Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A02",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.reflect.AccessibleObject.getAnnotations.AccessibleObject_getAnnotations_A01",
+              any())
+          // 1) t04
+          // java.lang.AssertionError: expected:<0> but was:<1>
+          // 2) t06
+          // java.lang.AssertionError: Misconfigured test
+
+          .put(
+              "lang.reflect.AccessibleObject.getDeclaredAnnotations.AccessibleObject_getDeclaredAnnotations_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: expected:<0> but was:<1>
+
+          .put(
+              "lang.reflect.AccessibleObject.getAnnotationLjava_lang_Class.AccessibleObject_getAnnotation_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+          // 2) t03
+          // java.lang.AssertionError: Misconfiguration: MissingAntn should not be accessible
+
+          .put("lang.IllegalAccessException.serialization.IllegalAccessException_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/IllegalAccessException/serialization/IllegalAccessException_serialization_A01.golden.0.ser
+
+          .put("lang.Character.getTypeI.Character_getType_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<22> but was:<28>
+
+          .put("lang.Character.isDigitI.Character_isDigit_A01", any())
+          // 1) t02
+          // java.lang.AssertionError
+
+          .put("lang.Character.getTypeC.Character_getType_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<22> but was:<28>
+
+          .put("lang.Character.serialization.Character_serialization_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/lang/Character/serialization/Character_serialization_A01.golden.0.ser
+
+          .put("lang.Character.isDigitC.Character_isDigit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put("lang.Character.digitCI.Character_digit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<1> but was:<-1>
+
+          .put("lang.Character.digitII.Character_digit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<1> but was:<-1>
+
+          .put("lang.Character.isLowerCaseC.Character_isLowerCase_A01", any())
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put("lang.Character.getDirectionalityI.Character_getDirectionality_A01", any())
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put("lang.Character.UnicodeBlock.ofC.UnicodeBlock_of_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: expected null, but was:<ARABIC_SUPPLEMENT>
+
+          .put("lang.Character.UnicodeBlock.ofI.UnicodeBlock_of_A01", any())
+          // 1) t02
+          // java.lang.AssertionError: expected null, but was:<ANCIENT_GREEK_NUMBERS>
+
+          .put("lang.Character.isLowerCaseI.Character_isLowerCase_A01", any())
+          // 1) t01
+          // java.lang.AssertionError
+
+          .put("lang.Process.waitFor.Process_waitFor_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: expected:<127> but was:<1>
+
+          .put("lang.System.getProperties.System_getProperties_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.Process.getErrorStream.Process_getErrorStream_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<69>
+
+          .put("lang.Character.getDirectionalityC.Character_getDirectionality_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Char #0
+
+          .put("lang.Process.exitValue.Process_exitValue_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: expected:<127> but was:<1>
+
+          .put("lang.System.loadLjava_lang_String.System_load_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.Process.getInputStream.Process_getInputStream_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<0> but was:<-1>
+
+          .put("lang.System.loadLibraryLjava_lang_String.System_loadLibrary_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A02",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.System.runFinalizersOnExitZ.System_runFinalizersOnExit_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.System.getenvLjava_lang_String.System_getenv_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.getenvLjava_lang_String.System_getenv_A01
+          // expected:<0> but was:<1>
+
+          .put("lang.System.getenv.System_getenv_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.getenv.System_getenv_A01
+          // expected:<0> but was:<1>
+
+          .put("lang.System.getPropertyLjava_lang_StringLjava_lang_String.System_getProperty_A01",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.System.exitI.System_exit_A01", any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<88> but was:<1>
+
+          .put(
+              "util.concurrent.ArrayBlockingQueue.serialization.ArrayBlockingQueue_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/ArrayBlockingQueue/serialization/ArrayBlockingQueue_serialization_A01.golden.0.ser
+
+          .put("lang.System.arraycopyLjava_lang_ObjectILjava_lang_ObjectII.System_arraycopy_A04",
+              any())
+          // 1) t05
+          // java.lang.ArrayIndexOutOfBoundsException: src.length=3 srcPos=0 dst.length=1 dstPos=1 length=2
+          // 2) t06
+          // java.lang.ArrayIndexOutOfBoundsException: src.length=1 srcPos=0 dst.length=3 dstPos=1 length=2
+          // 3) t07
+          // java.lang.ArrayIndexOutOfBoundsException: src.length=1 srcPos=0 dst.length=1 dstPos=1 length=100
+
+          .put("lang.System.setPropertiesLjava_util_Properties.System_setProperties_A02", any())
+          // 1) t01
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.setPropertiesLjava_util_Properties.System_setProperties_A02
+          // expected:<0> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: Bad exit code of spawned java proccess, err=Error: Could not find or load main class com.google.jctf.test.lib.java.lang.System.setPropertiesLjava_util_Properties.System_setProperties_A02
+          // expected:<0> but was:<1>
+
+          .put("lang.System.clearPropertyLjava_lang_String.System_clearProperty_A02", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put("lang.System.getPropertyLjava_lang_String.System_getProperty_A01", any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "util.concurrent.LinkedBlockingQueue.serialization.LinkedBlockingQueue_serialization_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/LinkedBlockingQueue/serialization/LinkedBlockingQueue_serialization_A01.golden.0.ser
+
+          .put(
+              "util.concurrent.LinkedBlockingDeque.serialization.LinkedBlockingDeque_serialization_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/LinkedBlockingDeque/serialization/LinkedBlockingDeque_serialization_A01.golden.0.ser
+
+          .put(
+              "util.concurrent.ConcurrentLinkedQueue.serialization.ConcurrentLinkedQueue_serialization_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/ConcurrentLinkedQueue/serialization/ConcurrentLinkedQueue_serialization_A01.golden.0.ser
+
+          .put("util.concurrent.SynchronousQueue.serialization.SynchronousQueue_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+          // 2) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/SynchronousQueue/serialization/SynchronousQueue_serialization_A01.golden.0.ser
+
+          .put(
+              "util.concurrent.CopyOnWriteArrayList.serialization.CopyOnWriteArrayList_serialization_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/CopyOnWriteArrayList/serialization/CopyOnWriteArrayList_serialization_A01.golden.0.ser
+
+          .put("util.concurrent.CopyOnWriteArrayList.subListII.CopyOnWriteArrayList_subList_A01",
+              any())
+          // 1) t03
+          // java.util.ConcurrentModificationException
+
+          .put(
+              "util.concurrent.ScheduledThreadPoolExecutor.getTaskCount.ScheduledThreadPoolExecutor_getTaskCount_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: expected:<1> but was:<2>
+
+          .put(
+              "util.concurrent.ConcurrentHashMap.serialization.ConcurrentHashMap_serialization_A01",
+              any())
+          // 1) t01
+          // java.lang.AssertionError: Unable to configure default providers
+
+          .put("util.concurrent.ConcurrentHashMap.keySet.ConcurrentHashMap_keySet_A01", any())
+          // 1) t01
+          // java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentHashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in r8/tools/linux/art/framework/core-oj-hostdex.jar)
+          // 2) t02
+          // java.lang.NoSuchMethodError: No virtual method keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; in class Ljava/util/concurrent/ConcurrentHashMap; or its super classes (declaration of 'java.util.concurrent.ConcurrentHashMap' appears in r8/tools/linux/art/framework/core-oj-hostdex.jar)
+
+          .put(
+              "util.concurrent.Executors.privilegedThreadFactory.Executors_privilegedThreadFactory_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Unexpected exception: java.lang.AssertionError: no AccessControlException
+          // 2) t02
+          // java.lang.AssertionError: java.lang.AssertionError: no AccessControlException
+          // Caused by: java.lang.AssertionError: no AccessControlException
+          // 3) t03
+          // java.lang.AssertionError: Unexpected exception: java.lang.AssertionError: no AccessControlException
+          // 4) t03
+          // java.lang.AssertionError: java.lang.AssertionError: no AccessControlException
+          // Caused by: java.lang.AssertionError: no AccessControlException
+
+          .put(
+              "util.concurrent.Executors.privilegedCallableLjava_util_concurrent_Callable.Executors_privilegedCallable_A01",
+              any())
+          // 1) t01
+          // java.lang.SecurityException
+
+          .put(
+              "util.concurrent.CopyOnWriteArraySet.serialization.CopyOnWriteArraySet_serialization_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/CopyOnWriteArraySet/serialization/CopyOnWriteArraySet_serialization_A01.golden.0.ser
+
+          .put(
+              "util.concurrent.Executors.privilegedCallableUsingCurrentClassLoaderLjava_util_concurrent_Callable.Executors_privilegedCallableUsingCurrentClassLoader_A01",
+              any())
+          // 1) t02
+          // java.lang.SecurityException
+
+          .put(
+              "util.concurrent.PriorityBlockingQueue.ConstructorLjava_util_Collection.PriorityBlockingQueue_Constructor_A01",
+              any())
+          // 1) t03
+          // java.lang.AssertionError: expected same:<com.google.jctf.test.lib.java.util.concurrent.PriorityBlockingQueue.MyReverseComparator@735f1e1> was not:<null>
+
+          .put(
+              "util.concurrent.PriorityBlockingQueue.serialization.PriorityBlockingQueue_serialization_A01",
+              any())
+          // 1) t02
+          // java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/PriorityBlockingQueue/serialization/PriorityBlockingQueue_serialization_A01.golden.0.ser
+
+          .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A01", match(R8_COMPILER))
+          // 1) t05
+          // java.lang.AssertionError: Destroyed thread group was not finalized
+
+          .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A01",
+              match(D8_COMPILER, runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.IllegalThreadStateException: Thread group still contains threads: Test group
+          // 2) t04
+          // java.lang.IllegalThreadStateException: Thread group still contains threads:  Depth = 2, N = 0
+          // 3) t05
+          // java.lang.AssertionError: Destroyed thread group was not finalized
+
+          .put("lang.Thread.start.Thread_start_A01",
+              match(runtimes(DexVm.ART_7_0_0)))
+          // 1) t01(com.google.jctf.test.lib.java.lang.Thread.start.Thread_start_A01)
+          // java.lang.AssertionError: no IllegalThreadStateException 1
+
+          .put("lang.String.getBytesLjava_lang_String.String_getBytes_A02",
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01(com.google.jctf.test.lib.java.lang.String.getBytesLjava_lang_String.String_getBytes_A02)
+          // java.lang.Exception: Unexpected exception, expected<java.lang.NullPointerException> but was<java.io.UnsupportedEncodingException>
+
+          .put(
+              "util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A02",
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // java.lang.AssertionError: Expected exception: java.lang.IndexOutOfBoundsException
+
+          .put(
+              "util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01",
+              match(runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01(com.google.jctf.test.lib.java.util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01)
+          // java.lang.AssertionError: expected:<3> but was:<1>
+          // 2) t02(com.google.jctf.test.lib.java.util.concurrent.CopyOnWriteArrayList.lastIndexOfLjava_lang_ObjectI.CopyOnWriteArrayList_lastIndexOf_A01)
+          // java.lang.ArrayIndexOutOfBoundsException: length=3; index=2147483647
+
+          .put("lang.StringBuffer.getCharsII$CI.StringBuffer_getChars_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t03
+          // java.lang.NullPointerException: dst == null
+
+          .put("lang.StringBuffer.appendF.StringBuffer_append_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError: Buffer is invalid length after append expected:<26> but was:<25>
+
+          .put("lang.StringBuffer.insertI$CII.StringBuffer_insert_A02",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.NullPointerException: Attempt to get length of null array
+
+          .put("lang.StrictMath.scalbDI.StrictMath_scalb_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Wrong result provided for argument: -1.7976931348623157E308 scaleFactor: 2147483647 expected:<-Infinity> but was:<-0.0>
+
+          .put("lang.StrictMath.scalbDI.StrictMath_scalb_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t03
+          // java.lang.AssertionError: Wrong result provided for argument: -2.2250738585072014E-308 scaleFactor: -2147483647 expected:<-0.0> but was:<-Infinity>
+          // 2) t04
+          // java.lang.AssertionError: Wrong result provided for argument: 1.7976931348623157E308 scaleFactor: -2046 expected:<2.2250738585072014E-308> but was:<2.225073858507201E-308>
+
+          .put("lang.StrictMath.scalbFI.StrictMath_scalb_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Wrong result provided for argument: -3.4028235E38 scaleFactor: 2147483647 expected:<-Infinity> but was:<-0.0>
+
+          .put("lang.StrictMath.scalbFI.StrictMath_scalb_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError: Wrong result provided for argument: 3.4028235E38 scaleFactor: -254 expected:<1.1754943508222875E-38> but was:<1.1754942106924411E-38>
+          // 2) t03
+          // java.lang.AssertionError: Wrong result provided for argument: -1.1754944E-38 scaleFactor: -2147483647 expected:<-0.0> but was:<-Infinity>
+
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A07",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError: wrong daemonism expected:<true> but was:<false>
+
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A07",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError: wrong daemonism expected:<true> but was:<false>
+
+          .put("lang.Thread.toString.Thread_toString_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError
+
+          .put("lang.Thread.start.Thread_start_A02",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.IllegalThreadStateException: Thread group still contains threads: start
+
+          .put("lang.Thread.setPriorityI.Thread_setPriority_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError: expected:<5> but was:<10>
+
+          .put("lang.ClassLoader.ConstructorLjava_lang_ClassLoader.ClassLoader_Constructor_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.NullPointerException: parentLoader == null && !nullAllowed
+          // 2) t03
+          // java.lang.NullPointerException: parentLoader == null && !nullAllowed
+
+          .put("lang.Enum.compareToLjava_lang_Enum.Enum_compareTo_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.ClassCastException
+
+          .put("lang.Enum.hashCode.Enum_hashCode_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError
+
+          .put("lang.StackTraceElement.hashCode.StackTraceElement_hashCode_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // java.lang.AssertionError
+
+          .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: should throw ClassCastException.
+
+          .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: should throw ClassCastException.
+
+          .put("lang.Float.toStringF.Float_toString_A04",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // org.junit.ComparisonFailure: Invalid string produced for bits: 4efffffa expected:<2.147482[88]E9> but was:<2.147482[9]E9>
+
+          .put("lang.Float.toStringF.Float_toString_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t02
+          // org.junit.ComparisonFailure: expected:<-1.175494[35]E-38> but was:<-1.175494[4]E-38>
+
+          .put("lang.ThreadGroup.getMaxPriority.ThreadGroup_getMaxPriority_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: New value should be the same as we set expected:<2> but was:<1>
+
+          .put(
+              "lang.ThreadGroup.uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable.ThreadGroup_uncaughtException_A02",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t05
+          // java.lang.AssertionError: Non-informative exception info: java.lang.RuntimeException
+
+          .put("lang.ThreadGroup.list.ThreadGroup_list_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t04
+          // java.lang.IllegalThreadStateException: Thread group still contains threads: Test group(list)
+
+          .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.IllegalThreadStateException: Thread group still contains threads: Test root(setMaxPriority)
+
+          .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A04",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: New value should be the same as we set expected:<2> but was:<1>
+          // 2) t02
+          // java.lang.AssertionError: expected:<4> but was:<1>
+          // 3) t03
+          // java.lang.AssertionError: expected:<7> but was:<1>
+
+          .put("lang.ThreadGroup.toString.ThreadGroup_toString_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // org.junit.ComparisonFailure: toString does not follow the RI expected:<... group(toString),max[pri]=10]> but was:<... group(toString),max[Priority]=10]>
+
+          .put("lang.Class.getFieldLjava_lang_String.Class_getField_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t04
+          // java.lang.AssertionError: expected:<interface com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A01$I1> but was:<class com.google.jctf.test.lib.java.lang.Class.getFieldLjava_lang_String.Class_getField_A01$S1>
+
+          .put("lang.String.replaceCC.String_replace_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t04
+          // java.lang.AssertionError: expected same:<aaaaaa> was not:<aaaaaa>
+
+          .put("lang.Package.isCompatibleWithLjava_lang_String.Package_isCompatibleWith_A02",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: NumberFormatException isn't thrown for desired . and current 1.0
+          // 2) t03
+          // java.lang.AssertionError: NumberFormatException isn't thrown for desired 1.0 and current .
+
+          .put("lang.StringBuilder.appendF.StringBuilder_append_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Invalid length of created builder expected:<14> but was:<13>
+
+          .put("lang.StringBuilder.insertIF.StringBuilder_insert_A01",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Invalid length of created builder expected:<14> but was:<13>
+
+          .put(
+              "lang.reflect.AccessibleObject.setAccessible$Ljava_lang_reflect_AccessibleObjectZ.AccessibleObject_setAccessible_A04",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: SecurityException expected.
+          // 2) t02
+          // java.lang.AssertionError: SecurityException expected.
+          // 3) t03
+          // java.lang.AssertionError: SecurityException expected.
+          // 4) t04
+          // java.lang.AssertionError: SecurityException expected.
+
+          .put("lang.Character.UnicodeBlock.forName_java_lang_String.UnicodeBlock_forName_A03",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
+
+          .put("lang.System.loadLjava_lang_String.System_load_A02",
+              match(runtimes(DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // 1) t03
+          // java.lang.AssertionError: Expected exception: java.lang.UnsatisfiedLinkError
+
+          .put("lang.StrictMath.nextAfterFD.StrictMath_nextAfter_A01",
+              match(R8_COMPILER, runtimes(DexVm.ART_5_1_1)))
+          // 1) t01
+          // java.lang.AssertionError: Wrong value returned for start: Infinity direction: NaN expected:<Infinity> but was:<NaN>
+          // 2) t02
+          // java.lang.AssertionError: Wrong value returned for start: -0.0 direction: NaN expected:<-1.4E-45> but was:<NaN>
+
+          .put("lang.Math.hypotDD.Math_hypot_A04", match(runtimes(DexVm.ART_5_1_1)))
+          // 1) t04
+          // java.lang.AssertionError
+
+          .build(); // end of failuresToTriage
+
+  public static final Multimap<String, TestCondition> flakyWithArt =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+
+          .put("lang.Object.notifyAll.Object_notifyAll_A03", any())
+          // AssertionError: Unexpected art failure: '+ invoke_with=
+          // AssertionError: expected:<BLOCKED> but was:<WAITING>
+
+          .put("lang.Object.notify.Object_notify_A03", any())
+          // AssertionError: Unexpected art failure: '+ invoke_with=
+          // AssertionError: expected:<BLOCKED> but was:<WAITING>
+
+          .put(
+              "util.concurrent.ConcurrentSkipListSet.addLjava_lang_Object.ConcurrentSkipListSet_add_A01",
+              any())
+          // AssertionError: Unexpected art failure: '+ invoke_with=
+          // AssertionError: Expected exception: java.lang.ClassCastException
+
+          .put("util.concurrent.SynchronousQueue.ConstructorZ", any())
+          // Only on bots:
+          // 1) t02
+          // java.lang.AssertionError: java.lang.AssertionError: expected:<5> but was:<4>
+
+          .put("lang.Thread.interrupt.Thread_interrupt_A04",
+              match(D8_COMPILER, runtimes(DexVm.ART_6_0_1)))
+          // Been running fine for a while then this happened on a bot:
+          // 1) t01
+          // java.lang.AssertionError: expected:<BLOCKED> but was:<RUNNABLE>
+
+          .put("util.concurrent.SynchronousQueue.ConstructorZ.SynchronousQueue_Constructor_A01",
+              any())
+          // Failed on bot only:
+          // 1) t02
+          // java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
+
+          .put("lang.Thread.getState.Thread_getState_A01", any())
+          // Failed on bot only (R8):
+          // 1) t02
+          // java.lang.AssertionError: java.lang.AssertionError: expected:<7> but was:<6>
+
+          .build(); // end of flakyWithArt
+
+  public static final Multimap<String, TestCondition> timeoutsWithArt =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          .put("lang.Thread.interrupt.Thread_interrupt_A01", any())
+          .put("lang.Thread.resume.Thread_resume_A01", any())
+          .put("lang.Thread.stop.Thread_stop_A01", any())
+          .put("lang.Thread.suspend.Thread_suspend_A01", any())
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A04",
+              any())
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A03",
+              any())
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_StringJ.Thread_Constructor_A05",
+              any())
+          .put("lang.Thread.setNameLjava_lang_String.Thread_setName_A02", any())
+          .put("lang.Thread.stop.Thread_stop_A02", any())
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A02",
+              any())
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A03",
+              any())
+          .put("lang.Thread.getStackTrace.Thread_getStackTrace_A03", any())
+          .put(
+              "lang.Thread.setDefaultUncaughtExceptionHandler.Thread_setDefaultUncaughtExceptionHandler_A02",
+              any())
+          .put("lang.Thread.checkAccess.Thread_checkAccess_A01", any())
+          .put(
+              "lang.Thread.ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String.Thread_Constructor_A04",
+              any())
+          .put("lang.Thread.setUncaughtExceptionHandler.Thread_setUncaughtExceptionHandler_A02",
+              any())
+          .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A01", any())
+          .put("lang.Thread.getAllStackTraces.Thread_getAllStackTraces_A02", any())
+          .put(
+              "lang.Thread.setContextClassLoaderLjava_lang_ClassLoader.Thread_setContextClassLoader_A02",
+              any())
+          .put("lang.Thread.setPriorityI.Thread_setPriority_A02", any())
+          .put("lang.Thread.stopLjava_lang_Throwable.Thread_stop_A02", any())
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A04",
+              any())
+          .put("lang.Thread.getContextClassLoader.Thread_getContextClassLoader_A02", any())
+          .put("lang.ThreadGroup.suspend.ThreadGroup_suspend_A02", any())
+          .put("lang.Thread.setDaemonZ.Thread_setDaemon_A03", any())
+          .put("lang.ProcessBuilder.environment.ProcessBuilder_environment_A07", any())
+          .put(
+              "lang.Runtime.exec$Ljava_lang_String$Ljava_lang_StringLjava_io_File.Runtime_exec_A04",
+              any())
+          .put("lang.Runtime.execLjava_lang_String$Ljava_lang_String.Runtime_exec_A04", any())
+          .put("lang.Runtime.exec$Ljava_lang_String.Runtime_exec_A04", any())
+          .put("lang.Runtime.execLjava_lang_String.Runtime_exec_A04", any())
+          .put("lang.System.clearPropertyLjava_lang_String.System_clearProperty_A03", any())
+          .put("lang.System.getSecurityManager.System_getSecurityManager_A01", any())
+          .put("lang.System.setInLjava_io_InputStream.System_setIn_A02", any())
+          .put("lang.System.setOutLjava_io_PrintStream.System_setOut_A02", any())
+          .put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A04", any())
+          .put("lang.ThreadGroup.enumerate$ThreadGroupZ.ThreadGroup_enumerate_A03", any())
+          .put("lang.ThreadGroup.enumerate$Thread.ThreadGroup_enumerate_A03", any())
+          .put("lang.ThreadGroup.enumerate$ThreadZ.ThreadGroup_enumerate_A03", any())
+          .put("lang.ThreadGroup.interrupt.ThreadGroup_interrupt_A02", any())
+          .put("lang.ThreadGroup.resume.ThreadGroup_resume_A02", any())
+          .put("lang.ThreadGroup.setMaxPriorityI.ThreadGroup_setMaxPriority_A02", any())
+          .put("lang.Runtime.exec$Ljava_lang_String$Ljava_lang_String.Runtime_exec_A04", any())
+          .put("lang.System.getenvLjava_lang_String.System_getenv_A03", any())
+          .put("lang.System.setPropertyLjava_lang_StringLjava_lang_String.System_setProperty_A02",
+              any())
+          .put("lang.ThreadGroup.enumerate$ThreadGroup.ThreadGroup_enumerate_A03", any())
+          .put("lang.ThreadGroup.getParent.ThreadGroup_getParent_A02", any())
+          .put("lang.ThreadGroup.setDaemonZ.ThreadGroup_setDaemon_A02", any())
+          .put("lang.ThreadGroup.stop.ThreadGroup_stop_A02", any())
+          .put("lang.Class.getSuperclass.Class_getSuperclass_A01", any())
+          .put("lang.System.getenv.System_getenv_A03", any())
+          .put("lang.System.inheritedChannel.System_inheritedChannel_A01", any())
+          .put(
+              "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+              any())
+          .put("lang.System.arraycopyLjava_lang_ObjectILjava_lang_ObjectII.System_arraycopy_A03",
+              any())
+          .put("lang.System.setErrLjava_io_PrintStream.System_setErr_A02", any())
+          .put(
+              "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+              any())
+          .put(
+              "lang.System.setSecurityManagerLjava_lang_SecurityManager.System_setSecurityManager_A01",
+              any())
+          .put(
+              "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+              any())
+          .put(
+              "util.concurrent.ArrayBlockingQueue.containsLjava_lang_Object.ArrayBlockingQueue_contains_A01",
+              any())
+          .put("lang.System.setPropertiesLjava_util_Properties.System_setProperties_A01", any())
+          .put(
+              "util.concurrent.CopyOnWriteArrayList.ConstructorLjava_util_Collection.CopyOnWriteArrayList_Constructor_A02",
+              any())
+          .put("util.concurrent.CyclicBarrier.reset.CyclicBarrier_reset_A03", any())
+          .put("lang.System.clearPropertyLjava_lang_String.System_clearProperty_A01", any())
+          .put("lang.System.getenv.System_getenv_A04", any())
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A02", any())
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A13", any())
+          .build(); // end of timeoutsWithArt
+
+  private static final boolean testMatch(Multimap<String, TestCondition> testConditions,
+      String name,
+      CompilerUnderTest compilerUnderTest, DexVm dexVm) {
+    Collection<TestCondition> entries = testConditions.get(name);
+    for (TestCondition entry : entries) {
+      if (entry.test(DexTool.NONE, compilerUnderTest, dexVm)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static final Outcome getExpectedOutcome(String name,
+      CompilerUnderTest compilerUnderTest,
+      DexVm dexVm) {
+
+    Outcome outcome = null;
+
+    if (testMatch(failuresToTriage, name, compilerUnderTest, dexVm)) {
+      outcome = Outcome.FAILS_WITH_ART;
+    }
+    if (testMatch(timeoutsWithArt, name, compilerUnderTest, dexVm)) {
+      assert outcome == null;
+      outcome = Outcome.TIMEOUTS_WITH_ART;
+    }
+    if (testMatch(flakyWithArt, name, compilerUnderTest, dexVm)) {
+      assert outcome == null;
+      outcome = Outcome.FLAKY_WITH_ART;
+    }
+    return outcome == null ? Outcome.PASSES : outcome;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
new file mode 100644
index 0000000..8fca2ed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8CodeCanonicalizationTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.dex.DexFileReader;
+import com.android.tools.r8.dex.Segment;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+
+public class R8CodeCanonicalizationTest {
+
+  private static final String SOURCE_DEX = "invokeempty/classes.dex";
+
+  private int readNumberOfCodes(Path file) throws IOException {
+    Segment[] segments = DexFileReader.parseMapFrom(file);
+    for (Segment segment : segments) {
+      if (segment.getType() == Constants.TYPE_CODE_ITEM) {
+        return segment.getSize();
+      }
+    }
+    return 0;
+  }
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void testNumberOfCodeItemsUnchanged()
+      throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+    int numberOfCodes = readNumberOfCodes(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX));
+    ToolHelper.runR8(ToolHelper.EXAMPLES_BUILD_DIR + SOURCE_DEX, temp.getRoot().getCanonicalPath());
+    int newNumberOfCodes = readNumberOfCodes(
+        Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex"));
+    Assert.assertEquals("Number of codeitems does not change.", numberOfCodes, newNumberOfCodes);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
new file mode 100644
index 0000000..b026dcd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -0,0 +1,1368 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.ArtErrorParser;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.JarBuilder;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.ObjectArrays;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * This test class is not invoked directly. Instead, the gradle script generates one subclass per
+ * actual art test. This allows us to run these in parallel.
+ */
+public abstract class R8RunArtTestsTest {
+
+  private static final boolean DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE = true;
+  private static String[] D8_EXTRA_ARGS = {"--debug"};
+
+  private final String name;
+  private final DexTool toolchain;
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  public enum DexTool {
+    JACK,
+    DX,
+    NONE // Working directly on .class files.
+  }
+
+  public enum CompilerUnderTest {
+    D8,
+    R8
+  }
+
+  private static final String ART_TESTS_DIR = "tests/art";
+  private static final String ART_TESTS_NATIVE_LIBRARY_DIR = "tests/art/lib64";
+
+  // Input jar for jctf tests.
+  private static final String JCTF_COMMON_JAR = "build/libs/jctfCommon.jar";
+
+  // Parent dir for on-the-fly compiled jctf dex output.
+  private static final String JCTF_TESTS_PREFIX = "build/classes/jctfTests";
+  private static final String JCTF_TESTS_LIB_PREFIX =
+      JCTF_TESTS_PREFIX + "/com/google/jctf/test/lib";
+  private static final String JUNIT_TEST_RUNNER = "org.junit.runner.JUnitCore";
+  private static final String JUNIT_JAR = "third_party/gradle/gradle/lib/plugins/junit-4.12.jar";
+  private static final String HAMCREST_JAR =
+      "third_party/gradle/gradle/lib/plugins/hamcrest-core-1.3.jar";
+
+  // Test that required to set min-sdk-version to a specific value.
+  private static Map<String, Integer> needMinSdkVersion =
+      new ImmutableMap.Builder<String, Integer>()
+          // Android O
+          .put("953-invoke-polymorphic-compiler", Constants.ANDROID_O_API)
+          .put("957-methodhandle-transforms", Constants.ANDROID_O_API)
+          .put("958-methodhandle-stackframe", Constants.ANDROID_O_API)
+          .put("959-invoke-polymorphic-accessors", Constants.ANDROID_O_API)
+          // Test intentionally asserts presence of bridge default methods desugar removes.
+          .put("044-proxy", Constants.ANDROID_N_API)
+          // Test intentionally asserts absence of default interface method in a class.
+          .put("048-reflect-v8", Constants.ANDROID_N_API)
+          // Interface initializer is not triggered after desugaring.
+          .put("962-iface-static", Constants.ANDROID_N_API)
+          // Interface initializer is not triggered after desugaring.
+          .put("964-default-iface-init-gen", Constants.ANDROID_N_API)
+          // AbstractMethodError (for method not implemented in class) instead of
+          // IncompatibleClassChangeError (for conflict of default interface methods).
+          .put("968-default-partial-compile-gen", Constants.ANDROID_N_API)
+          // NoClassDefFoundError (for companion class) instead of NoSuchMethodError.
+          .put("970-iface-super-resolution-gen", Constants.ANDROID_N_API)
+          // NoClassDefFoundError (for companion class) instead of AbstractMethodError.
+          .put("971-iface-super", Constants.ANDROID_N_API)
+          // Test for miranda methods is not relevant for desugaring scenario.
+          .put("972-default-imt-collision", Constants.ANDROID_N_API)
+          // Uses default interface methods.
+          .put("972-iface-super-multidex", Constants.ANDROID_N_API)
+          // java.util.Objects is missing and test has default methods.
+          .put("973-default-multidex", Constants.ANDROID_N_API)
+          // a.klass.that.does.not.Exist is missing and test has default methods.
+          .put("974-verify-interface-super", Constants.ANDROID_N_API)
+          // Desugaring of interface private methods is not yet supported.
+          .put("975-iface-private", Constants.ANDROID_N_API)
+          .build();
+
+  // Tests requiring interface method desugaring because they explicitly or
+  // implicitly define static or default interface methods and are enabled
+  // on pre-N Android.
+  private static List<String> enableInterfaceMethodDesugaring =
+      ImmutableList.of(
+          "563-checker-invoke-super",
+          "604-hot-static-interface",
+          "961-default-iface-resolution-gen",
+          "963-default-range-smali",
+          "965-default-verify",
+          "967-default-ame",
+          "969-iface-super",
+          "978-virtual-interface"
+      );
+
+  // Tests that timeout when run with Art.
+  private static final Multimap<String, TestCondition> timeoutOrSkipRunWithArt =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // Loops on art - timeout.
+          .put("109-suspend-check", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1)))
+          // Flaky loops on art.
+          .put("129-ThreadGetId", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1)))
+          // Takes ages to run on art 5.1.1 and behaves the same as on 6.0.1. Running this
+          // tests on 5.1.1 makes our buildbot cycles time too long.
+          .put("800-smali", TestCondition.match(TestCondition.runtimes(DexVm.ART_5_1_1)))
+          .build();
+
+  // Tests that are flaky with the Art version we currently use.
+  // TODO(zerny): Amend flaky tests with an expected flaky result to track issues.
+  private static List<String> flakyRunWithArt = ImmutableList.of(
+      // Can crash but mostly passes
+      // Crashes:
+      // check_reference_map_visitor.h:44] At Main.f
+      // Check failed: size() >= sizeof(T) (size()=0, sizeof(T)=1)
+      // If art is passed -Xrelocate instead of -Xnorelocate the test passes.
+      "004-ReferenceMap",
+      // Also marked flaky in the art repo, sometimes hangs, sometimes fails with a segfault:
+      // line 105: 23283 Segmentation fault
+      "004-ThreadStress",
+      // When it fails:
+      // stack_walk_jni.cc:57] Check failed: 0xdU == GetDexPc() (0xdU=13, GetDexPc()=23)
+      // The R8/D8 code does not produce code with the same instructions.
+      "004-StackWalk",
+      // Nothing ensures the weak-ref is not cleared between makeRef and printWeakReference on lines
+      // Main.java:78-79. An expected flaky result contains: "but was:<wimp: [null]".
+      "036-finalizer",
+      // Can lead to art-master crash: concurrent_copying.cc:2135] Check failed:
+      // to_ref != nullptr Fall-back non-moving space allocation failed
+      "080-oom-fragmentation",
+      // Seen crash: currently no more information
+      "144-static-field-sigquit",
+      // Opens a lot of file descriptors and depending on the state of the machine this
+      // can crash art or not. Skip the run on art.
+      "151-OpenFileLimit",
+      // Can cause a segfault in the art vm 7.0.0
+      // tools/linux/art-7.0.0/bin/art: line 105: 14395 Segmentation fault
+      "607-daemon-stress",
+      // Marked as flaky in the Art repository.
+      "149-suspend-all-stress"
+  );
+
+  // Tests that are never compiled or run.
+  private static List<String> skipAltogether = ImmutableList.of(
+      // This test contains an invalid type hierarchy, which we cannot currently handle.
+      "065-mismatched-implements",
+      // This test contains invalid dex code that reads uninitialized registers after an
+      // an instruction that would in any case throw (implicit via aget null 0).
+      "706-jit-skip-compilation",
+      // This test uses a user defined class loader to validate correct execution order
+      // between loadclass event and the execution of a static method.
+      // This does not work when performing inlining across classes.
+      "496-checker-inlining-class-loader"
+  );
+
+  // Tests that may produce different output on consecutive runs or when processed or not.
+  private static List<String> outputMayDiffer = ImmutableList.of(
+      // One some versions of art, this will print the address of the boot classloader, which
+      // may change between runs.
+      "506-verify-aput",
+      // Art does fail during validation of dex files if it encounters an ill-typed virtual-invoke
+      // but allows interface-invokes to pass. As we rewrite one kind into the other, we remove
+      // a verification error and thus change output.
+      "135-MirandaDispatch",
+      // This test constructs a conflict between static and instance field and expects to throw
+      // an IllegalClassChangeException. However, with R8 or D8, the two accesses are rebound to
+      // their actual target, this resolving the conflict.
+      "073-mismatched-field"
+  );
+
+  // Tests that make use of Java 8 features.
+  private static List<String> usesJava8 = ImmutableList.of(
+      "914-hello-obsolescence",
+      "915-obsolete-2",
+      "916-obsolete-jit",
+      "919-obsolete-fields",
+      "926-multi-obsolescence",
+      "936-search-onload",
+      "940-recursive-obsolete",
+      "941-recurive-obsolete-jit",
+      "942-private-recursive",
+      "943-private-recursive-jit",
+      "946-obsolete-throw",
+      "951-threaded-obsolete",
+      "952-invoke-custom"
+  );
+
+  // Tests that make use of agents/native code. Our test setup does handle flags/linking of these.
+  private static List<String> usesNativeAgentCode = ImmutableList.of(
+      "497-inlining-and-class-loader",
+      "626-const-class-linking",
+      "642-fp-callees",
+      "909-attach-agent",
+      "917-fields-transformation",
+      "918-fields",
+      "920-objects",
+      "921-hello-failure",
+      "922-properties",
+      "923-monitors",
+      "924-threads",
+      "925-threadgroups",
+      "927-timers",
+      "928-jni-table",
+      "929-search",
+      "930-hello-retransform",
+      "931-agent-thread",
+      "932-transform-saves",
+      "933-misc-events",
+      "934-load-transform",
+      "935-non-retransformable",
+      "936-search-onload",
+      "937-hello-retransform-package",
+      "938-load-transform-bcp",
+      "939-hello-transformation-bcp",
+      "944-transform-classloaders",
+      "945-obsolete-native",
+      "947-reflect-method",
+      "948-change-annotations",
+      "949-in-memory-transform",
+      "950-redefine-intrinsic",
+      "980-redefine-object"
+  );
+
+  // Tests with custom run.
+  private static List<String> customRun = ImmutableList.of(
+      "000-nop",
+      "018-stack-overflow",
+      "055-enum-performance",
+      "071-dexfile-map-clean",
+      "091-override-package-private-method",
+      "103-string-append",
+      "115-native-bridge",
+      "116-nodex2oat",
+      "117-nopatchoat",
+      "118-noimage-dex2oat",
+      "119-noimage-patchoat",
+      "126-miranda-multidex",
+      "127-checker-secondarydex",
+      "131-structural-change",
+      "133-static-invoke-super",
+      "134-nodex2oat-nofallback",
+      "137-cfi",
+      "138-duplicate-classes-check2",
+      "146-bad-interface",
+      "147-stripped-dex-fallback",
+      "304-method-tracing",
+      "529-checker-unresolved",
+      "555-checker-regression-x86const",
+      "569-checker-pattern-replacement",
+      "570-checker-osr",
+      "574-irreducible-and-constant-area",
+      "577-profile-foreign-dex",
+      "595-profile-saving",
+      "597-deopt-new-string",
+      "608-checker-unresolved-lse",
+      "613-inlining-dex-cache",
+      "636-wrong-static-access",
+      "900-hello-plugin",
+      "901-hello-ti-agent",
+      "902-hello-transformation",
+      "910-methods",
+      "911-get-stack-trace",
+      "912-classes",
+      "913-heaps"
+  );
+
+  // Tests with C++ code (JNI)
+  private static List<String> useJNI = ImmutableList.of(
+      "004-JniTest",
+      "004-NativeAllocations",
+      "004-ReferenceMap",
+      "004-SignalTest",
+      "004-StackWalk",
+      "004-ThreadStress",
+      "004-UnsafeTest",
+      "044-proxy",
+      "051-thread",
+      "115-native-bridge",
+      "117-nopatchoat",
+      "136-daemon-jni-shutdown",
+      "137-cfi",
+      "139-register-natives",
+      "141-class-unload",
+      "148-multithread-gc-annotations",
+      "149-suspend-all-stress",
+      "154-gc-loop",
+      "155-java-set-resolved-type",
+      "157-void-class",
+      "158-app-image-class-table",
+      "454-get-vreg",
+      "457-regs",
+      "461-get-reference-vreg",
+      "466-get-live-vreg",
+      "497-inlining-and-class-loader",
+      "543-env-long-ref",
+      "566-polymorphic-inlining",
+      "570-checker-osr",
+      "595-profile-saving",
+      "596-app-images",
+      "597-deopt-new-string",
+      "616-cha",
+      "616-cha-abstract",
+      "616-cha-regression-proxy-method",
+      "616-cha-native",
+      "626-const-class-linking",
+      "626-set-resolved-string",
+      "629-vdex-speed",
+      "900-hello-plugin",
+      "901-hello-ti-agent",
+      "1337-gc-coverage"
+  );
+
+  private static List<String> expectedToFailRunWithArtNonDefault = ImmutableList.of(
+      // Fails due to missing symbol, jni tests, fails on non-R8/D8 run.
+      "004-JniTest",
+      "004-SignalTest",
+      "004-ThreadStress",
+      "004-UnsafeTest",
+      "044-proxy",
+      "051-thread",
+      "136-daemon-jni-shutdown",
+      "139-register-natives",
+      "148-multithread-gc-annotations",
+      "149-suspend-all-stress",
+      "154-gc-loop",
+      "155-java-set-resolved-type",
+      "156-register-dex-file-multi-loader",
+      "157-void-class",
+      "158-app-image-class-table",
+      "466-get-live-vreg",
+      "497-inlining-and-class-loader",
+      "566-polymorphic-inlining",
+      "596-app-images",
+      "616-cha",
+      "616-cha-abstract",
+      "616-cha-regression-proxy-method",
+      "616-cha-native",
+      "626-set-resolved-string",
+      "629-vdex-speed",
+      "1337-gc-coverage",
+
+      // Addition of checks for super-class-initialization cause this to abort on non-ToT art.
+      "008-exceptions",
+
+      // Fails due to non-matching Exception messages.
+      "201-built-in-except-detail-messages",
+
+      // Fails on non-R8/D8 run
+      "031-class-attributes",
+      "617-clinit-oome"
+  );
+
+  private static Map<DexVm, List<String>> expectedToFailRunWithArtVersion = ImmutableMap.of(
+      DexVm.ART_7_0_0, ImmutableList.of(
+          // Generally fails on non-R8/D8 running.
+          "412-new-array",
+          "610-arraycopy",
+          "625-checker-licm-regressions",
+          // Crashes the VM, cause is unclear.
+          "080-oom-throw"
+      ),
+      DexVm.ART_6_0_1, ImmutableList.of(
+          // Generally fails on non-R8/D8 running.
+          "004-checker-UnsafeTest18",
+          "005-annotations",
+          "008-exceptions",
+          "061-out-of-memory",
+          "082-inline-execute",
+          "099-vmdebug",
+          "412-new-array",
+          "530-checker-lse2",
+          "534-checker-bce-deoptimization",
+          "550-new-instance-clinit",
+          "580-checker-round",
+          "594-invoke-super",
+          "625-checker-licm-regressions",
+          "626-const-class-linking",
+          // Crashes the VM, cause is unclear.
+          "080-oom-throw"
+      ),
+      DexVm.ART_5_1_1, ImmutableList.of(
+          // Generally fails on non R8/D8 running.
+          "004-checker-UnsafeTest18",
+          "004-NativeAllocations",
+          "005-annotations",
+          "008-exceptions",
+          "082-inline-execute",
+          "099-vmdebug",
+          "143-string-value",
+          "530-checker-lse2",
+          "536-checker-intrinsic-optimization",
+          "552-invoke-non-existent-super",
+          "580-checker-round",
+          "580-checker-string-fact-intrinsics",
+          "594-invoke-super",
+          "605-new-string-from-bytes",
+          "626-const-class-linking"
+      ),
+      DexVm.ART_4_4_4, ImmutableList.of()
+
+  );
+
+  // Tests where the R8/D8 output runs in Art but the original does not.
+  private static Multimap<String, TestCondition> failingRunWithArtOriginalOnly =
+      new ImmutableListMultimap.Builder<String, TestCondition>().build();
+
+  // Tests where the output of R8 fails when run with Art.
+  private static final Multimap<String, TestCondition> failingRunWithArt =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // This test relies on specific field access patterns, which we rewrite.
+          .put("064-field-access", TestCondition.match(TestCondition.R8_COMPILER))
+          // The growth limit test fails after processing by R8 because R8 will eliminate an
+          // "unneeded" const store. The following reflective call to the VM's GC will then see the
+          // large array as still live and the subsequent allocations will fail to reach the desired
+          // size before an out-of-memory error occurs. See:
+          // tests/art/{dx,jack}/104-growth-limit/src/Main.java:40
+          .put("104-growth-limit", TestCondition.match(TestCondition.R8_COMPILER))
+          .put(
+              "461-get-reference-vreg",
+              TestCondition.match(
+                  TestCondition.D8_COMPILER,
+                  TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // Unsatisfiable link error:
+          // libarttest.so: undefined symbol: _ZN3art6Thread18RunEmptyCheckpointEv
+          .put(
+              "543-env-long-ref",
+              TestCondition.match(
+                  TestCondition.D8_COMPILER,
+                  TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          // Regression test for an issue that is not fixed on version 5.1.1. Throws an Exception
+          // instance instead of the expected NullPointerException. This bug is only tickled when
+          // running the R8 generated code when starting from jar or from dex code generated with
+          // dx. However, the code that R8 generates is valid and there is nothing we can do for
+          // this one.
+          .put(
+              "551-implicit-null-checks",
+              TestCondition.match(
+                  TestCondition.tools(DexTool.NONE, DexTool.DX),
+                  TestCondition.R8_COMPILER,
+                  TestCondition.runtimes(DexVm.ART_5_1_1)))
+          // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an
+          // iput on a static field.
+          .put(
+              "600-verifier-fails",
+              TestCondition.match(
+                  TestCondition.D8_COMPILER,
+                  TestCondition.runtimes(DexVm.ART_7_0_0, DexVm.ART_6_0_1, DexVm.ART_5_1_1)))
+          .build();
+
+  // Tests where the output of R8/D8 runs in Art but produces different output than the expected.txt
+  // checked into the Art repo.
+  private static final Multimap<String, TestCondition> failingRunWithArtOutput =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // This one is expected to have different output. It counts instances, but the list that
+          // keeps the instances alive is dead and could be garbage collected. The compiler reuses
+          // the register for the list and therefore there are no live instances.
+          .put("099-vmdebug", TestCondition.any())
+          // This test relies on output on stderr, which we currently do not collect.
+          .put("143-string-value", TestCondition.any())
+          // This one is expected to have different output. It counts instances, but the list that
+          // keeps the instances alive is dead and could be garbage collected. The compiler reuses
+          // the register for the list and therefore there are no live instances.
+          .put("099-vmdebug", TestCondition.any())
+          // This test relies on output on stderr, which we currently do not collect.
+          .put("143-string-value", TestCondition.any())
+          .put(
+              "800-smali",
+              TestCondition.match(
+                  TestCondition.D8_COMPILER,
+                  TestCondition.runtimes(DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+          // Triggers regression test in 6.0.1 when using R8/D8 in debug mode.
+          .put(
+              "474-fp-sub-neg",
+              TestCondition.match(
+                  TestCondition.tools(DexTool.NONE, DexTool.JACK),
+                  TestCondition.D8_COMPILER,
+                  TestCondition.runtimes(DexVm.ART_6_0_1)))
+          .build();
+
+  private static final TestCondition beforeAndroidN =
+      TestCondition
+          .match(TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1));
+  private static final TestCondition beforeAndroidO =
+      TestCondition.match(
+          TestCondition.runtimes(
+              DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0));
+
+  // TODO(ager): Could we test that these fail in the way that we expect?
+  private static final Multimap<String, TestCondition> expectedToFailRunWithArt =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // Contains bad finalizer that times out.
+          .put("030-bad-finalizer", TestCondition.any())
+          // Contains a direct method call with a null receiver.
+          .put("034-call-null", TestCondition.any())
+          // Testing uncaught exceptions.
+          .put("054-uncaught", TestCondition.any())
+          // Contains a null pointer exception.
+          .put("038-inner-null", TestCondition.any())
+          // Null pointer exception.
+          .put("071-dexfile", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("088-monitor-verification", TestCondition.any())
+          // Attempts to run hprof.
+          .put("130-hprof", TestCondition.any())
+          // Null pointer exception. Test doesn't exist for dx.
+          .put("138-duplicate-classes-check", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("150-loadlibrary", TestCondition.any())
+          // Uses dex file version 37 and therefore only runs on Android N and above.
+          .put("370-dex-v37",
+              TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
+                  TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+          // Array index out of bounds exception.
+          .put("449-checker-bce", TestCondition.any())
+          // Fails: get_vreg_jni.cc:46] Check failed: value == 42u (value=314630384, 42u=42)
+          // The R8/D8 code does not produce values in the same registers as the tests expects in
+          // Main.testPairVReg where Main.doNativeCall is called (v1 vs v0).
+          .put("454-get-vreg", TestCondition.any())
+          // Fails: regs_jni.cc:42] Check failed: GetVReg(m, 0, kIntVReg, &value)
+          // The R8/D8 code does not put values in the same registers as the tests expects.
+          .put("457-regs", TestCondition.any())
+          // Class not found.
+          .put("529-checker-unresolved", TestCondition.any())
+          // Fails: env_long_ref.cc:44] Check failed: GetVReg(m, 1, kReferenceVReg, &value)
+          // The R8/D8 code does not produce values in the same registers as the tests expects in
+          // the stack frame for TestCase.testCase checked by the native Main.lookForMyRegisters
+          // (v1 vs v0).
+          .put("543-env-long-ref", TestCondition.match(TestCondition.R8_COMPILER))
+          // Array index out of bounds exception.
+          .put("555-UnsafeGetLong-regression", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("563-checker-fakestring", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("575-checker-string-init-alias", TestCondition.any())
+          // Runtime exception.
+          .put("577-profile-foreign-dex", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("602-deoptimizeable", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("604-hot-static-interface", TestCondition.any())
+          // Array index out of bounds exception.
+          .put("612-jit-dex-cache", TestCondition.any())
+          // Fails: get_reference_vreg_jni.cc:43] Check failed: GetVReg(m, 1, kReferenceVReg,
+          // &value)
+          // The R8 code does not put values in the same registers as the tests expects in
+          // Main.testThisWithInstanceCall checked by the native Main.doNativeCallRef (v0 vs. v1 and
+          // only 1 register instead fof 2).
+          .put("461-get-reference-vreg", TestCondition.match(TestCondition.R8_COMPILER))
+          // This test uses register r1 in method that is declared to only use 1 register (r0). This
+          // is in dex code which D8 does not convert. Therefore the error is a verification error
+          // at runtime and that is expected.
+          .put("142-classloader2", TestCondition.match(TestCondition.D8_COMPILER))
+          // Invoke-polymorphic is supported by D8 and R8, but it can only run on our newest version
+          // of art.
+          .put("953-invoke-polymorphic-compiler", beforeAndroidO)
+          .put("957-methodhandle-transforms", beforeAndroidO)
+          .put("958-methodhandle-stackframe", beforeAndroidO)
+          .put("959-invoke-polymorphic-accessors", beforeAndroidO)
+          .put("044-proxy", beforeAndroidN) // --min-sdk = 24
+          .put("048-reflect-v8", beforeAndroidN) // --min-sdk = 24
+          .put("962-iface-static", beforeAndroidN) // --min-sdk = 24
+          .put("964-default-iface-init-gen", beforeAndroidN) // --min-sdk = 24
+          .put("968-default-partial-compile-gen", beforeAndroidN) // --min-sdk = 24
+          .put("970-iface-super-resolution-gen", beforeAndroidN) // --min-sdk = 24
+          .put("971-iface-super", beforeAndroidN) // --min-sdk = 24
+          .put("972-default-imt-collision", beforeAndroidN) // --min-sdk = 24
+          .put("973-default-multidex", beforeAndroidN) // --min-sdk = 24
+          .put("974-verify-interface-super", beforeAndroidN) // --min-sdk = 24
+          .put("975-iface-private", beforeAndroidN) // --min-sdk = 24
+          // Uses dex file version 37 and therefore only runs on Android N and above.
+          .put("972-iface-super-multidex",
+              TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
+                  TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+          // Uses dex file version 37 and therefore only runs on Android N and above.
+          .put("978-virtual-interface",
+              TestCondition.match(TestCondition.tools(DexTool.JACK, DexTool.DX),
+                  TestCondition.runtimes(DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1)))
+          .build();
+
+  // Tests where code generation fails.
+  private static final Multimap<String, TestCondition> failingWithCompiler =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // Contains two methods with the same name and signature but different code.
+          .put("097-duplicate-method", TestCondition.any())
+          // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an
+          // iput on a static field.
+          .put("600-verifier-fails", TestCondition.match(TestCondition.R8_COMPILER))
+          // Contains a method that falls off the end without a return.
+          .put("606-erroneous-class", TestCondition
+              .match(TestCondition.tools(DexTool.DX, DexTool.JACK), TestCondition.R8_COMPILER))
+          .put("064-field-access", TestCondition
+              .match(TestCondition.tools(DexTool.NONE), TestCondition.D8_COMPILER))
+          .build();
+
+  // Tests that are invalid dex files and on which R8/D8 fails and that is OK.
+  private static final Multimap<String, TestCondition> expectedToFailWithCompiler =
+      new ImmutableListMultimap.Builder<String, TestCondition>()
+          // When starting from the Jar frontend we see the A$B class both from the Java source
+          // code and from the smali dex code. We reject that because there are then two definitions
+          // of the same class in the application. When running from the final dex files there is
+          // only on A$B class because of a custom build script that merges them.
+          .put("121-modifiers", TestCondition.match(TestCondition.tools(DexTool.NONE)))
+          // This test uses register r1 in method that is declared to only use 1 register (r0).
+          .put("142-classloader2", TestCondition.match(TestCondition.R8_COMPILER))
+          // This test uses an uninitialized register.
+          .put("471-uninitialized-locals", TestCondition.match(TestCondition.R8_COMPILER))
+          // This test is starting from invalid dex code. It splits up a double value and uses
+          // the first register of a double with the second register of another double.
+          .put("800-smali", TestCondition.match(TestCondition.R8_COMPILER))
+          // Contains a loop in the class hierarchy.
+          .put("804-class-extends-itself", TestCondition.any())
+          // It is not possible to compute target of method call due to ambiguous methods, thus fail
+          // to generate one dex from several dex inputs that represent an invalid program.
+          .put("004-JniTest", TestCondition.match(TestCondition.R8_COMPILER))
+          .put("960-default-smali", TestCondition.match(TestCondition.R8_COMPILER))
+          .put("966-default-conflict", TestCondition.match(TestCondition.R8_COMPILER))
+          .put("972-iface-super-multidex", TestCondition.match(TestCondition.R8_COMPILER))
+          .build();
+
+  // Tests that does not have valid input for us to be compatible with jack/dx running.
+  private static List<String> noInputJar = ImmutableList.of(
+      "064-field-access", // Missing classes2 dir (has src2)
+      "097-duplicate-method", // No input class files.
+      "606-erroneous-class", // Based on multiple smali files.
+      "630-safecast-array", // No input class files.
+      "801-VoidCheckCast", // No input class files.
+      "804-class-extends-itself", // No input class files.
+      "972-iface-super-multidex", // Based on multiple smali files
+      "973-default-multidex", // Based on multiple smali files.
+      "974-verify-interface-super", // No input class files.
+      "975-iface-private", // No input class files.
+      "976-conflict-no-methods", // No input class files
+      "978-virtual-interface" // No input class files
+  );
+
+  // Some JCTF test cases require test classes from other tests. These are listed here.
+  private static final Map<String, List<String>> jctfTestsExternalClassFiles =
+      new ImmutableMap.Builder<String, List<String>>()
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A13",
+              new ImmutableList.Builder<String>()
+                  .add("lang/Thread/stop/Thread_stop_A02.class")
+                  .add("lang/Thread/stopLjava_lang_Throwable/Thread_stop_A02.class")
+                  .build())
+          .put("lang.RuntimePermission.Class.RuntimePermission_class_A02",
+              new ImmutableList.Builder<String>()
+                  .add("lang/Class/getClassLoader/Class_getClassLoader_A03.class")
+                  .add("lang/ClassLoader/getParent/ClassLoader_getParent_A02.class")
+                  .add("lang/Thread/getContextClassLoader/Thread_getContextClassLoader_A02.class")
+                  .add("lang/Runtime/exitI/Runtime_exit_A02.class")
+                  .build())
+          .put("lang.Runtime.exitI.Runtime_exit_A03",
+              new ImmutableList.Builder<String>()
+                  .add("lang/Runtime/exitI/Runtime_exit_A02.class")
+                  .build())
+          .build();
+
+  private static List<String> failuresToTriage = ImmutableList.of();
+
+  private static class TestSpecification {
+
+    // Name of the Art test
+    private final String name;
+    // Directory of the test files (containing prebuild dex/jar files and expected output).
+    private final File directory;
+    // Compiler that these expectations are for dx or Jack, or none if running on class files.
+    private final DexTool dexTool;
+    // Native library to use for running this test - if any.
+    private final String nativeLibrary;
+    // Skip running this test with Art - most likely due to timeout.
+    private final boolean skipArt;
+    // Skip running this test altogether. For example, there might be no input in this configuration
+    // (e.g. no class files).
+    private final boolean skipTest;
+    // Fails compilation - most likely throws an exception.
+    private final boolean failsWithX8;
+    // Expected to fail compilation with a CompilationError.
+    private final boolean expectedToFailWithX8;
+    // Fails running the output in Art with an assertion error. Typically due to verification
+    // errors.
+    private final boolean failsWithArt;
+    // Runs in art but fails the run because it produces different results.
+    private final boolean failsWithArtOutput;
+    // Original fails in art but the R8/D8 version can run in art.
+    private final boolean failsWithArtOriginalOnly;
+    // Test might produce different outputs.
+    private final boolean outputMayDiffer;
+
+    TestSpecification(String name, DexTool dexTool,
+        File directory, boolean skipArt, boolean skipTest, boolean failsWithX8,
+        boolean failsWithArt, boolean failsWithArtOutput, boolean failsWithArtOriginalOnly,
+        String nativeLibrary, boolean expectedToFailWithX8, boolean outputMayDiffer) {
+      this.name = name;
+      this.dexTool = dexTool;
+      this.nativeLibrary = nativeLibrary;
+      this.directory = directory;
+      this.skipArt = skipArt;
+      this.skipTest = skipTest;
+      this.failsWithX8 = failsWithX8;
+      this.failsWithArt = failsWithArt;
+      this.failsWithArtOutput = failsWithArtOutput;
+      this.failsWithArtOriginalOnly = failsWithArtOriginalOnly;
+      this.expectedToFailWithX8 = expectedToFailWithX8;
+      this.outputMayDiffer = outputMayDiffer;
+    }
+
+    TestSpecification(String name, DexTool dexTool, File directory, boolean skipArt,
+        boolean failsWithArt) {
+      this(name, dexTool, directory, skipArt,
+          false, false, failsWithArt, false, false, null, false, false);
+    }
+
+    public File resolveFile(String name) {
+      return directory.toPath().resolve(name).toFile();
+    }
+
+    public String toString() {
+      return name + " (" + dexTool + ")";
+    }
+  }
+
+  private static class SpecificationKey {
+
+    private final String name;
+    private final DexTool toolchain;
+
+    private SpecificationKey(String name, DexTool toolchain) {
+      assert name != null;
+      this.name = name;
+      this.toolchain = toolchain;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof SpecificationKey)) {
+        return false;
+      }
+
+      SpecificationKey that = (SpecificationKey) o;
+      return name.equals(that.name) && toolchain == that.toolchain;
+    }
+
+    @Override
+    public int hashCode() {
+      return 31 * name.hashCode() + toolchain.hashCode();
+    }
+  }
+
+  private static Map<SpecificationKey, TestSpecification> AVAILABLE_TESTS_MAP_R8 =
+      data(CompilerUnderTest.R8);
+
+  private static Map<SpecificationKey, TestSpecification> AVAILABLE_TESTS_MAP_D8 =
+      data(CompilerUnderTest.D8);
+
+
+  private static Set<String> collectTestsMatchingConditions(DexTool dexTool,
+      CompilerUnderTest compilerUnderTest, DexVm dexVm,
+      Multimap<String, TestCondition> testConditionsMap) {
+    Set<String> set = Sets.newHashSet();
+    for (Map.Entry<String, TestCondition> kv : testConditionsMap.entries()) {
+      if (kv.getValue().test(dexTool, compilerUnderTest, dexVm)) {
+        set.add(kv.getKey());
+      }
+    }
+    return set;
+  }
+
+
+  private static Map<SpecificationKey, TestSpecification> data(
+      CompilerUnderTest compilerUnderTest) {
+    File artTestDir = new File(ART_TESTS_DIR);
+    if (!artTestDir.exists()) {
+      // Don't run any tests if the directory does not exist.
+      return Collections.emptyMap();
+    }
+    Map<SpecificationKey, TestSpecification> data = new HashMap<>();
+
+    // Collect tests where running Art is skipped (we still run R8/D8 on these).
+    Set<String> skipArt = Sets.newHashSet(flakyRunWithArt);
+    skipArt.addAll(customRun);
+
+    Set<String> skipTest = Sets.newHashSet(skipAltogether);
+    skipTest.addAll(usesJava8);
+    skipTest.addAll(usesNativeAgentCode);
+    skipTest.addAll(failuresToTriage);
+
+    // Collect the tests requiring the native library.
+    Set<String> useNativeLibrary = Sets.newHashSet(useJNI);
+
+    DexVm dexVm = ToolHelper.getDexVm();
+    for (DexTool dexTool : DexTool.values()) {
+      // Collect the tests failing code generation.
+      Set<String> failsWithCompiler = collectTestsMatchingConditions(dexTool, compilerUnderTest,
+          dexVm, failingWithCompiler);
+
+      // Collect tests that has no input:
+      if (dexTool == DexTool.NONE) {
+        skipTest.addAll(noInputJar);
+      }
+
+      // Collect the test that we should skip in this configuration.
+      skipArt.addAll(collectTestsMatchingConditions(dexTool, compilerUnderTest,
+          dexVm, timeoutOrSkipRunWithArt));
+
+      // Collect the tests failing to run in Art (we still run R8/D8 on these).
+      Set<String> failsWithArt = collectTestsMatchingConditions(dexTool, compilerUnderTest, dexVm,
+          failingRunWithArt);
+      {
+        Set<String> tmpSet = collectTestsMatchingConditions(dexTool, compilerUnderTest, dexVm,
+            expectedToFailRunWithArt);
+        failsWithArt.addAll(tmpSet);
+      }
+
+      if (!ToolHelper.isDefaultDexVm()) {
+        // Generally failing when not TOT art.
+        failsWithArt.addAll(expectedToFailRunWithArtNonDefault);
+        // Version specific failures
+        failsWithArt.addAll(expectedToFailRunWithArtVersion.get(ToolHelper.getDexVm()));
+      }
+
+      // Collect the tests failing with output differences in Art.
+      Set<String> failsRunWithArtOutput = collectTestsMatchingConditions(dexTool, compilerUnderTest,
+          dexVm, failingRunWithArtOutput);
+      Set<String> expectedToFailWithCompilerSet = collectTestsMatchingConditions(dexTool,
+          compilerUnderTest, dexVm, expectedToFailWithCompiler);
+
+      // Collect the tests where the original works in Art and the R8/D8 generated output does not.
+      Set<String> failsRunWithArtOriginalOnly = collectTestsMatchingConditions(dexTool,
+          compilerUnderTest, dexVm, failingRunWithArtOriginalOnly);
+
+      File compilerTestDir = artTestDir.toPath().resolve(dexToolDirectory(dexTool)).toFile();
+      File[] testDirs = compilerTestDir.listFiles();
+      assert testDirs != null;
+      for (File testDir : testDirs) {
+        String name = testDir.getName();
+        // All the native code for all Art tests is currently linked into the
+        // libarttest.so file.
+        data.put(
+            new SpecificationKey(name, dexTool),
+            new TestSpecification(name, dexTool, testDir, skipArt.contains(name),
+                skipTest.contains(name),
+                failsWithCompiler.contains(name),
+                failsWithArt.contains(name),
+                failsRunWithArtOutput.contains(name),
+                failsRunWithArtOriginalOnly.contains(name),
+                useNativeLibrary.contains(name) ? "arttest" : null,
+                expectedToFailWithCompilerSet.contains(name),
+                outputMayDiffer.contains(name)));
+      }
+    }
+    return data;
+  }
+
+  private static String dexToolDirectory(DexTool tool) {
+    // DexTool.NONE uses class files in the dx directory.
+    return tool == DexTool.JACK ? "jack" : "dx";
+  }
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  public R8RunArtTestsTest(String name, DexTool toolchain) {
+    this.name = name;
+    this.toolchain = toolchain;
+  }
+
+  private ArtCommandBuilder buildArtCommand(
+      File dexFile, TestSpecification specification, DexVm artVersion) {
+    ArtCommandBuilder builder = new ArtCommandBuilder(artVersion);
+    builder.appendClasspath(dexFile.toString());
+    // All Art tests have the main class Main.
+    builder.setMainClass("Main");
+    if (specification.nativeLibrary != null) {
+      // All the native libraries for all Art tests is in the same directory.
+      File artTestNativeLibraryDir = new File(ART_TESTS_NATIVE_LIBRARY_DIR);
+      builder.appendArtSystemProperty(
+          "java.library.path",
+          artTestNativeLibraryDir.getAbsolutePath());
+      builder.appendProgramArgument(specification.nativeLibrary);
+    }
+    return builder;
+  }
+
+  protected void runArtTest() throws Throwable {
+    // Use the default dex VM specified.
+    runArtTest(ToolHelper.getDexVm(), CompilerUnderTest.R8);
+  }
+
+  protected void runArtTest(CompilerUnderTest compilerUnderTest) throws Throwable {
+    // Use the default dex VM specified.
+    runArtTest(ToolHelper.getDexVm(), compilerUnderTest);
+  }
+
+  private void executeCompilerUnderTest(
+      CompilerUnderTest compilerUnderTest, Collection<String> fileNames, String resultPath)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, null);
+  }
+
+  private void executeCompilerUnderTest(
+      CompilerUnderTest compilerUnderTest,
+      Collection<String> fileNames,
+      String resultPath,
+      String keepRulesFile)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    switch (compilerUnderTest) {
+      case D8: {
+        assert keepRulesFile == null : "Keep-rules file specified for D8.";
+        D8Command.Builder builder =
+            D8Command.builder()
+                .setMode(CompilationMode.DEBUG)
+                .addProgramFiles(ListUtils.map(fileNames, Paths::get));
+        Integer minSdkVersion = needMinSdkVersion.get(name);
+        if (minSdkVersion != null) {
+          builder.setMinApiLevel(minSdkVersion);
+        }
+        D8Output output = D8.run(builder.build());
+        output.write(Paths.get(resultPath));
+        break;
+      }
+      case R8: {
+        R8Command.Builder builder =
+            R8Command.builder()
+                .setMode(CompilationMode.RELEASE)
+                .setOutputPath(Paths.get(resultPath))
+                .addProgramFiles(ListUtils.map(fileNames, Paths::get))
+                .setIgnoreMissingClasses(true);
+        Integer minSdkVersion = needMinSdkVersion.get(name);
+        if (minSdkVersion != null) {
+          builder.setMinApiLevel(minSdkVersion);
+        }
+        if (keepRulesFile != null) {
+          builder.addProguardConfigurationFiles(Paths.get(keepRulesFile));
+        }
+        // Add internal flags for testing purposes.
+        ToolHelper.runR8(builder.build(), options -> {
+          if (enableInterfaceMethodDesugaring.contains(name)) {
+            options.interfaceMethodDesugaring = OffOrAuto.Auto;
+          }
+        });
+        break;
+      }
+      default:
+        assert false : compilerUnderTest;
+    }
+  }
+
+  private static R8Command.Builder setDefaultArgs(R8Command.Builder builder) {
+    return builder.setMinification(false);
+  }
+
+  private static final boolean isAuxClassFile(String fileName, String auxClassFileBase) {
+    return fileName.endsWith(".class")
+        && (fileName.startsWith(auxClassFileBase + "$")
+        || fileName.startsWith(auxClassFileBase + "_"));
+  }
+
+
+  private ArrayList<File> getJctfTestAuxClassFiles(File classFile) {
+    // Collect additional files from the same directory with file names like
+    // <dir>/<filename_wo_ext>$*.class and <dir>/<filename_wo_ext>_*.class
+    String classFileString = classFile.toString();
+    assert classFileString.endsWith(".class");
+
+    String auxClassFileBase =
+        new File(
+            classFileString.substring(0, classFileString.length() - ".class".length()))
+            .getName();
+
+    ArrayList<File> auxClassFiles = new ArrayList<>();
+
+    File[] files = classFile.getParentFile()
+        .listFiles(
+            (File file) -> isAuxClassFile(file.getName(), auxClassFileBase));
+    if (files != null) {
+      auxClassFiles.addAll(Arrays.asList(files));
+    }
+
+    if (auxClassFileBase.matches(".*[A-Z]\\d\\d")) {
+      // Also collect all the files in this directory that doesn't match this pattern
+      // They will be helper classes defined in one of the test class files but we don't know in
+      // which one, so we just add them to all tests.
+      final int SUFFIX_LENGTH_TO_STRIP = 3; // one letter (usually 'A' and two digits)
+      String testClassFilePattern =
+          auxClassFileBase.substring(0, auxClassFileBase.length() - SUFFIX_LENGTH_TO_STRIP)
+              + "[A-Z]\\d\\d.*\\.class";
+      files = classFile.getParentFile()
+          .listFiles(
+              (File file) -> file.getName().matches(".*\\.class") && !file.getName()
+                  .matches(testClassFilePattern));
+      if (files != null) {
+        auxClassFiles.addAll(Arrays.asList(files));
+      }
+    }
+
+    return auxClassFiles;
+  }
+
+  protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
+      String fullClassName)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+
+    DexVm dexVm = ToolHelper.getDexVm();
+
+    File resultDir = temp.getRoot();
+
+    JctfTestSpecifications.Outcome expectedOutcome = JctfTestSpecifications
+        .getExpectedOutcome(name, compilerUnderTest, dexVm);
+    TestSpecification specification = new TestSpecification(name, DexTool.NONE, resultDir,
+        expectedOutcome == JctfTestSpecifications.Outcome.TIMEOUTS_WITH_ART
+            || expectedOutcome == JctfTestSpecifications.Outcome.FLAKY_WITH_ART,
+        expectedOutcome == JctfTestSpecifications.Outcome.FAILS_WITH_ART);
+
+    if (specification.skipTest) {
+      return;
+    }
+
+    File classFile = new File(JCTF_TESTS_PREFIX + "/" + classFilePath);
+    if (!classFile.exists()) {
+      throw new FileNotFoundException(
+          "Class file for Jctf test not found: \"" + classFile.toString() + "\".");
+    }
+
+    ArrayList<File> classFiles = new ArrayList<>();
+    classFiles.add(classFile);
+
+    // some tests need files from other tests
+    int langIndex = fullClassName.indexOf(".java.");
+    assert langIndex >= 0;
+    List<String> externalClassFiles = jctfTestsExternalClassFiles
+        .get(fullClassName.substring(langIndex + ".java.".length()));
+
+    if (externalClassFiles != null) {
+      for (String s : externalClassFiles) {
+        classFiles.add(new File(JCTF_TESTS_LIB_PREFIX + "/java/" + s));
+      }
+    }
+
+    ArrayList<File> allClassFiles = new ArrayList<>();
+
+    for (File f : classFiles) {
+      allClassFiles.add(f);
+      allClassFiles.addAll(getJctfTestAuxClassFiles(f));
+    }
+
+    File jctfCommonFile = new File(JCTF_COMMON_JAR);
+    if (!jctfCommonFile.exists()) {
+      throw new FileNotFoundException(
+          "Jar file of Jctf tests common code not found: \"" + jctfCommonFile.toString() + "\".");
+    }
+
+    File junitFile = new File(JUNIT_JAR);
+    if (!junitFile.exists()) {
+      throw new FileNotFoundException(
+          "Junit Jar not found: \"" + junitFile.toString() + "\".");
+    }
+
+    File hamcrestFile = new File(HAMCREST_JAR);
+    if (!hamcrestFile.exists()) {
+      throw new FileNotFoundException(
+          "Hamcrest Jar not found: \"" + hamcrestFile.toString() + "\".");
+    }
+
+    // allClassFiles may contain duplicated files, that's why the HashSet
+    Set<String> fileNames = new HashSet<>();
+
+    fileNames.addAll(Arrays.asList(
+        jctfCommonFile.getCanonicalPath(),
+        junitFile.getCanonicalPath(),
+        hamcrestFile.getCanonicalPath()
+    ));
+
+    for (File f : allClassFiles) {
+      fileNames.add(f.getCanonicalPath());
+    }
+    executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+
+    File processedFile;
+
+    // Collect the generated dex files.
+    File[] outputFiles =
+        resultDir.listFiles((File file) -> file.getName().endsWith(".dex"));
+    if (outputFiles.length == 1) {
+      // Just run Art on classes.dex.
+      processedFile = outputFiles[0];
+    } else {
+      // Run Art on JAR file with multiple dex files.
+      processedFile
+          = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile();
+      JarBuilder.buildJar(outputFiles, processedFile);
+    }
+
+    boolean compileOnly = System.getProperty("jctf_compile_only", "0").equals("1");
+    if (compileOnly || specification.skipArt) {
+      // verify dex code instead of running it
+      Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+      ToolHelper.runDex2Oat(processedFile.toPath(), oatFile);
+      return;
+    }
+
+    ArtCommandBuilder builder = buildArtCommand(processedFile, specification, dexVm);
+    builder.appendArtOption("-Ximage:/system/non/existent/jdwp/image.art");
+    for (String s : ToolHelper.getArtBootLibs()) {
+      builder.appendBootClassPath(new File(s).getCanonicalPath());
+    }
+    builder.setMainClass(JUNIT_TEST_RUNNER);
+    builder.appendProgramArgument(fullClassName);
+
+    if (specification.failsWithArt) {
+      thrown.expect(AssertionError.class);
+    }
+
+    try {
+      ToolHelper.runArt(builder);
+    } catch (AssertionError e) {
+      addDexInformationToVerificationError(fileNames, processedFile,
+          specification.resolveFile("classes.dex"), e);
+      throw e;
+    }
+    if (specification.failsWithArt) {
+      System.err.println("Should have failed run with art");
+      return;
+    }
+  }
+
+  protected void runArtTest(DexVm version, CompilerUnderTest compilerUnderTest)
+      throws Throwable {
+    Map<SpecificationKey, TestSpecification> specificationMap = null;
+
+    if (compilerUnderTest == CompilerUnderTest.R8) {
+      specificationMap = AVAILABLE_TESTS_MAP_R8;
+    } else {
+      assert (compilerUnderTest == CompilerUnderTest.D8);
+      specificationMap = AVAILABLE_TESTS_MAP_D8;
+    }
+
+    TestSpecification specification = specificationMap.get(new SpecificationKey(name, toolchain));
+
+    if (specification == null) {
+      throw new RuntimeException("Test " + name + " has no specification for toolchain"
+          + toolchain + ".");
+    }
+
+    if (specification.skipTest) {
+      return;
+    }
+
+    File[] inputFiles;
+    if (toolchain == DexTool.NONE) {
+      File classes = new File(specification.directory, "classes");
+      inputFiles =
+          Files.fileTreeTraverser().breadthFirstTraversal(classes).filter(
+              (File f) -> !f.isDirectory()).toArray(File.class);
+      File smali = new File(specification.directory, "smali");
+      if (smali.exists()) {
+        File smaliDex = new File(smali, "out.dex");
+        assert smaliDex.exists();
+        inputFiles = ObjectArrays.concat(inputFiles, smaliDex);
+      }
+      File classes2 = new File(specification.directory, "classes2");
+      if (classes2.exists()) {
+        inputFiles = ObjectArrays.concat(inputFiles,
+            Files.fileTreeTraverser().breadthFirstTraversal(classes2).filter(
+                (File f) -> !f.isDirectory()).toArray(File.class), File.class);
+      }
+    } else {
+      inputFiles =
+          specification.directory.listFiles((File file) -> file.getName().endsWith(".dex"));
+    }
+    List<String> fileNames = new ArrayList<>();
+    for (File file : inputFiles) {
+      fileNames.add(file.getCanonicalPath());
+    }
+    File resultDir = temp.getRoot();
+
+    if (specification.expectedToFailWithX8) {
+      thrown.expect(CompilationError.class);
+      try {
+        executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+      } catch (CompilationException e) {
+        throw new CompilationError(e.getMessage(), e);
+      } catch (ExecutionException e) {
+        throw e.getCause();
+      }
+      System.err.println("Should have failed R8/D8 compilation with a CompilationError.");
+      return;
+    } else if (specification.failsWithX8) {
+      thrown.expect(Throwable.class);
+      executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+      System.err.println("Should have failed R8/D8 compilation with an exception.");
+      return;
+    } else {
+      executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getCanonicalPath());
+    }
+
+    if (!specification.skipArt && ToolHelper.artSupported()) {
+      File originalFile;
+      File processedFile;
+
+      // Collect the generated dex files.
+      File[] outputFiles =
+          resultDir.listFiles((File file) -> file.getName().endsWith(".dex"));
+      if (outputFiles.length == 1) {
+        // Just run Art on classes.dex.
+        processedFile = outputFiles[0];
+      } else {
+        // Run Art on JAR file with multiple dex files.
+        processedFile
+            = temp.getRoot().toPath().resolve(specification.name + ".jar").toFile();
+        JarBuilder.buildJar(outputFiles, processedFile);
+      }
+
+      File expectedFile = specification.resolveFile("expected.txt");
+      String expected = Files.toString(expectedFile, Charsets.UTF_8);
+      if (specification.failsWithArt) {
+        thrown.expect(AssertionError.class);
+      }
+
+      ArtCommandBuilder builder = buildArtCommand(processedFile, specification, version);
+      String output;
+      try {
+        output = ToolHelper.runArt(builder);
+      } catch (AssertionError e) {
+        addDexInformationToVerificationError(fileNames, processedFile,
+            specification.resolveFile("classes.dex"), e);
+        throw e;
+      }
+      if (specification.failsWithArt) {
+        System.err.println("Should have failed run with art");
+        return;
+      }
+
+      File checkCommand = specification.resolveFile("check");
+      if (checkCommand.exists()) {
+        // Run the Art test custom check command.
+        File actualFile = temp.newFile();
+        Files.asByteSink(actualFile).write(output.getBytes(Charsets.UTF_8));
+        ProcessBuilder processBuilder = new ProcessBuilder();
+        processBuilder.command(
+            specification.resolveFile("check").toString(), expectedFile.toString(),
+            actualFile.toString());
+        ProcessResult result = ToolHelper.runProcess(processBuilder);
+        if (result.exitCode != 0 && !specification.failsWithArtOutput) {
+          System.err.println("ERROR: check script failed. Building comparison of dex files");
+          failWithDexDiff(specification.resolveFile("classes.dex"), processedFile);
+        }
+      } else {
+        if (!expected.equals(output)) {
+          // The expected.txt in the Android repository might not match what our version of Art
+          // produces.
+          originalFile = specification.resolveFile(specification.name + ".jar");
+          if (specification.failsWithArtOriginalOnly) {
+            thrown.expect(AssertionError.class);
+          }
+          builder = buildArtCommand(originalFile, specification, version);
+          expected = ToolHelper.runArt(builder);
+          if (specification.failsWithArtOriginalOnly) {
+            System.err.println("Original file should have failed run with art");
+            return;
+          }
+        }
+        if (specification.failsWithArtOutput) {
+          thrown.expect(ComparisonFailure.class);
+        }
+        if (!specification.outputMayDiffer) {
+          assertEquals(expected, output);
+        }
+      }
+    }
+  }
+
+  private void failWithDexDiff(File originalFile, File processedFile)
+      throws IOException, ExecutionException {
+    DexInspector inspectOriginal =
+        new DexInspector(originalFile.toPath().toAbsolutePath());
+    DexInspector inspectProcessed =
+        new DexInspector(processedFile.toPath().toAbsolutePath());
+    StringBuilder builderOriginal = new StringBuilder();
+    StringBuilder builderProcessed = new StringBuilder();
+    inspectOriginal.forAllClasses((clazz) -> builderOriginal.append(clazz.dumpMethods()));
+    inspectProcessed.forAllClasses((clazz) -> builderProcessed.append(clazz.dumpMethods()));
+    assertEquals(builderOriginal.toString(), builderProcessed.toString());
+    fail();
+  }
+
+  private void addDexInformationToVerificationError(
+      Collection<String> inputFiles, File processedFile, File referenceFile,
+      AssertionError verificationError) {
+    List<ComparisonFailure> errors;
+    try {
+      // Parse all the verification errors.
+      DexInspector processed = new DexInspector(processedFile.toPath());
+      DexInspector original = DEX_COMPARE_WITH_DEX_REFERENCE_ON_FAILURE
+          ? new DexInspector(referenceFile.toPath())
+          : new DexInspector(inputFiles.stream().map(Paths::get).collect(Collectors.toList()));
+      List<ArtErrorInfo> errorInfo = ArtErrorParser.parse(verificationError.getMessage());
+      errors = ListUtils.map(errorInfo, (error) ->
+          new ComparisonFailure(
+              error.getMessage(),
+              "ORIGINAL\n" + error.dump(original, false) + "\nEND ORIGINAL",
+              "PROCESSED\n" + error.dump(processed, true) + "\nEND PROCESSED"));
+    } catch (Throwable e) {
+      System.err.println("Failed to add extra dex information to the verification error:");
+      e.printStackTrace();
+      throw verificationError;
+    }
+
+    // If we failed to annotate anything, rethrow the original exception.
+    if (errors.isEmpty()) {
+      throw verificationError;
+    }
+
+    // Otherwise, we print each error and throw the last one, since Intellij only supports nice
+    // comparison-diff if thrown and we can only throw one :-(
+    System.err.println(verificationError.getMessage());
+    for (ComparisonFailure error : errors.subList(0, errors.size() - 1)) {
+      System.err.println(error.toString());
+    }
+    throw errors.get(errors.size() - 1);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
new file mode 100644
index 0000000..1335970
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidNTest.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class R8RunExamplesAndroidNTest {
+
+  private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR;
+
+  private static Map<DexVm, List<String>> failsOn =
+      ImmutableMap.of(
+          DexVm.ART_4_4_4,
+          ImmutableList.of(
+              // Dex version not supported
+              "staticinterfacemethods", "defaultmethods"),
+          DexVm.ART_5_1_1,
+          ImmutableList.of(
+              // Dex version not supported
+              "staticinterfacemethods", "defaultmethods"),
+          DexVm.ART_6_0_1,
+          ImmutableList.of(
+              // Dex version not supported
+              "staticinterfacemethods", "defaultmethods"),
+          DexVm.ART_7_0_0,
+          ImmutableList.of(),
+          DexVm.ART_DEFAULT,
+          ImmutableList.of());
+
+  @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Rule public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void staticInterfaceMethods() throws Throwable {
+    doTest(
+        "staticinterfacemethods",
+        "interfacemethods",
+        "StaticInterfaceMethods",
+        Constants.ANDROID_N_API,
+        options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
+  }
+
+  @Test
+  public void staticInterfaceMethodsErrorDueToMinSdk() throws Throwable {
+    thrown.expect(CompilationError.class);
+    doTest(
+        "staticinterfacemethods-error-due-to-min-sdk",
+        "interfacemethods",
+        "StaticInterfaceMethods");
+  }
+
+  @Test
+  public void defaultMethods() throws Throwable {
+    doTest(
+        "defaultmethods",
+        "interfacemethods",
+        "DefaultMethods",
+        Constants.ANDROID_N_API,
+        options -> options.interfaceMethodDesugaring = OffOrAuto.Auto);
+  }
+
+  @Test
+  public void defaultMethodsErrorDueToMinSdk() throws Throwable {
+    thrown.expect(CompilationError.class);
+    doTest("defaultmethods-error-due-to-min-sdk", "interfacemethods", "DefaultMethods");
+  }
+
+  private void doTest(String testName, String packageName, String className) throws Throwable {
+    doTest(testName, packageName, className, R8Command.builder(), options -> {});
+  }
+
+  private void doTest(
+      String testName,
+      String packageName,
+      String className,
+      int minSdk,
+      Consumer<InternalOptions> optionsConsumer)
+      throws Throwable {
+    doTest(
+        testName,
+        packageName,
+        className,
+        R8Command.builder().setMinApiLevel(minSdk),
+        optionsConsumer);
+  }
+
+  public void doTest(
+      String testName,
+      String packageName,
+      String className,
+      R8Command.Builder builder,
+      Consumer<InternalOptions> optionsConsumer)
+      throws Throwable {
+    String mainClass = packageName + "." + className;
+    Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+    Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+    try {
+      ToolHelper.runR8(
+          builder.addProgramFiles(inputFile).setOutputPath(out).build(), optionsConsumer);
+    } catch (ExecutionException e) {
+      throw e.getCause();
+    }
+
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+
+    boolean expectedToFail = false;
+    if (failsOn.containsKey(ToolHelper.getDexVm())
+        && failsOn.get(ToolHelper.getDexVm()).contains(testName)) {
+      expectedToFail = true;
+      thrown.expect(Throwable.class);
+    }
+    String output = ToolHelper.runArtNoVerificationErrors(out.toString(), mainClass);
+    if (!expectedToFail) {
+      ProcessResult javaResult =
+          ToolHelper.runJava(ImmutableList.of(inputFile.toString()), mainClass);
+      assertEquals("JVM run failed", javaResult.exitCode, 0);
+      assertTrue(
+          "JVM output does not match art output.\n\tjvm: "
+              + javaResult.stdout
+              + "\n\tart: "
+              + output,
+          output.equals(javaResult.stdout));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
new file mode 100644
index 0000000..6c45f40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.dex.Constants;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.function.UnaryOperator;
+import org.junit.Test;
+
+public class R8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<R8Command.Builder> {
+
+  private static Map<DexVm, List<String>> alsoFailsOn =
+      ImmutableMap.of(
+          DexVm.ART_4_4_4, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.ART_5_1_1, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.ART_6_0_1, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.ART_7_0_0, ImmutableList.of(
+              "invokecustom-with-shrinking"
+          ),
+          DexVm.ART_DEFAULT, ImmutableList.of(
+          )
+      );
+
+  @Test
+  public void invokeCustomWithShrinking() throws Throwable {
+    test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
+        .withMinApiLevel(Constants.ANDROID_O_API)
+        .withBuilderTransformation(builder ->
+            builder.addProguardConfigurationFiles(
+                Paths.get(ToolHelper.EXAMPLES_ANDROID_O_DIR, "invokecustom/keep-rules.txt")))
+        .run();
+  }
+
+  class R8TestRunner extends TestRunner {
+
+    R8TestRunner(String testName, String packageName, String mainClass) {
+      super(testName, packageName, mainClass);
+    }
+
+    @Override
+    TestRunner withMinApiLevel(int minApiLevel) {
+      return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel));
+    }
+
+    @Override
+    void build(Path inputFile, Path out) throws Throwable {
+      try {
+        R8Command.Builder builder = R8Command.builder();
+        for (UnaryOperator<R8Command.Builder> transformation : builderTransformations) {
+          builder = transformation.apply(builder);
+        }
+        R8Command command = builder.addProgramFiles(inputFile).setOutputPath(out).build();
+        ToolHelper.runR8(command, this::combinedOptionConsumer);
+      } catch (ExecutionException e) {
+        throw e.getCause();
+      }
+    }
+  }
+
+  @Override
+  TestRunner test(String testName, String packageName, String mainClass) {
+    return new R8TestRunner(testName, packageName, mainClass);
+  }
+
+  @Override
+  boolean expectedToFail(String name) {
+    return super.expectedToFail(name) || failsOn(alsoFailsOn, name);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
new file mode 100644
index 0000000..f72694b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -0,0 +1,255 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.JarBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8RunExamplesTest {
+
+  private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
+  private static final String JAR_EXTENSION = ".jar";
+  private static final String DEX_EXTENSION = ".dex";
+  private static final String DEFAULT_DEX_FILENAME = "classes.dex";
+
+  private static final String[] failsWithJar = {};
+
+  // For local testing on a specific Art version(s) change this set. e.g. to
+  // ImmutableSet.of("default") or pass the option -Ddex_vm=<version> to the Java VM.
+  private static Set<DexVm> artVersions = ToolHelper.getArtVersions();
+
+  // A set of class names for examples that might produce bigger output and thus are excluded from
+  // size testing.
+  private static Set<String> mayBeBigger = ImmutableSet.of(
+      // Contains a reference to an extra type due to member rebinding.
+      "throwing.Throwing"
+  );
+
+  @Parameters(name = "{0}{1}")
+  public static Collection<String[]> data() {
+    String[][] tests = {
+        {"arithmetic.Arithmetic", null},
+        {"arrayaccess.ArrayAccess", "37=37"},
+        {"barray.BArray", "bits 42 and bool true"},
+        {"bridge.BridgeMethod", null},
+        {"cse.CommonSubexpressionElimination", "1\n1\n2 2\n2\n3\n3\n4 4\n4\nA\nB\n"},
+        {"constants.Constants", null},
+        {"controlflow.ControlFlow", null},
+        {"conversions.Conversions", null},
+        {"floating_point_annotations.FloatingPointValuedAnnotationTest", null},
+        {"filledarray.FilledArray", null},
+        {"hello.Hello", "Hello, world"},
+        {"ifstatements.IfStatements", null},
+        {"instancevariable.InstanceVariable", "144=144"},
+        {"instanceofstring.InstanceofString", "is-string:true"},
+        {"invoke.Invoke", null},
+        {"jumbostring.JumboString", null},
+        {"loadconst.LoadConst", null},
+        {"newarray.NewArray", null},
+        {"regalloc.RegAlloc", null},
+        {"returns.Returns", null},
+        {"staticfield.StaticField", "101010\n101010\nABC\nABC\n"},
+        {"stringbuilding.StringBuilding",
+            "a2c-xyz-abc7xyz\ntrueABCDE1234232.21.101an Xstringbuilder"},
+        {"switches.Switches", null},
+        {"sync.Sync", null},
+        {"throwing.Throwing", "Throwing"},
+        {"trivial.Trivial", null},
+        {"trycatch.TryCatch", "Success!"},
+        {"trycatchmany.TryCatchMany", "Success!"},
+        {"invokeempty.InvokeEmpty", "AB"},
+        {"regress.Regress", null},
+        {"regress2.Regress2", "START\nLOOP\nLOOP\nLOOP\nLOOP\nLOOP\nEND"},
+        {"regress_37726195.Regress", null},
+        {"regress_37658666.Regress", null},
+        {"regress_37875803.Regress", null},
+        {"regress_37955340.Regress", null},
+        {"memberrebinding2.Test", Integer.toString((8 * 9) / 2)},
+        {"memberrebinding3.Test", null},
+        {"minification.Minification", null},
+        {"enclosingmethod.Main", null},
+    };
+
+    List<String[]> fullTestList = new ArrayList<>(tests.length * 2);
+    for (String[] test : tests) {
+      String qualified = test[0];
+      String pkg = qualified.substring(0, qualified.lastIndexOf('.'));
+      fullTestList.add(new String[]{pkg, DEX_EXTENSION, qualified, test[1]});
+      fullTestList.add(new String[]{pkg, JAR_EXTENSION, qualified, test[1]});
+    }
+    return fullTestList;
+  }
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+  private final String name;
+
+  private final String mainClass;
+  private final String expectedOutput;
+  private final String fileType;
+  private static Map<DexVm, List<String>> failsOn =
+      ImmutableMap.of(
+          DexVm.ART_4_4_4, ImmutableList.of(
+              "vmdebug.dex",
+              "vmdebug.jar",
+              "memberrebinding2.dex", // b/38187737
+              "memberrebinding2.jar" // b/38187737
+          ),
+          DexVm.ART_5_1_1, ImmutableList.of(
+              "vmdebug.dex",
+              "vmdebug.jar",
+              "memberrebinding2.dex", // b/38187737
+              "memberrebinding2.jar" // b/38187737
+          ),
+          DexVm.ART_6_0_1, ImmutableList.of(
+              "vmdebug.dex",
+              "vmdebug.jar",
+              "memberrebinding2.dex", // b/38187737
+              "memberrebinding2.jar" // b/38187737
+          ),
+          DexVm.ART_7_0_0, ImmutableList.of(
+              "memberrebinding2.dex", // b/38187737
+              "memberrebinding2.jar" // b/38187737
+          ),
+          DexVm.ART_DEFAULT, ImmutableList.of(
+              "memberrebinding2.dex", // b/38187737
+              "memberrebinding2.jar" // b/38187737
+          )
+      );
+
+  public R8RunExamplesTest(String name, String fileType, String mainClass, String expectedOutput) {
+    this.name = name;
+    this.fileType = fileType;
+    this.mainClass = mainClass;
+    this.expectedOutput = expectedOutput;
+  }
+
+  private Path getInputFile() {
+    if (fileType == JAR_EXTENSION) {
+      return Paths.get(EXAMPLE_DIR, name + JAR_EXTENSION);
+    } else {
+      assert fileType == DEX_EXTENSION;
+      return getOriginalDexFile();
+    }
+  }
+
+  private Path getOriginalDexFile() {
+    return Paths.get(EXAMPLE_DIR, name, DEFAULT_DEX_FILENAME);
+  }
+
+  private Path getGeneratedDexFile() throws IOException {
+    return Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_DEX_FILENAME);
+  }
+
+  private String getTestName() {
+    return this.name + this.fileType;
+  }
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Before
+  public void generateR8Version()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    if (fileType == JAR_EXTENSION && Arrays.asList(failsWithJar).contains(name)) {
+      thrown.expect(Throwable.class);
+    }
+    String out = temp.getRoot().getCanonicalPath();
+    ToolHelper.runR8(getInputFile().toString(), out);
+  }
+
+  @Test
+  public void processedFileIsSmaller() throws IOException {
+    if (mayBeBigger.contains(mainClass)) {
+      return;
+    }
+    long original = Files.size(getOriginalDexFile());
+    long generated = Files.size(getGeneratedDexFile());
+
+    if (generated > original) {
+      DexInspector inspectOriginal = null;
+      DexInspector inspectGenerated = null;
+      try {
+        inspectOriginal = new DexInspector(getOriginalDexFile());
+        inspectGenerated = new DexInspector(getGeneratedDexFile());
+      } catch (Throwable e) {
+        System.err.println("Failed to parse dex files for post-failure processing");
+        e.printStackTrace();
+      }
+      if (inspectGenerated != null && inspectOriginal != null) {
+        assertEquals("Generated file is larger than original: " + generated + " vs. " + original,
+            inspectOriginal.clazz(mainClass).dumpMethods(),
+            inspectGenerated.clazz(mainClass).dumpMethods());
+      }
+    }
+    assertTrue("Generated file is larger than original: " + generated + " vs. " + original,
+        generated <= original);
+  }
+
+  @Test
+  public void outputIsIdentical() throws IOException, InterruptedException, ExecutionException {
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+
+    String original = getOriginalDexFile().toString();
+
+    File generated;
+    // Collect the generated dex files.
+    File[] outputFiles =
+        temp.getRoot().listFiles((File file) -> file.getName().endsWith(".dex"));
+    if (outputFiles.length == 1) {
+      // Just run Art on classes.dex.
+      generated = outputFiles[0];
+    } else {
+      // Run Art on JAR file with multiple dex files.
+      generated = temp.getRoot().toPath().resolve(name + ".jar").toFile();
+      JarBuilder.buildJar(outputFiles, generated);
+    }
+    // TODO(ager): Once we have a bot running using dalvik (version 4.4.4) we should remove
+    // this explicit loop to get rid of repeated testing on the buildbots.
+    for (DexVm version : artVersions) {
+      boolean expectedToFail = false;
+      if (failsOn.containsKey(version) && failsOn.get(version).contains(getTestName())) {
+        expectedToFail = true;
+        thrown.expect(Throwable.class);
+      }
+      String output =
+          ToolHelper.checkArtOutputIdentical(original, generated.toString(), mainClass, version);
+      if (expectedOutput != null && !expectedToFail) {
+        assertTrue("'" + output + "' lacks '" + expectedOutput + "'",
+            output.contains(expectedOutput));
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
new file mode 100644
index 0000000..3fe2173
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8RunSmaliTestsTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  private static final String SMALI_DIR = ToolHelper.SMALI_BUILD_DIR;
+
+  // Tests where the original smali code fails on Art, but runs after R8 processing.
+  private static Map<DexVm, List<String>> originalFailingOnArtVersions = ImmutableMap.of(
+      DexVm.ART_5_1_1, ImmutableList.of(
+          "sparse-switch",
+          "regression/33846227"
+      )
+  );
+
+  // Tests where the original smali code runs on Art, but fails after R8 processing
+  private static Map<String, List<String>> failingOnArtVersions = ImmutableMap.of(
+      // This list is currently empty!
+  );
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Parameters(name = "{0}")
+  public static Collection<String[]> data() {
+    return Arrays.asList(new String[][]{
+        {"arithmetic", "-1\n3\n2\n3\n3.0\n1\n0\n-131580\n-131580\n2\n4\n-2\n"},
+        {"controlflow", "2\n1\n2\n1\n2\n1\n2\n1\n2\n1\n2\n1\n2\n"},
+        {"fibonacci", "55\n55\n55\n55\n"},
+        {"fill-array-data", "[1, 2, 3][4, 5, 6]"},
+        {"filled-new-array", "[1, 2, 3][4, 5, 6][1, 2, 3, 4, 5, 6][6, 5, 4, 3, 2, 1]"},
+        {"packed-switch", "12345"},
+        {"sparse-switch", "12345"},
+        {"unreachable-code-1", "777"},
+        {"multiple-returns", "TFtf\n1\n4611686018427387904\ntrue\nfalse\n"},
+        {"try-catch", ""},
+        {"phi-removal-regression", "returnBoolean\n"},
+        {"overlapping-long-registers", "-9151314442816847872\n-9151314442816319488\n"},
+        {"type-confusion-regression", "java.lang.RuntimeException: Test.<init>()\n"},
+        {"type-confusion-regression2",
+            "java.lang.NullPointerException: Attempt to read from null array\n"},
+        {"type-confusion-regression3",
+            "java.lang.NullPointerException: Attempt to read from field 'byte[] Test.a'" +
+                " on a null object reference\n"},
+        {"type-confusion-regression4", ""},
+        {"type-confusion-regression5", "java.lang.RuntimeException: getId()I\n"},
+        {"chain-of-loops", "java.lang.RuntimeException: f(II)\n"},
+        {"new-instance-and-init", "Test(0)\nTest(0)\nTest(0)\n"},
+        {"bad-codegen",
+            "java.lang.NullPointerException: Attempt to read from field " +
+                "'Test Test.a' on a null object reference\n"},
+        {"merge-blocks-regression", "java.lang.NullPointerException: Attempt to invoke virtual"
+            +" method 'Test Test.bW_()' on a null object reference\n"},
+        {"self-is-catch-block", "100\n-1\n"},
+        {"infinite-loop", ""},
+        {"regression/33336471",
+            "START\n0\n2\nLOOP\n1\n2\nLOOP\n2\n2\nDONE\n" +
+                "START\n0\n2\nLOOP\n1\n2\nLOOP\n2\n2\nDONE\n"},
+        {"regression/33846227", ""},
+    });
+  }
+
+  private String directoryName;
+  private String dexFileName;
+  private String expectedOutput;
+
+  public R8RunSmaliTestsTest(String name, String expectedOutput) {
+    this.directoryName = name;
+    this.dexFileName = name.substring(name.lastIndexOf('/') + 1) + ".dex";
+    this.expectedOutput = expectedOutput;
+  }
+
+  @Test
+  public void SmaliTest()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    File originalDexFile = Paths.get(SMALI_DIR, directoryName, dexFileName).toFile();
+    String outputPath = temp.getRoot().getCanonicalPath();
+    ToolHelper.runR8(originalDexFile.getCanonicalPath(), outputPath);
+
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+
+    String mainClass = "Test";
+    String generated = outputPath + "/classes.dex";
+    String output;
+
+    // If the original smali code fails on the target VM, only run the code produced by R8.
+    if (originalFailingOnArtVersions.containsKey(ToolHelper.getDexVm())
+        && originalFailingOnArtVersions.get(ToolHelper.getDexVm()).contains(directoryName)) {
+      output = ToolHelper.runArtNoVerificationErrors(generated, mainClass);
+    } else {
+      if (failingOnArtVersions.containsKey(ToolHelper.getDexVm())
+          && failingOnArtVersions.get(ToolHelper.getDexVm()).contains(directoryName)) {
+        thrown.expect(Throwable.class);
+      }
+      output =
+          ToolHelper.checkArtOutputIdentical(originalDexFile.toString(), generated, mainClass, null);
+    }
+    assertEquals(expectedOutput, output);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
new file mode 100644
index 0000000..77eb5eb0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/R8UnreachableCodeTest.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Test;
+
+public class R8UnreachableCodeTest {
+
+  private static final Path SMALI_DIR = Paths.get(ToolHelper.SMALI_BUILD_DIR);
+
+  @Test
+  public void UnreachableCode() throws IOException, ExecutionException {
+    String name = "unreachable-code-1";
+    AndroidApp input = AndroidApp.fromProgramFiles(SMALI_DIR.resolve(name).resolve(name + ".dex"));
+    ExecutorService executorService = Executors.newSingleThreadExecutor();
+    Timing timing = new Timing("R8UnreachableCodeTest");
+    DexApplication application =
+        new ApplicationReader(input, new InternalOptions(), timing).read(executorService);
+    IRConverter converter =
+        new IRConverter(
+            application, new AppInfoWithSubtyping(application), new InternalOptions(), null);
+    converter.optimize();
+    DexProgramClass clazz = application.classes().iterator().next();
+    assertEquals(4, clazz.directMethods().length);
+    for (DexEncodedMethod method : clazz.directMethods()) {
+      if (!method.method.name.toString().equals("main")) {
+        assertEquals(2, method.getCode().asDexCode().instructions.length);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
new file mode 100644
index 0000000..3f63319
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -0,0 +1,242 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import static com.android.tools.r8.dex.Constants.ANDROID_K_API;
+import static com.android.tools.r8.dex.Constants.ANDROID_O_API;
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.FileUtils.ZIP_EXTENSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OffOrAuto;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.UnaryOperator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class RunExamplesAndroidOTest<B> {
+  static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR;
+
+  abstract class TestRunner {
+    final String testName;
+    final String packageName;
+    final String mainClass;
+
+    final List<Consumer<InternalOptions>> optionConsumers = new ArrayList<>();
+    final List<UnaryOperator<B>> builderTransformations = new ArrayList<>();
+
+    TestRunner(String testName, String packageName, String mainClass) {
+      this.testName = testName;
+      this.packageName = packageName;
+      this.mainClass = mainClass;
+    }
+
+    TestRunner withOptionConsumer(Consumer<InternalOptions> consumer) {
+      optionConsumers.add(consumer);
+      return this;
+    }
+
+    TestRunner withInterfaceMethodDesugaring(OffOrAuto behavior) {
+      return withOptionConsumer(o -> o.interfaceMethodDesugaring = behavior);
+    }
+
+    TestRunner withBuilderTransformation(UnaryOperator<B> builderTransformation) {
+      builderTransformations.add(builderTransformation);
+      return this;
+    }
+
+    void combinedOptionConsumer(InternalOptions options) {
+      for (Consumer<InternalOptions> consumer : optionConsumers) {
+        consumer.accept(options);
+      }
+    }
+
+    void run() throws Throwable {
+      if (compilationErrorExpected(testName)) {
+        thrown.expect(CompilationError.class);
+      }
+
+      String qualifiedMainClass = packageName + "." + mainClass;
+      Path inputFile = Paths.get(EXAMPLE_DIR, packageName + JAR_EXTENSION);
+      Path out = temp.getRoot().toPath().resolve(testName + ZIP_EXTENSION);
+
+      build(inputFile, out);
+
+      if (!ToolHelper.artSupported()) {
+        return;
+      }
+
+      boolean expectedToFail = expectedToFail(testName);
+      if (expectedToFail) {
+        thrown.expect(Throwable.class);
+      }
+
+      String output = ToolHelper.runArtNoVerificationErrors(out.toString(), qualifiedMainClass);
+      if (!expectedToFail) {
+        ToolHelper.ProcessResult javaResult =
+            ToolHelper.runJava(ImmutableList.of(inputFile.toString()), qualifiedMainClass);
+        assertEquals("JVM run failed", javaResult.exitCode, 0);
+        assertTrue(
+            "JVM output does not match art output.\n\tjvm: "
+                + javaResult.stdout
+                + "\n\tart: "
+                + output,
+            output.equals(javaResult.stdout));
+      }
+    }
+
+    abstract TestRunner withMinApiLevel(int minApiLevel);
+
+    abstract void build(Path inputFile, Path out) throws Throwable;
+  }
+
+  private static List<String> compilationErrorExpected =
+      ImmutableList.of(
+          "invokepolymorphic-error-due-to-min-sdk", "invokecustom-error-due-to-min-sdk");
+
+  private static Map<DexVm, List<String>> failsOn =
+      ImmutableMap.of(
+          DexVm.ART_4_4_4, ImmutableList.of(
+              // API not supported
+              "paramnames",
+              "repeat_annotations_new_api",
+              // Dex version not supported
+              "invokepolymorphic",
+              "invokecustom"
+          ),
+          DexVm.ART_5_1_1, ImmutableList.of(
+              // API not supported
+              "paramnames",
+              "repeat_annotations_new_api",
+              // Dex version not supported
+              "invokepolymorphic",
+              "invokecustom"
+          ),
+          DexVm.ART_6_0_1, ImmutableList.of(
+              // API not supported
+              "paramnames",
+              "repeat_annotations_new_api",
+              // Dex version not supported
+              "invokepolymorphic",
+              "invokecustom"
+          ),
+          DexVm.ART_7_0_0, ImmutableList.of(
+              // API not supported
+              "paramnames",
+              // Dex version not supported
+              "invokepolymorphic",
+              "invokecustom"
+          ),
+          DexVm.ART_DEFAULT, ImmutableList.of(
+          )
+      );
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  boolean failsOn(Map<ToolHelper.DexVm, List<String>> failsOn, String name) {
+    return failsOn.containsKey(ToolHelper.getDexVm())
+        && failsOn.get(ToolHelper.getDexVm()).contains(name);
+  }
+
+  boolean expectedToFail(String name) {
+    return failsOn(failsOn, name);
+  }
+
+  boolean compilationErrorExpected(String testName) {
+    return compilationErrorExpected.contains(testName);
+  }
+
+  @Test
+  public void invokeCustom() throws Throwable {
+    test("invokecustom", "invokecustom", "InvokeCustom")
+        .withMinApiLevel(ANDROID_O_API)
+        .run();
+  }
+
+  @Test
+  public void invokeCustomErrorDueToMinSdk() throws Throwable {
+    test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
+        .withMinApiLevel(25)
+        .run();
+  }
+
+  @Test
+  public void invokePolymorphic() throws Throwable {
+    test("invokepolymorphic", "invokepolymorphic", "InvokePolymorphic")
+        .withMinApiLevel(ANDROID_O_API)
+        .run();
+  }
+
+  @Test
+  public void invokePolymorphicErrorDueToMinSdk() throws Throwable {
+    test("invokepolymorphic-error-due-to-min-sdk", "invokepolymorphic", "InvokePolymorphic")
+        .withMinApiLevel(25)
+        .run();
+  }
+
+  @Test
+  public void lambdaDesugaring() throws Throwable {
+    test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
+        .withMinApiLevel(ANDROID_K_API)
+        .run();
+  }
+
+  @Test
+  public void lambdaDesugaringNPlus() throws Throwable {
+    test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
+        .withMinApiLevel(ANDROID_K_API)
+        .withInterfaceMethodDesugaring(OffOrAuto.Auto)
+        .run();
+  }
+
+  @Test
+  public void lambdaDesugaringValueAdjustments() throws Throwable {
+    test("lambdadesugaring-value-adjustments", "lambdadesugaring", "ValueAdjustments")
+        .withMinApiLevel(ANDROID_K_API)
+        .run();
+  }
+
+  @Test
+  public void paramNames() throws Throwable {
+    test("paramnames", "paramnames", "ParameterNames")
+        .withMinApiLevel(26)
+        .run();
+  }
+
+  @Test
+  public void repeatAnnotationsNewApi() throws Throwable {
+    // No need to specify minSdk as repeat annotations are handled by javac and we do not have
+    // to do anything to support them. The library methods to access them just have to be in
+    // the system.
+    test("repeat_annotations_new_api", "repeat_annotations", "RepeatAnnotationsNewApi").run();
+  }
+
+  @Test
+  public void repeatAnnotations() throws Throwable {
+    // No need to specify minSdk as repeat annotations are handled by javac and we do not have
+    // to do anything to support them. The library methods to access them just have to be in
+    // the system.
+    test("repeat_annotations", "repeat_annotations", "RepeatAnnotations").run();
+  }
+
+  abstract TestRunner test(String testName, String packageName, String mainClass);
+}
diff --git a/src/test/java/com/android/tools/r8/TestCondition.java b/src/test/java/com/android/tools/r8/TestCondition.java
new file mode 100644
index 0000000..ef2eac3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/TestCondition.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.R8RunArtTestsTest.DexTool;
+import com.android.tools.r8.ToolHelper.DexVm;
+import java.util.Arrays;
+import java.util.EnumSet;
+
+public class TestCondition {
+
+  static class ToolSet {
+
+    final EnumSet<DexTool> set;
+
+    public ToolSet(EnumSet<DexTool> set) {
+      this.set = set;
+    }
+  }
+
+  static class CompilerSet {
+
+    final EnumSet<CompilerUnderTest> set;
+
+    public CompilerSet(EnumSet<CompilerUnderTest> set) {
+      this.set = set;
+    }
+  }
+
+  static class RuntimeSet {
+
+    final EnumSet<DexVm> set;
+
+    public RuntimeSet(EnumSet<DexVm> set) {
+      this.set = set;
+    }
+  }
+
+  public static final CompilerSet D8_COMPILER = compilers(CompilerUnderTest.D8);
+  public static final CompilerSet R8_COMPILER = compilers(CompilerUnderTest.R8);
+
+  private static final ToolSet ANY_TOOL = new ToolSet(EnumSet.allOf(DexTool.class));
+  private static final CompilerSet ANY_COMPILER =
+      new CompilerSet(EnumSet.allOf(CompilerUnderTest.class));
+  private static final RuntimeSet ANY_RUNTIME = new RuntimeSet(EnumSet.allOf(DexVm.class));
+
+  private final EnumSet<DexTool> dexTools;
+  private final EnumSet<CompilerUnderTest> compilers;
+  private final EnumSet<DexVm> dexVms;
+
+  public TestCondition(
+      EnumSet<DexTool> dexTools,
+      EnumSet<CompilerUnderTest> compilers,
+      EnumSet<DexVm> dexVms) {
+    this.dexTools = dexTools;
+    this.compilers = compilers;
+    this.dexVms = dexVms;
+  }
+
+  public static ToolSet tools(DexTool... tools) {
+    assert tools.length > 0;
+    return new ToolSet(EnumSet.copyOf(Arrays.asList(tools)));
+  }
+
+  public static CompilerSet compilers(CompilerUnderTest... compilers) {
+    assert compilers.length > 0;
+    return new CompilerSet(EnumSet.copyOf(Arrays.asList(compilers)));
+  }
+
+  public static RuntimeSet runtimes(DexVm... runtimes) {
+    assert runtimes.length > 0;
+    return new RuntimeSet(EnumSet.copyOf(Arrays.asList(runtimes)));
+  }
+
+  public static TestCondition match(ToolSet tools, CompilerSet compilers, RuntimeSet runtimes) {
+    return new TestCondition(tools.set, compilers.set, runtimes.set);
+  }
+
+  public static TestCondition any() {
+    return match(TestCondition.ANY_TOOL, TestCondition.ANY_COMPILER, TestCondition.ANY_RUNTIME);
+  }
+
+  public static TestCondition match(ToolSet tools) {
+    return match(tools, TestCondition.ANY_COMPILER, TestCondition.ANY_RUNTIME);
+  }
+
+  public static TestCondition match(ToolSet tools, CompilerSet compilers) {
+    return match(tools, compilers, TestCondition.ANY_RUNTIME);
+  }
+
+  public static TestCondition match(ToolSet tools, RuntimeSet runtimes) {
+    return match(tools, TestCondition.ANY_COMPILER, runtimes);
+  }
+
+  public static TestCondition match(CompilerSet compilers) {
+    return match(TestCondition.ANY_TOOL, compilers, TestCondition.ANY_RUNTIME);
+  }
+
+  public static TestCondition match(CompilerSet compilers, RuntimeSet runtimes) {
+    return match(TestCondition.ANY_TOOL, compilers, runtimes);
+  }
+
+  public static TestCondition match(RuntimeSet runtimes) {
+    return match(TestCondition.ANY_TOOL, TestCondition.ANY_COMPILER, runtimes);
+  }
+
+  public boolean test(DexTool dexTool, CompilerUnderTest compilerUnderTest, DexVm dexVm) {
+    return dexTools.contains(dexTool) && compilers.contains(compilerUnderTest)
+        && dexVms.contains(dexVm);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
new file mode 100644
index 0000000..6e45d0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -0,0 +1,702 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import joptsimple.internal.Strings;
+import org.junit.rules.TemporaryFolder;
+
+public class ToolHelper {
+
+  public static final String BUILD_DIR = "build/";
+  public static final String EXAMPLES_DIR = "src/test/examples/";
+  public static final String EXAMPLES_ANDROID_O_DIR = "src/test/examplesAndroidO/";
+  public static final String EXAMPLES_BUILD_DIR = BUILD_DIR + "test/examples/";
+  public static final String EXAMPLES_ANDROID_N_BUILD_DIR = BUILD_DIR + "test/examplesAndroidN/";
+  public static final String EXAMPLES_ANDROID_O_BUILD_DIR = BUILD_DIR + "test/examplesAndroidO/";
+  public static final String SMALI_BUILD_DIR = BUILD_DIR + "test/smali/";
+
+  private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+  private static final int DEFAULT_MIN_SDK = 14;
+
+  public enum DexVm {
+    ART_4_4_4("4.4.4"),
+    ART_5_1_1("5.1.1"),
+    ART_6_0_1("6.0.1"),
+    ART_7_0_0("7.0.0"),
+    ART_DEFAULT("default");
+
+    private static final ImmutableMap<String, DexVm> SHORT_NAME_MAP =
+        new ImmutableMap.Builder<String, DexVm>()
+            .putAll(
+                Arrays.stream(DexVm.values()).collect(
+                    Collectors.toMap(a -> a.toString(), a -> a)))
+            .build();
+
+    public String toString() {
+      return shortName;
+    }
+
+    public static DexVm fromShortName(String shortName) {
+      return SHORT_NAME_MAP.get(shortName);
+    }
+
+    public boolean isNewerThan(DexVm other) {
+      return compareTo(other) > 0;
+    }
+
+    private DexVm(String shortName) {
+      this.shortName = shortName;
+    }
+
+    private final String shortName;
+  }
+
+
+  public abstract static class CommandBuilder {
+
+    private List<String> options = new ArrayList<>();
+    private Map<String, String> systemProperties = new LinkedHashMap<>();
+    private List<String> classpaths = new ArrayList<>();
+    private String mainClass;
+    private List<String> programArguments = new ArrayList<>();
+    private List<String> bootClassPaths = new ArrayList<>();
+
+    public CommandBuilder appendArtOption(String option) {
+      options.add(option);
+      return this;
+    }
+
+    public CommandBuilder appendArtSystemProperty(String key, String value) {
+      systemProperties.put(key, value);
+      return this;
+    }
+
+    public CommandBuilder appendClasspath(String classpath) {
+      classpaths.add(classpath);
+      return this;
+    }
+
+    public CommandBuilder setMainClass(String className) {
+      this.mainClass = className;
+      return this;
+    }
+
+    public CommandBuilder appendProgramArgument(String option) {
+      programArguments.add(option);
+      return this;
+    }
+
+    public CommandBuilder appendBootClassPath(String lib) {
+      bootClassPaths.add(lib);
+      return this;
+    }
+
+    private List<String> command() {
+      List<String> result = new ArrayList<>();
+      // The art script _must_ be run with bash, bots default to /bin/dash for /bin/sh, so
+      // explicitly set it;
+      if (isLinux()) {
+        result.add("/bin/bash");
+      } else {
+        result.add("tools/docker/run.sh");
+      }
+      result.add(getExecutable());
+      for (String option : options) {
+        result.add(option);
+      }
+      for (Map.Entry<String, String> entry : systemProperties.entrySet()) {
+        StringBuilder builder = new StringBuilder("-D");
+        builder.append(entry.getKey());
+        builder.append("=");
+        builder.append(entry.getValue());
+        result.add(builder.toString());
+      }
+      if (!classpaths.isEmpty()) {
+        result.add("-cp");
+        result.add(Strings.join(classpaths, ":"));
+      }
+      if (!bootClassPaths.isEmpty()) {
+        result.add("-Xbootclasspath:" + String.join(":", bootClassPaths));
+      }
+      if (mainClass != null) {
+        result.add(mainClass);
+      }
+      for (String argument : programArguments) {
+        result.add(argument);
+      }
+      return result;
+    }
+
+    public ProcessBuilder asProcessBuilder() {
+      return new ProcessBuilder(command());
+    }
+
+    public String build() {
+      return String.join(" ", command());
+    }
+
+    protected abstract String getExecutable();
+  }
+
+  public static class ArtCommandBuilder extends CommandBuilder {
+
+    private DexVm version;
+
+    public ArtCommandBuilder() {
+    }
+
+    public ArtCommandBuilder(DexVm version) {
+      assert ART_BINARY_VERSIONS.containsKey(version);
+      this.version = version;
+    }
+
+    @Override
+    protected String getExecutable() {
+      return version != null ? getArtBinary(version) : getArtBinary();
+    }
+  }
+
+  public static class DXCommandBuilder extends CommandBuilder {
+
+    public DXCommandBuilder() {
+      appendProgramArgument("--dex");
+    }
+
+    @Override
+    protected String getExecutable() {
+      return DX;
+    }
+  }
+
+  private static class StreamReader implements Runnable {
+
+    private InputStream stream;
+    private String result;
+
+    public StreamReader(InputStream stream) {
+      this.stream = stream;
+    }
+
+    public String getResult() {
+      return result;
+    }
+
+    @Override
+    public void run() {
+      try {
+        result = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
+        stream.close();
+      } catch (IOException e) {
+        result = "Failed reading result for stream " + stream;
+      }
+    }
+  }
+
+  private static final String TOOLS = "tools";
+  private static final Map<DexVm, String> ART_DIRS =
+      ImmutableMap.of(
+          DexVm.ART_DEFAULT, "art",
+          DexVm.ART_7_0_0, "art-7.0.0",
+          DexVm.ART_6_0_1, "art-6.0.1",
+          DexVm.ART_5_1_1, "art-5.1.1",
+          DexVm.ART_4_4_4, "dalvik");
+  private static final Map<DexVm, String> ART_BINARY_VERSIONS =
+      ImmutableMap.of(
+          DexVm.ART_DEFAULT, "bin/art",
+          DexVm.ART_7_0_0, "bin/art",
+          DexVm.ART_6_0_1, "bin/art",
+          DexVm.ART_5_1_1, "bin/art",
+          DexVm.ART_4_4_4, "bin/dalvik");
+
+  private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
+      ImmutableMap.of(
+          DexVm.ART_DEFAULT, "bin/art",
+          DexVm.ART_7_0_0, "bin/art",
+          DexVm.ART_6_0_1, "bin/art");
+  private static final List<String> ART_BOOT_LIBS =
+      ImmutableList.of(
+          "core-libart-hostdex.jar",
+          "core-oj-hostdex.jar",
+          "apache-xml-hostdex.jar"
+      );
+
+  private static final String LIB_PATH = TOOLS + "/linux/art/lib";
+  private static final String DX = TOOLS + "/linux/dx/bin/dx";
+  private static final String DEX2OAT = TOOLS + "/linux/art/bin/dex2oat";
+  private static final String ANGLER_DIR = TOOLS + "/linux/art/product/angler";
+  private static final String ANGLER_BOOT_IMAGE = ANGLER_DIR + "/system/framework/boot.art";
+
+  public static String getArtDir(DexVm version) {
+    String dir = ART_DIRS.get(version);
+    if (dir == null) {
+      throw new IllegalStateException("Does not support dex vm: " + version);
+    }
+    if (isLinux() || isMac()) {
+      // The Linux version is used on Mac, where it is run in a Docker container.
+      return TOOLS + "/linux/" + dir;
+    }
+    fail("Unsupported platform, we currently only support mac and linux: " + getPlatform());
+    return ""; //never here
+  }
+
+  public static String getArtBinary(DexVm version) {
+    String binary = ART_BINARY_VERSIONS.get(version);
+    if (binary == null) {
+      throw new IllegalStateException("Does not support running with dex vm: " + version);
+    }
+    return getArtDir(version) + "/" + binary;
+  }
+
+  public static String getDefaultAndroidJar() {
+    return getAndroidJar(Constants.DEFAULT_ANDROID_API);
+  }
+
+  public static String getAndroidJar(int minSdkVersion) {
+    return String.format(
+        ANDROID_JAR_PATTERN,
+        minSdkVersion == Constants.DEFAULT_ANDROID_API ? DEFAULT_MIN_SDK : minSdkVersion);
+  }
+
+  public static Path getJdwpTestsJarPath(int minSdk) {
+    if (minSdk >= Constants.ANDROID_N_API) {
+      return Paths.get("third_party", "jdwp-tests", "apache-harmony-jdwp-tests-host.jar");
+    } else {
+      return Paths.get(ToolHelper.BUILD_DIR, "libs", "jdwp-tests-preN.jar");
+    }
+  }
+
+  // For non-Linux platforms create the temporary directory in the repository root to simplify
+  // running Art in a docker container
+  public static TemporaryFolder getTemporaryFolderForTest() {
+    return new TemporaryFolder(ToolHelper.isLinux() ? null : Paths.get("build", "tmp").toFile());
+  }
+
+  public static String getArtBinary() {
+    return getArtBinary(getDexVm());
+  }
+
+  public static Set<DexVm> getArtVersions() {
+    String artVersion = System.getProperty("dex_vm");
+    if (artVersion != null) {
+      DexVm artVersionEnum = getDexVm();
+      if (!ART_BINARY_VERSIONS.containsKey(artVersionEnum)) {
+        throw new RuntimeException("Unsupported Art version " + artVersion);
+      }
+      return ImmutableSet.of(artVersionEnum);
+    } else {
+      if (isLinux()) {
+        return ART_BINARY_VERSIONS.keySet();
+      } else {
+        return ART_BINARY_VERSIONS_X64.keySet();
+      }
+    }
+  }
+
+  public static List<String> getArtBootLibs() {
+    String prefix = getArtDir(getDexVm()) + "/";
+    List<String> result = new ArrayList<>();
+    ART_BOOT_LIBS.stream().forEach(x -> result.add(prefix + "framework/" + x));
+    return result;
+  }
+
+  // Returns if the passed in vm to use is the default.
+  public static boolean isDefaultDexVm() {
+    return getDexVm() == DexVm.ART_DEFAULT;
+  }
+
+  public static DexVm getDexVm() {
+    String artVersion = System.getProperty("dex_vm");
+    if (artVersion == null) {
+      return DexVm.ART_DEFAULT;
+    } else {
+      DexVm artVersionEnum = DexVm.fromShortName(artVersion);
+      if (artVersionEnum == null) {
+        throw new RuntimeException("Unsupported Art version " + artVersion);
+      } else {
+        return artVersionEnum;
+      }
+    }
+  }
+
+  public static int getMinApiLevelForDexVm(DexVm dexVm) {
+    switch (dexVm) {
+      case ART_DEFAULT:
+        return Constants.ANDROID_O_API;
+      case ART_7_0_0:
+        return Constants.ANDROID_N_API;
+      default:
+        return Constants.DEFAULT_ANDROID_API;
+    }
+  }
+
+  private static String getPlatform() {
+    return System.getProperty("os.name");
+  }
+
+  public static boolean isLinux() {
+    return getPlatform().startsWith("Linux");
+  }
+
+  public static boolean isMac() {
+    return getPlatform().startsWith("Mac");
+  }
+
+  public static boolean artSupported() {
+    if (!isLinux() && !isMac()) {
+      System.err.println("Testing on your platform is not fully supported. " +
+          "Art does not work on on your platform.");
+      return false;
+    }
+    return true;
+  }
+
+  public static Path getClassPathForTests() {
+    return Paths.get(BUILD_DIR, "classes", "test");
+  }
+
+  public static Path getClassFileForTestClass(Class clazz) {
+    String[] parts = clazz.getCanonicalName().split("\\.");
+    parts[parts.length - 1] = parts[parts.length - 1] + ".class";
+    return getClassPathForTests().resolve(Paths.get("", parts));
+  }
+
+  public static DexApplication buildApplication(List<String> fileNames)
+      throws IOException, ExecutionException {
+    return new ApplicationReader(
+            AndroidApp.fromProgramFiles(ListUtils.map(fileNames, Paths::get)),
+            new InternalOptions(),
+            new Timing("ToolHelper buildApplication"))
+        .read();
+  }
+
+  public static AndroidApp runR8(AndroidApp app)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    return runR8(R8Command.builder(app).build());
+  }
+
+  public static AndroidApp runR8(AndroidApp app, Path output)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    assert output != null;
+    return runR8(R8Command.builder(app).setOutputPath(output).build());
+  }
+
+  public static AndroidApp runR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+    return runR8(R8Command.builder(app).build(), optionsConsumer);
+  }
+
+  public static AndroidApp runR8(R8Command command)
+      throws ProguardRuleParserException, ExecutionException, IOException {
+    return runR8(command, null);
+  }
+
+  public static AndroidApp runR8(R8Command command, Consumer<InternalOptions> optionsConsumer)
+      throws ProguardRuleParserException, ExecutionException, IOException {
+    // TODO(zerny): Should we really be adding the android library in ToolHelper?
+    AndroidApp app = command.getInputApp();
+    if (app.getClassLibraryResources().isEmpty()) {
+      app =
+          AndroidApp.builder(app)
+              .addLibraryFiles(Paths.get(getAndroidJar(command.getMinApiLevel())))
+              .build();
+    }
+    InternalOptions options = command.getInternalOptions();
+    // TODO(zerny): Should we really be setting this in ToolHelper?
+    options.ignoreMissingClasses = true;
+    if (optionsConsumer != null) {
+      optionsConsumer.accept(options);
+    }
+    AndroidApp result = R8.runForTesting(app, options);
+    if (command.getOutputPath() != null) {
+      result.write(command.getOutputPath());
+    }
+    return result;
+  }
+
+  public static AndroidApp runR8(String fileName, String out)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    return runR8(Collections.singletonList(fileName), out);
+  }
+
+  public static AndroidApp runR8(Collection<String> fileNames, String out)
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    return R8.run(
+        R8Command.builder()
+            .addProgramFiles(ListUtils.map(fileNames, Paths::get))
+            .setOutputPath(Paths.get(out))
+            .setIgnoreMissingClasses(true)
+            .build());
+  }
+
+  public static AndroidApp runD8(AndroidApp app) throws CompilationException, IOException {
+    return runD8(app, null);
+  }
+
+  public static AndroidApp runD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
+      throws CompilationException, IOException {
+    return runD8(D8Command.builder(app).build(), optionsConsumer);
+  }
+
+  public static AndroidApp runD8(D8Command command) throws IOException {
+    return runD8(command, null);
+  }
+
+  public static AndroidApp runD8(D8Command command, Consumer<InternalOptions> optionsConsumer)
+      throws IOException {
+    InternalOptions options = command.getInternalOptions();
+    if (optionsConsumer != null) {
+      optionsConsumer.accept(options);
+    }
+    AndroidApp result = D8.runForTesting(command.getInputApp(), options);
+    if (command.getOutputPath() != null) {
+      result.write(command.getOutputPath());
+    }
+    return result;
+  }
+
+  public static AndroidApp runDexer(String fileName, String outDir, String... extraArgs)
+      throws IOException {
+    List<String> args = new ArrayList<>();
+    Collections.addAll(args, extraArgs);
+    Collections.addAll(args, "--output=" + outDir + "/classes.dex", fileName);
+    int result = runDX(args.toArray(new String[args.size()])).exitCode;
+    return result != 0 ? null : AndroidApp.fromProgramDirectory(Paths.get(outDir));
+  }
+
+  public static ProcessResult runDX(String[] args) throws IOException {
+    DXCommandBuilder builder = new DXCommandBuilder();
+    for (String arg : args) {
+      builder.appendProgramArgument(arg);
+    }
+    return runProcess(builder.asProcessBuilder());
+  }
+
+  public static ProcessResult runJava(Class clazz) throws Exception {
+    String main = clazz.getCanonicalName();
+    Path path = getClassPathForTests();
+    return runJava(ImmutableList.of(path.toString()), main);
+  }
+
+  public static ProcessResult runJava(List<String> classpath, String mainClass) throws IOException {
+    ProcessBuilder builder = new ProcessBuilder(
+        getJavaExecutable(), "-cp", String.join(":", classpath), mainClass);
+    return runProcess(builder);
+  }
+
+  public static ProcessResult forkD8(Path dir, String... args)
+      throws IOException, InterruptedException {
+    return forkJava(dir, D8.class, args);
+  }
+
+  public static ProcessResult forkR8(Path dir, String... args)
+      throws IOException, InterruptedException {
+    return forkJava(dir, R8.class, ImmutableList.builder()
+        .addAll(Arrays.asList(args))
+        .add("--ignore-missing-classes")
+        .build()
+        .toArray(new String[0]));
+  }
+
+  private static ProcessResult forkJava(Path dir, Class clazz, String... args)
+      throws IOException, InterruptedException {
+    List<String> command = new ImmutableList.Builder<String>()
+        .add(getJavaExecutable())
+        .add("-cp").add(System.getProperty("java.class.path"))
+        .add(clazz.getCanonicalName())
+        .addAll(Arrays.asList(args))
+        .build();
+    return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
+  }
+
+  public static String getJavaExecutable() {
+    return Paths.get(System.getProperty("java.home"), "bin", "java").toString();
+  }
+
+  public static String runArtNoVerificationErrors(String file, String mainClass)
+      throws IOException {
+    return runArtNoVerificationErrors(Collections.singletonList(file), mainClass, null);
+  }
+
+  public static String runArtNoVerificationErrors(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras)
+      throws IOException {
+    return runArtNoVerificationErrors(files, mainClass, extras, null);
+  }
+
+  public static String runArtNoVerificationErrors(List<String> files, String mainClass,
+      Consumer<ArtCommandBuilder> extras,
+      DexVm version)
+      throws IOException {
+    ArtCommandBuilder builder =
+        version != null ? new ArtCommandBuilder(version) : new ArtCommandBuilder();
+    files.forEach(builder::appendClasspath);
+    builder.setMainClass(mainClass);
+    if (extras != null) {
+      extras.accept(builder);
+    }
+    return runArtNoVerificationErrors(builder);
+  }
+
+  public static String runArtNoVerificationErrors(ArtCommandBuilder builder) throws IOException {
+    ProcessResult result = runArtProcess(builder);
+    if (result.stderr.contains("Verification error")) {
+      fail("Verification error: \n" + result.stderr);
+    }
+    return result.stdout;
+  }
+
+  private static ProcessResult runArtProcess(ArtCommandBuilder builder) throws IOException {
+    ProcessResult result = runProcess(builder.asProcessBuilder());
+    if (result.exitCode != 0) {
+      fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
+    }
+    return result;
+  }
+
+  public static String runArt(ArtCommandBuilder builder) throws IOException {
+    ProcessResult result = runArtProcess(builder);
+    return result.stdout;
+  }
+
+  public static String checkArtOutputIdentical(String file1, String file2, String mainClass,
+      DexVm version)
+      throws IOException {
+    return checkArtOutputIdentical(Collections.singletonList(file1),
+        Collections.singletonList(file2), mainClass, null, version);
+  }
+
+  public static String checkArtOutputIdentical(List<String> files1, List<String> files2,
+      String mainClass,
+      Consumer<ArtCommandBuilder> extras,
+      DexVm version)
+      throws IOException {
+    // Run art on original.
+    for (String file : files1) {
+      assertTrue("file1 " + file + " must exists", Files.exists(Paths.get(file)));
+    }
+    String output1 = ToolHelper.runArtNoVerificationErrors(files1, mainClass, extras, version);
+    // Run art on R8 processed version.
+    for (String file : files2) {
+      assertTrue("file2 " + file + " must exists", Files.exists(Paths.get(file)));
+    }
+    String output2 = ToolHelper.runArtNoVerificationErrors(files2, mainClass, extras, version);
+    assertEquals(output1, output2);
+    return output1;
+  }
+
+  public static void runDex2Oat(Path file, Path outFile) throws IOException {
+    assert Files.exists(file);
+    assert ByteStreams.toByteArray(Files.newInputStream(file)).length > 0;
+    List<String> command = new ArrayList<>();
+    command.add(DEX2OAT);
+    command.add("--android-root=" + ANGLER_DIR);
+    command.add("--runtime-arg");
+    command.add("-Xnorelocate");
+    command.add("--boot-image=" + ANGLER_BOOT_IMAGE);
+    command.add("--dex-file=" + file.toAbsolutePath());
+    command.add("--oat-file=" + outFile.toAbsolutePath());
+    command.add("--instruction-set=arm64");
+    command.add("--compiler-filter=interpret-only");
+    ProcessBuilder builder = new ProcessBuilder(command);
+    builder.environment().put("LD_LIBRARY_PATH", LIB_PATH);
+    ProcessResult result = runProcess(builder);
+    if (result.exitCode != 0) {
+      fail("dex2oat failed, exit code " + result.exitCode + ", stderr:\n" + result.stderr);
+    }
+    if (result.stderr.contains("Verification error")) {
+      fail("Verification error: \n" + result.stderr);
+    }
+  }
+
+  public static class ProcessResult {
+
+    public final int exitCode;
+    public final String stdout;
+    public final String stderr;
+
+    ProcessResult(int exitCode, String stdout, String stderr) {
+      this.exitCode = exitCode;
+      this.stdout = stdout;
+      this.stderr = stderr;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append("EXIT CODE: ");
+      builder.append(exitCode);
+      builder.append("\n");
+      builder.append("STDOUT: ");
+      builder.append("\n");
+      builder.append(stdout);
+      builder.append("\n");
+      builder.append("STDERR: ");
+      builder.append("\n");
+      builder.append(stderr);
+      builder.append("\n");
+      return builder.toString();
+    }
+  }
+
+  public static ProcessResult runProcess(ProcessBuilder builder) throws IOException {
+    System.out.println(String.join(" ", builder.command()));
+    Process p = builder.start();
+    // Drain stdout and stderr so that the process does not block. Read stdout and stderr
+    // in parallel to make sure that neither buffer can get filled up which will cause the
+    // C program to block in a call to write.
+    StreamReader stdoutReader = new StreamReader(p.getInputStream());
+    StreamReader stderrReader = new StreamReader(p.getErrorStream());
+    Thread stdoutThread = new Thread(stdoutReader);
+    Thread stderrThread = new Thread(stderrReader);
+    stdoutThread.start();
+    stderrThread.start();
+    try {
+      p.waitFor();
+      stdoutThread.join();
+      stderrThread.join();
+    } catch (InterruptedException e) {
+      throw new RuntimeException("Execution interrupted", e);
+    }
+    return new ProcessResult(p.exitValue(), stdoutReader.getResult(), stderrReader.getResult());
+  }
+
+  public static AndroidApp getApp(BaseCommand command) {
+    return command.getInputApp();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/bisect/BisectTest.java b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
new file mode 100644
index 0000000..ba49e64
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bisect/BisectTest.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.bisect;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.bisect.BisectOptions.Result;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.smali.SmaliTestBase.MethodSignature;
+import com.android.tools.r8.smali.SmaliTestBase.SmaliBuilder;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class BisectTest {
+
+  private final String[] CLASSES = {"A", "B", "C", "D", "E", "F", "G", "H"};
+  private final String ERRONEOUS_CLASS = "F";
+  private final String ERRONEOUS_METHOD = "foo";
+  private final String VALID_METHOD = "bar";
+
+  // Set during build to more easily inspect later.
+  private MethodSignature erroneousMethodSignature = null;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void bisect() throws Exception {
+    InternalOptions options = new InternalOptions();
+    Timing timing = new Timing("bisect-test");
+
+    // Build "good" application with no method in "F".
+    SmaliBuilder builderGood = new SmaliBuilder();
+    for (String clazz : CLASSES) {
+      builderGood.addClass(clazz);
+      builderGood.addStaticMethod(
+          "void", VALID_METHOD, ImmutableList.of(), 0, "return-void");
+    }
+    AndroidApp inputGood = AndroidApp.fromDexProgramData(builderGood.compile());
+    DexApplication appGood = new ApplicationReader(inputGood, options, timing).read();
+
+    // Build "bad" application with a method "foo" in "F".
+    SmaliBuilder builderBad = new SmaliBuilder();
+    for (String clazz : CLASSES) {
+      builderBad.addClass(clazz);
+      if (clazz.equals(ERRONEOUS_CLASS)) {
+        erroneousMethodSignature = builderBad.addStaticMethod(
+            "void", ERRONEOUS_METHOD, ImmutableList.of(), 0, "return-void");
+      } else {
+        builderBad.addStaticMethod(
+            "void", VALID_METHOD, ImmutableList.of(), 0, "return-void");
+      }
+    }
+    AndroidApp inputBad = AndroidApp.fromDexProgramData(builderBad.compile());
+    DexApplication appBad = new ApplicationReader(inputBad, options, timing).read();
+
+    ExecutorService executor = Executors.newWorkStealingPool();
+    try {
+      BisectState state = new BisectState(appGood, appBad, null);
+      DexProgramClass clazz = Bisect.run(state, this::command, temp.newFolder().toPath(), executor);
+      System.out.println("Found bad class: " + clazz);
+      assertEquals(clazz.type.toString(), ERRONEOUS_CLASS);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  private Result command(DexApplication application) {
+    DexInspector inspector = new DexInspector(application);
+    if (inspector
+        .clazz(ERRONEOUS_CLASS)
+        .method(erroneousMethodSignature.returnType,
+            erroneousMethodSignature.name,
+            erroneousMethodSignature.parameterTypes)
+        .isPresent()) {
+      return Result.BAD;
+    }
+    return Result.GOOD;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java b/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
new file mode 100644
index 0000000..ddd4f69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/code/InstructionFactoryTest.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.code;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.OffsetToObjectMapping;
+import java.nio.ByteBuffer;
+import org.junit.Test;
+
+/**
+ * Tests for the InstructionFactory.
+ */
+public class InstructionFactoryTest {
+
+  @Test
+  public void emptyBuffer() {
+    ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
+    InstructionFactory factory = new InstructionFactory();
+    Instruction[] instructions =
+        factory.readSequenceFrom(emptyBuffer, 0, 0, new OffsetToObjectMapping());
+    assertTrue(instructions.length == 0);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
new file mode 100644
index 0000000..228b9b3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatdx/CompatDxTests.java
@@ -0,0 +1,174 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.compatdx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.maindexlist.MainDexListTests;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class CompatDxTests {
+  private static final String EXAMPLE_JAR_FILE1 = "build/test/examples/arithmetic.jar";
+  private static final String EXAMPLE_JAR_FILE2 = "build/test/examples/barray.jar";
+
+  private static final String NO_LOCALS = "--no-locals";
+  private static final String NO_POSITIONS = "--positions=none";
+  private static final String MULTIDEX = "--multi-dex";
+  private static final String NUM_THREADS_5 = "--num-threads=5";
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void noFilesTest() throws IOException {
+    runDexer("--no-files");
+  }
+
+  @Test
+  public void noOutputTest() throws IOException {
+    runDexerWithoutOutput(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void singleJarInputFile() throws IOException {
+    runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void multipleJarInputFiles() throws IOException {
+    runDexer(NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1, EXAMPLE_JAR_FILE2);
+  }
+
+  @Test
+  public void outputZipFile() throws IOException {
+    runDexerWithOutput("foo.dex.zip", NO_POSITIONS, NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void useMultipleThreads() throws IOException {
+    runDexer(NUM_THREADS_5, NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void withPositions() throws IOException {
+    runDexer(NO_LOCALS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void withLocals() throws IOException {
+    runDexer(NO_POSITIONS, MULTIDEX, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void withoutMultidex() throws IOException {
+    runDexer(NO_POSITIONS, NO_LOCALS, EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void writeToNamedDexFile() throws IOException {
+    runDexerWithOutput("named-output.dex", EXAMPLE_JAR_FILE1);
+  }
+
+  @Test
+  public void singleDexProgramIsTooLarge() throws IOException {
+    thrown.expect(CompilationError.class);
+    runDexer(MainDexListTests.getTwoLargeClassesAppPath().toString());
+  }
+
+  private void runDexer(String... args) throws IOException {
+    runDexerWithOutput("", args);
+  }
+
+  private void runDexerWithoutOutput(String... args) throws IOException {
+    runDexerWithOutput(null, args);
+  }
+
+  private void runDexerWithOutput(String out, String... args) throws IOException {
+    Path d8Out = null;
+    Path dxOut = null;
+    if (out != null) {
+      d8Out = temp.newFolder("d8-out").toPath().resolve(out);
+      dxOut = temp.newFolder("dx-out").toPath().resolve(out);
+      assertNotEquals(d8Out, dxOut);
+    }
+
+    List<String> d8Args = new ArrayList<>(args.length + 2);
+    d8Args.add("--dex");
+    if (d8Out != null) {
+      d8Args.add("--output=" + d8Out);
+    }
+    Collections.addAll(d8Args, args);
+    System.out.println("running: d8 " + StringUtils.join(d8Args, " "));
+    CompatDx.main(d8Args.toArray(new String[d8Args.size()]));
+
+    List<String> dxArgs = new ArrayList<>(args.length + 2);
+    if (dxOut != null) {
+      dxArgs.add("--output=" + dxOut);
+    }
+    Collections.addAll(dxArgs, args);
+    System.out.println("running: dx " + StringUtils.join(dxArgs, " "));
+    ToolHelper.runDX(dxArgs.toArray(new String[dxArgs.size()]));
+
+    if (out == null) {
+      // Can't check output if explicitly not writing any.
+      return;
+    }
+
+    List<Path> d8Files = Files.list(Files.isDirectory(d8Out) ? d8Out : d8Out.getParent())
+        .sorted().collect(Collectors.toList());
+    List<Path> dxFiles = Files.list(Files.isDirectory(dxOut) ? dxOut : dxOut.getParent())
+        .sorted().collect(Collectors.toList());
+    assertEquals("Out file names differ",
+        StringUtils.join(dxFiles, "\n", BraceType.NONE, (file) ->
+            file.getFileName().toString()),
+        StringUtils.join(d8Files, "\n", BraceType.NONE, (file) ->
+            file.getFileName().toString()));
+
+    for (int i = 0; i < d8Files.size(); i++) {
+      if (FileUtils.isArchive(d8Files.get(i))) {
+        compareArchiveFiles(d8Files.get(i), dxFiles.get(i));
+      }
+    }
+  }
+
+  private void compareArchiveFiles(Path d8File, Path dxFile) throws IOException {
+    ZipFile d8Zip = new ZipFile(d8File.toFile());
+    ZipFile dxZip = new ZipFile(dxFile.toFile());
+    // TODO(zerny): This should test resource containment too once supported.
+    Set<String> d8Content = d8Zip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+    Set<String> dxContent = dxZip.stream().map(ZipEntry::getName).collect(Collectors.toSet());
+    for (String entry : d8Content) {
+      assertTrue("Expected dx output to contain " + entry, dxContent.contains(entry));
+    }
+    for (String entry : dxContent) {
+      if (FileUtils.isDexFile(Paths.get(entry))) {
+        assertTrue("Expected d8 output to contain " + entry, d8Content.contains(entry));
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
new file mode 100644
index 0000000..0d72a52
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
@@ -0,0 +1,184 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.d8;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.D8Output;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class DexVersionTests {
+
+  private static final Path ARITHMETIC_JAR =
+      Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/arithmetic.jar");
+
+  private static final Path ARRAYACCESS_JAR =
+      Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/arrayaccess.jar");
+
+  @Rule public TemporaryFolder defaultApiFolder1 = ToolHelper.getTemporaryFolderForTest();
+  @Rule public TemporaryFolder defaultApiFolder2 = ToolHelper.getTemporaryFolderForTest();
+  @Rule public TemporaryFolder androidOApiFolder1 = ToolHelper.getTemporaryFolderForTest();
+  @Rule public TemporaryFolder androidOApiFolder2 = ToolHelper.getTemporaryFolderForTest();
+  @Rule public TemporaryFolder androidNApiFolder1 = ToolHelper.getTemporaryFolderForTest();
+  @Rule public TemporaryFolder androidNApiFolder2 = ToolHelper.getTemporaryFolderForTest();
+
+  @Before
+  public void compileVersions() throws CompilationException, IOException {
+    D8Command.Builder arithmeticBuilder = D8Command.builder().addProgramFiles(ARITHMETIC_JAR);
+    D8Command.Builder arrayAccessBuilder = D8Command.builder().addProgramFiles(ARRAYACCESS_JAR);
+    D8Output output = D8.run(arrayAccessBuilder.build());
+    output.write(defaultApiFolder1.getRoot().toPath());
+    output = D8.run(arrayAccessBuilder.setMinApiLevel(Constants.ANDROID_O_API).build());
+    output.write(androidOApiFolder1.getRoot().toPath());
+    output = D8.run(arrayAccessBuilder.setMinApiLevel(Constants.ANDROID_N_API).build());
+    output.write(androidNApiFolder1.getRoot().toPath());
+    output = D8.run(arithmeticBuilder.build());
+    output.write(defaultApiFolder2.getRoot().toPath());
+    output = D8.run(arithmeticBuilder.setMinApiLevel(Constants.ANDROID_O_API).build());
+    output.write(androidOApiFolder2.getRoot().toPath());
+    output = D8.run(arithmeticBuilder.setMinApiLevel(Constants.ANDROID_N_API).build());
+    output.write(androidNApiFolder2.getRoot().toPath());
+  }
+
+  private Path default1() {
+    return defaultApiFolder1.getRoot().toPath().resolve("classes.dex");
+  }
+
+  private Path default2() {
+    return defaultApiFolder2.getRoot().toPath().resolve("classes.dex");
+  }
+
+  private Path androidO1() {
+    return androidOApiFolder1.getRoot().toPath().resolve("classes.dex");
+  }
+
+  private Path androidO2() {
+    return androidOApiFolder2.getRoot().toPath().resolve("classes.dex");
+  }
+
+  private Path androidN1() {
+    return androidNApiFolder1.getRoot().toPath().resolve("classes.dex");
+  }
+
+  private Path androidN2() {
+    return androidNApiFolder2.getRoot().toPath().resolve("classes.dex");
+  }
+
+  @Test
+  public void mergeCompatibleVersions() throws CompilationException, IOException {
+    // Verify that we can merge between all versions when no explicit min sdk version is set.
+    D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(default2()).build());
+    D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(androidO2()).build());
+    D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(androidN2()).build());
+    D8.run(D8Command.builder().addProgramFiles(androidO1()).addProgramFiles(androidN2()).build());
+    D8.run(D8Command.builder().addProgramFiles(androidO1()).addProgramFiles(androidO2()).build());
+    D8.run(D8Command.builder().addProgramFiles(androidN1()).addProgramFiles(androidN2()).build());
+    // Verify that we can merge between all version when api version is explicitly
+    // set to Android O.
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(default2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(androidO2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(androidN2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(androidO1())
+            .addProgramFiles(androidN2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(androidO1())
+            .addProgramFiles(androidO2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(androidN1())
+            .addProgramFiles(androidN2())
+            .build());
+    // Verify that we can merge up to version N when api version is explicitly set to
+    // Android N.
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_N_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(default2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_N_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(androidN2())
+            .build());
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_N_API)
+            .addProgramFiles(androidN1())
+            .addProgramFiles(androidN2())
+            .build());
+    // Verify that we can merge default api version when api version is explicitly set to
+    // Android K.
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_K_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(default2())
+            .build());
+  }
+
+  @Test(expected = CompilationError.class)
+  public void mergeErrorVersionNWithVersionOInput() throws CompilationException, IOException {
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_N_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(androidO2())
+            .build());
+  }
+
+  @Test(expected = CompilationError.class)
+  public void mergeErrorVersionKWithVersionOInput() throws CompilationException, IOException {
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_K_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(androidO2())
+            .build());
+  }
+
+  @Test(expected = CompilationError.class)
+  public void mergeErrorVersionKWithVersionNInput() throws CompilationException, IOException {
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_K_API)
+            .addProgramFiles(default1())
+            .addProgramFiles(androidN2())
+            .build());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
new file mode 100644
index 0000000..0b042a9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -0,0 +1,793 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.Event;
+import org.apache.harmony.jpda.tests.framework.jdwp.EventBuilder;
+import org.apache.harmony.jpda.tests.framework.jdwp.EventPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands.StackFrameCommandSet;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.EventKind;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.StepDepth;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.StepSize;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.SuspendPolicy;
+import org.apache.harmony.jpda.tests.framework.jdwp.Location;
+import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent;
+import org.apache.harmony.jpda.tests.framework.jdwp.ParsedEvent.EventThread;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.apache.harmony.jpda.tests.framework.jdwp.VmMirror;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase;
+import org.apache.harmony.jpda.tests.share.JPDATestOptions;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+/**
+ *
+ * Base class for debugging tests
+ */
+public abstract class DebugTestBase {
+
+  public static final StepFilter NO_FILTER = new StepFilter.NoStepFilter();
+  public static final StepFilter INTELLIJ_FILTER = new StepFilter.IntelliJStepFilter();
+  private static final StepFilter DEFAULT_FILTER = NO_FILTER;
+
+  // Set to true to run tests with java
+  private static final boolean RUN_DEBUGGEE_WITH_JAVA = false;
+
+  // Set to true to enable verbose logs
+  private static final boolean DEBUG_TESTS = false;
+
+  private static final List<DexVm> UNSUPPORTED_ART_VERSIONS = ImmutableList.of(
+      // Dalvik does not support command ReferenceType.Methods which is used to set breakpoint.
+      // TODO(shertz) use command ReferenceType.MethodsWithGeneric instead
+      DexVm.ART_4_4_4,
+      // Older runtimes fail on buildbot
+      // TODO(shertz) re-enable once issue is solved
+      DexVm.ART_5_1_1,
+      DexVm.ART_6_0_1);
+
+  private static final Path DEBUGGEE_JAR = Paths
+      .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
+
+  @ClassRule
+  public static TemporaryFolder temp = new TemporaryFolder();
+  private static Path jdwpDexD8 = null;
+  private static Path debuggeeDexD8 = null;
+
+  @Rule
+  public TestName testName = new TestName();
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    // Convert jar to dex with d8 with debug info
+    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    {
+      Path jdwpJar = ToolHelper.getJdwpTestsJarPath(minSdk);
+      Path dexOutputDir = temp.newFolder("d8-jdwp-jar").toPath();
+      jdwpDexD8 = dexOutputDir.resolve("classes.dex");
+      D8.run(
+          D8Command.builder()
+              .addProgramFiles(jdwpJar)
+              .setOutputPath(dexOutputDir)
+              .setMinApiLevel(minSdk)
+              .setMode(CompilationMode.DEBUG)
+              .build());
+    }
+    {
+      Path dexOutputDir = temp.newFolder("d8-debuggee-jar").toPath();
+      debuggeeDexD8 = dexOutputDir.resolve("classes.dex");
+      D8.run(
+          D8Command.builder()
+              .addProgramFiles(DEBUGGEE_JAR)
+              .setOutputPath(dexOutputDir)
+              .setMinApiLevel(minSdk)
+              .setMode(CompilationMode.DEBUG)
+              .build());
+    }
+  }
+
+  protected final void runDebugTest(String debuggeeClass, JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runDebugTest(debuggeeClass, Arrays.asList(commands));
+  }
+
+  protected final void runDebugTest(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
+    // Skip test due to unsupported runtime.
+    Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
+        ToolHelper.artSupported());
+    Assume.assumeFalse(
+        "Skipping failing test " + testName.getMethodName() + " for runtime " + ToolHelper
+            .getDexVm(), UNSUPPORTED_ART_VERSIONS.contains(ToolHelper.getDexVm()));
+
+    // Run with ART.
+    String[] paths = new String[]{jdwpDexD8.toString(), debuggeeDexD8.toString()};
+    new JUnit3Wrapper(debuggeeClass, paths, commands).runBare();
+  }
+
+  protected final JUnit3Wrapper.Command run() {
+    return new JUnit3Wrapper.Command.RunCommand();
+  }
+
+  protected final JUnit3Wrapper.Command breakpoint(String className, String methodName) {
+    return new JUnit3Wrapper.Command.BreakpointCommand(className, methodName);
+  }
+
+  protected final JUnit3Wrapper.Command stepOver() {
+    return stepOver(DEFAULT_FILTER);
+  }
+
+  protected final JUnit3Wrapper.Command stepOver(StepFilter stepFilter) {
+    return step(StepDepth.OVER, stepFilter);
+  }
+
+  protected final JUnit3Wrapper.Command stepOut() {
+    return stepOut(DEFAULT_FILTER);
+  }
+
+  protected final JUnit3Wrapper.Command stepOut(StepFilter stepFilter) {
+    return step(StepDepth.OUT, stepFilter);
+  }
+
+  protected final JUnit3Wrapper.Command stepInto() {
+    return stepInto(DEFAULT_FILTER);
+  }
+
+  protected final JUnit3Wrapper.Command stepInto(StepFilter stepFilter) {
+    return step(StepDepth.INTO, stepFilter);
+  }
+
+  private JUnit3Wrapper.Command step(byte stepDepth,
+      StepFilter stepFilter) {
+    return new JUnit3Wrapper.Command.StepCommand(stepDepth, stepFilter);
+  }
+
+  protected final JUnit3Wrapper.Command checkLocal(String localName, Value expectedValue) {
+    return inspect(t -> t.checkLocal(localName, expectedValue));
+  }
+
+  protected final JUnit3Wrapper.Command checkNoLocal() {
+    return inspect(t -> Assert.assertTrue(t.getLocalNames().isEmpty()));
+  }
+
+  protected final JUnit3Wrapper.Command checkLine(int line) {
+    return inspect(t -> t.checkLine(line));
+  }
+
+  protected final JUnit3Wrapper.Command checkMethod(String className, String methodName) {
+    return inspect(t -> t.checkMethod(className, methodName));
+  }
+
+  protected final JUnit3Wrapper.Command inspect(Consumer<JUnit3Wrapper.DebuggeeState> inspector) {
+    return t -> inspector.accept(t.debuggeeState);
+  }
+
+  protected final JUnit3Wrapper.Command setLocal(String localName, Value newValue) {
+    return new JUnit3Wrapper.Command.SetLocalCommand(localName, newValue);
+  }
+
+  @Ignore("Prevents Gradle from running the wrapper as a test.")
+  static class JUnit3Wrapper extends JDWPTestCase {
+
+    private final String debuggeeClassName;
+
+    private final String[] debuggeePath;
+
+    // Initially, the runtime is suspended so we're ready to process commands.
+    private State state = State.ProcessCommand;
+
+    /**
+     * Represents the context of the debuggee suspension. This is {@code null} when the debuggee is
+     * not suspended.
+     */
+    private DebuggeeState debuggeeState = null;
+
+    private final Queue<Command> commandsQueue;
+
+    // Active event requests.
+    private final Map<Integer, EventHandler> events = new TreeMap<>();
+
+    JUnit3Wrapper(String debuggeeClassName, String[] debuggeePath, List<Command> commands) {
+      this.debuggeeClassName = debuggeeClassName;
+      this.debuggeePath = debuggeePath;
+      this.commandsQueue = new ArrayDeque<>(commands);
+    }
+
+    @Override
+    protected void runTest() throws Throwable {
+      if (DEBUG_TESTS) {
+        logWriter.println("Starts loop with " + commandsQueue.size() + " command(s) to process");
+      }
+
+      boolean exited = false;
+      while (!exited) {
+        if (DEBUG_TESTS) {
+          logWriter.println("Loop on state " + state.name());
+        }
+        switch (state) {
+          case ProcessCommand: {
+            Command command = commandsQueue.poll();
+            assert command != null;
+            if (DEBUG_TESTS) {
+              logWriter.println("Process command " + command.toString());
+            }
+            command.perform(this);
+            break;
+          }
+          case WaitForEvent:
+            processEvents();
+            break;
+          case Exit:
+            exited = true;
+            break;
+          default:
+            throw new AssertionError();
+        }
+      }
+
+      assertTrue("All commands have NOT been processed", commandsQueue.isEmpty());
+
+      logWriter.println("Finish loop");
+    }
+
+    @Override
+    protected String getDebuggeeClassName() {
+      return debuggeeClassName;
+    }
+
+    private enum State {
+      /**
+       * Process next command
+       */
+      ProcessCommand,
+      /**
+       * Wait for the next event
+       */
+      WaitForEvent,
+      /**
+       * The debuggee has exited
+       */
+      Exit
+    }
+
+    private void processEvents() {
+      EventPacket eventPacket = getMirror().receiveEvent();
+      ParsedEvent[] parsedEvents = ParsedEvent.parseEventPacket(eventPacket);
+      if (DEBUG_TESTS) {
+        logWriter.println("Received " + parsedEvents.length + " event(s)");
+        for (int i = 0; i < parsedEvents.length; ++i) {
+          String msg = String.format("#%d: %s (id=%d)", Integer.valueOf(i),
+              JDWPConstants.EventKind.getName(parsedEvents[i].getEventKind()),
+              Integer.valueOf(parsedEvents[i].getRequestID()));
+          logWriter.println(msg);
+        }
+      }
+      // We only expect one event at a time.
+      assertEquals(1, parsedEvents.length);
+      ParsedEvent parsedEvent = parsedEvents[0];
+      byte eventKind = parsedEvent.getEventKind();
+      int requestID = parsedEvent.getRequestID();
+
+      if (eventKind == JDWPConstants.EventKind.VM_DEATH) {
+        // Special event when debuggee is about to terminate.
+        assertEquals(0, requestID);
+        setState(State.Exit);
+      } else {
+        assert parsedEvent.getSuspendPolicy() == SuspendPolicy.ALL;
+
+        // Capture the context of the event suspension.
+        updateEventContext((EventThread) parsedEvent);
+
+        // Handle event.
+        EventHandler eh = events.get(requestID);
+        assert eh != null;
+        eh.handle(this);
+      }
+    }
+
+    //
+    // Inspection
+    //
+
+    /**
+     * Allows to inspect the state of a debuggee when it is suspended.
+     */
+    public class DebuggeeState {
+
+      private final long threadId;
+      private final long frameId;
+      private final Location location;
+
+      public DebuggeeState(long threadId, long frameId, Location location) {
+        this.threadId = threadId;
+        this.frameId = frameId;
+        this.location = location;
+      }
+
+      public long getThreadId() {
+        return threadId;
+      }
+
+      public long getFrameId() {
+        return frameId;
+      }
+
+      public Location getLocation() {
+        return this.location;
+      }
+
+      public void checkLocal(String localName, Value expectedValue) {
+        Variable localVar = getVariableAt(getLocation(), localName);
+
+        // Get value
+        CommandPacket commandPacket = new CommandPacket(
+            JDWPCommands.StackFrameCommandSet.CommandSetID,
+            JDWPCommands.StackFrameCommandSet.GetValuesCommand);
+        commandPacket.setNextValueAsThreadID(getThreadId());
+        commandPacket.setNextValueAsFrameID(getFrameId());
+        commandPacket.setNextValueAsInt(1);
+        commandPacket.setNextValueAsInt(localVar.getSlot());
+        commandPacket.setNextValueAsByte(localVar.getTag());
+        ReplyPacket replyPacket = getMirror().performCommand(commandPacket);
+        checkReplyPacket(replyPacket, "StackFrame.GetValues command");
+        int valuesCount = replyPacket.getNextValueAsInt();
+        assert valuesCount == 1;
+        Value localValue = replyPacket.getNextValueAsValue();
+        assertAllDataRead(replyPacket);
+
+        Assert.assertEquals(expectedValue, localValue);
+      }
+
+      public void checkLine(int line) {
+        Location location = getLocation();
+        int currentLine = getMirror()
+            .getLineNumber(location.classID, location.methodID, location.index);
+        Assert.assertEquals(line, currentLine);
+      }
+
+      public List<String> getLocalNames() {
+        return getVariablesAt(location).stream().map(v -> v.getName()).collect(Collectors.toList());
+      }
+
+      public void checkMethod(String className, String methodName) {
+        String currentClassSig = getMirror().getClassSignature(location.classID);
+        assert currentClassSig.charAt(0) == 'L';
+        String currentClassName = currentClassSig.substring(1, currentClassSig.length() - 1)
+            .replace('/', '.');
+        Assert.assertEquals("Incorrect class name", className, currentClassName);
+
+        String currentMethodName = getMirror().getMethodName(location.classID, location.methodID);
+        Assert.assertEquals("Incorrect method name", methodName, currentMethodName);
+      }
+    }
+
+    private static boolean inScope(long index, Variable var) {
+      long varStart = var.getCodeIndex();
+      long varEnd = varStart + var.getLength();
+      return index >= varStart && index < varEnd;
+    }
+
+    private Variable getVariableAt(Location location, String localName) {
+      return getVariablesAt(location).stream()
+          .filter(v -> localName.equals(v.getName()))
+          .findFirst()
+          .get();
+    }
+
+    private List<Variable> getVariablesAt(Location location) {
+      // Get variable table and keep only variables visible at this location.
+      return getVariables(location.classID, location.methodID).stream()
+          .filter(v -> inScope(location.index, v))
+          .collect(Collectors.toList());
+    }
+
+    private List<Variable> getVariables(long classID, long methodID) {
+      List<Variable> list = getMirror().getVariableTable(classID, methodID);
+      return list != null ? list : Collections.emptyList();
+    }
+
+    private void setState(State state) {
+      this.state = state;
+    }
+
+    public DebuggeeState getDebuggeeState() {
+      return debuggeeState;
+    }
+
+    private void updateEventContext(EventThread event) {
+      long threadId = event.getThreadID();
+      long frameId = -1;
+      Location location = null;
+      // ART returns an error if we ask for frames when there is none. Workaround by asking the frame
+      // count first.
+      int frameCount = getMirror().getFrameCount(threadId);
+      if (frameCount > 0) {
+        ReplyPacket replyPacket = getMirror().getThreadFrames(threadId, 0, 1);
+        {
+          int number = replyPacket.getNextValueAsInt();
+          assertEquals(1, number);
+        }
+        frameId = replyPacket.getNextValueAsFrameID();
+        location = replyPacket.getNextValueAsLocation();
+        assertAllDataRead(replyPacket);
+      }
+      debuggeeState = new DebuggeeState(threadId, frameId, location);
+    }
+
+    private VmMirror getMirror() {
+      return debuggeeWrapper.vmMirror;
+    }
+
+    private void resume() {
+      debuggeeState = null;
+      getMirror().resume();
+      setState(State.WaitForEvent);
+    }
+
+    private boolean installBreakpoint(BreakpointInfo breakpointInfo) {
+      final long classId = getMirror().getClassID(getClassSignature(breakpointInfo.className));
+      if (classId == -1) {
+        // The class is not ready yet. Request a CLASS_PREPARE to delay the installation of the
+        // breakpoint.
+        ReplyPacket replyPacket = getMirror().setClassPrepared(breakpointInfo.className);
+        int classPrepareRequestId = replyPacket.getNextValueAsInt();
+        assertAllDataRead(replyPacket);
+        events.put(Integer.valueOf(classPrepareRequestId),
+            new ClassPrepareHandler(breakpointInfo, classPrepareRequestId));
+        return false;
+      } else {
+        int breakpointId = getMirror()
+            .setBreakpointAtMethodBegin(classId, breakpointInfo.methodName);
+        // Nothing to do on breakpoint
+        events.put(Integer.valueOf(breakpointId), new DefaultEventHandler());
+        return true;
+      }
+    }
+
+    //
+    // Command processing
+    //
+    public interface Command {
+
+      void perform(JUnit3Wrapper testBase);
+
+      class RunCommand implements Command {
+
+        @Override
+        public void perform(JUnit3Wrapper testBase) {
+          testBase.resume();
+        }
+
+        @Override
+        public String toString() {
+          return "run";
+        }
+      }
+
+      // TODO(shertz) add method signature support (when multiple methods have the same name)
+      class BreakpointCommand implements Command {
+
+        private final String className;
+        private final String methodName;
+
+        public BreakpointCommand(String className, String methodName) {
+          assert className != null;
+          assert methodName != null;
+          this.className = className;
+          this.methodName = methodName;
+        }
+
+        @Override
+        public void perform(JUnit3Wrapper testBase) {
+          testBase.installBreakpoint(new BreakpointInfo(className, methodName));
+        }
+
+        @Override
+        public String toString() {
+          StringBuilder sb = new StringBuilder();
+          sb.append("breakpoint");
+          sb.append(" class=");
+          sb.append(className);
+          sb.append(" method=");
+          sb.append(methodName);
+          return sb.toString();
+        }
+      }
+
+      class StepCommand implements Command {
+
+        private final byte stepDepth;
+        private final StepFilter stepFilter;
+
+        public StepCommand(byte stepDepth,
+            StepFilter stepFilter) {
+          this.stepDepth = stepDepth;
+          this.stepFilter = stepFilter;
+        }
+
+        @Override
+        public void perform(JUnit3Wrapper testBase) {
+          long threadId = testBase.getDebuggeeState().getThreadId();
+          int stepRequestID;
+          {
+            EventBuilder eventBuilder = Event.builder(EventKind.SINGLE_STEP, SuspendPolicy.ALL);
+            eventBuilder.setStep(threadId, StepSize.LINE, stepDepth);
+            stepFilter.getExcludedClasses().stream().forEach(s -> eventBuilder.setClassExclude(s));
+            ReplyPacket replyPacket = testBase.getMirror().setEvent(eventBuilder.build());
+            stepRequestID = replyPacket.getNextValueAsInt();
+            testBase.assertAllDataRead(replyPacket);
+          }
+          testBase.events.put(stepRequestID, new StepEventHandler(stepRequestID, stepFilter));
+
+          // Resume all threads.
+          testBase.resume();
+        }
+
+        @Override
+        public String toString() {
+          return "step " + JDWPConstants.StepDepth.getName(stepDepth);
+        }
+      }
+
+      class SetLocalCommand implements Command {
+
+        private final String localName;
+        private final Value newValue;
+
+        public SetLocalCommand(String localName, Value newValue) {
+          this.localName = localName;
+          this.newValue = newValue;
+        }
+
+        @Override
+        public void perform(JUnit3Wrapper testBase) {
+          Variable v = testBase.getVariableAt(testBase.debuggeeState.location, localName);
+          CommandPacket setValues = new CommandPacket(StackFrameCommandSet.CommandSetID,
+              StackFrameCommandSet.SetValuesCommand);
+          setValues.setNextValueAsThreadID(testBase.getDebuggeeState().getThreadId());
+          setValues.setNextValueAsFrameID(testBase.getDebuggeeState().getFrameId());
+          setValues.setNextValueAsInt(1);
+          setValues.setNextValueAsInt(v.getSlot());
+          setValues.setNextValueAsValue(newValue);
+          ReplyPacket replyPacket = testBase.getMirror().performCommand(setValues);
+          testBase.checkReplyPacket(replyPacket, "StackFrame.SetValues");
+        }
+      }
+    }
+
+    //
+    // Event handling
+    //
+    private interface EventHandler {
+
+      void handle(JUnit3Wrapper testBase);
+    }
+
+    private static class DefaultEventHandler implements EventHandler {
+
+      @Override
+      public void handle(JUnit3Wrapper testBase) {
+        testBase.setState(State.ProcessCommand);
+      }
+    }
+
+    private static class StepEventHandler extends DefaultEventHandler {
+
+      private final int stepRequestID;
+      private final StepFilter stepFilter;
+
+      private StepEventHandler(int stepRequestID,
+          StepFilter stepFilter) {
+        this.stepRequestID = stepRequestID;
+        this.stepFilter = stepFilter;
+      }
+
+      @Override
+      public void handle(JUnit3Wrapper testBase) {
+        if (stepFilter
+            .skipLocation(testBase.getMirror(), testBase.getDebuggeeState().getLocation())) {
+          // Keep the step active and resume so that we do another step.
+          testBase.resume();
+        } else {
+          // When hit, the single step must be cleared.
+          testBase.getMirror().clearEvent(EventKind.SINGLE_STEP, stepRequestID);
+          testBase.events.remove(Integer.valueOf(stepRequestID));
+          super.handle(testBase);
+        }
+      }
+    }
+
+    private static class BreakpointInfo {
+
+      private final String className;
+      private final String methodName;
+
+      private BreakpointInfo(String className, String methodName) {
+        this.className = className;
+        this.methodName = methodName;
+      }
+    }
+
+    /**
+     * CLASS_PREPARE signals us that we can install a breakpoint
+     */
+    private static class ClassPrepareHandler implements EventHandler {
+
+      private final BreakpointInfo breakpointInfo;
+      private final int classPrepareRequestId;
+
+      private ClassPrepareHandler(BreakpointInfo breakpointInfo, int classPrepareRequestId) {
+        this.breakpointInfo = breakpointInfo;
+        this.classPrepareRequestId = classPrepareRequestId;
+      }
+
+      @Override
+      public void handle(JUnit3Wrapper testBase) {
+        // Remove the CLASS_PREPARE
+        testBase.events.remove(Integer.valueOf(classPrepareRequestId));
+        testBase.getMirror().clearEvent(JDWPConstants.EventKind.CLASS_PREPARE,
+            classPrepareRequestId);
+
+        // Install breakpoint now.
+        boolean success = testBase.installBreakpoint(breakpointInfo);
+        Assert.assertTrue("Failed to insert breakpoint after class has been prepared", success);
+
+        // Resume now
+        testBase.resume();
+      }
+    }
+
+    @Override
+    protected JPDATestOptions createTestOptions() {
+      if (RUN_DEBUGGEE_WITH_JAVA) {
+        return super.createTestOptions();
+      } else {
+        // Override properties to run debuggee with ART/Dalvik.
+        class ArtTestOptions extends JPDATestOptions {
+
+          ArtTestOptions(String[] debuggeePath) {
+            // Set debuggee command-line.
+            if (!RUN_DEBUGGEE_WITH_JAVA) {
+              ArtCommandBuilder artCommandBuilder = new ArtCommandBuilder(ToolHelper.getDexVm());
+              if (ToolHelper.getDexVm().isNewerThan(DexVm.ART_5_1_1)) {
+                artCommandBuilder.appendArtOption("-Xcompiler-option");
+                artCommandBuilder.appendArtOption("--debuggable");
+                artCommandBuilder.appendArtOption("-Xcompiler-option");
+                artCommandBuilder.appendArtOption("--compiler-filter=interpret-only");
+              }
+              setProperty("jpda.settings.debuggeeJavaPath", artCommandBuilder.build());
+
+              // Set debuggee classpath
+              String debuggeeClassPath = String.join(File.pathSeparator, debuggeePath);
+              setProperty("jpda.settings.debuggeeClasspath", debuggeeClassPath);
+            }
+
+            // Set verbosity
+            setProperty("jpda.settings.verbose", Boolean.toString(DEBUG_TESTS));
+          }
+        }
+        return new ArtTestOptions(debuggeePath);
+      }
+    }
+  }
+
+  //
+  // Step filtering
+  //
+
+  interface StepFilter {
+
+    /**
+     * Provides a list of class name to be skipped when single stepping. This can be a fully
+     * qualified name (like java.lang.String) or a subpackage (like java.util.*).
+     */
+    List<String> getExcludedClasses();
+
+    /**
+     * Indicates whether the given location must be skipped.
+     */
+    boolean skipLocation(VmMirror mirror, Location location);
+
+    /**
+     * A {@link StepFilter} that does not filter anything.
+     */
+    class NoStepFilter implements StepFilter {
+
+      @Override
+      public List<String> getExcludedClasses() {
+        return Collections.emptyList();
+      }
+
+      @Override
+      public boolean skipLocation(VmMirror mirror, Location location) {
+        return false;
+      }
+    }
+
+    /**
+     * A {@link StepFilter} that matches the default behavior of IntelliJ regarding single
+     * stepping.
+     */
+    class IntelliJStepFilter implements StepFilter {
+      // This is the value specified by JDWP in documentation of ReferenceType.Methods command.
+      private static final int SYNTHETIC_FLAG = 0xF0000000;
+
+      @Override
+      public List<String> getExcludedClasses() {
+        return Arrays.asList(
+            "com.sun.*",
+            "java.*",
+            "javax.*",
+            "org.omg.*",
+            "sun.*",
+            "jdk.internal.*",
+            "junit.*",
+            "com.intellij.rt.*",
+            "com.yourkit.runtime.*",
+            "com.springsource.loaded.*",
+            "org.springsource.loaded.*",
+            "javassist.*",
+            "org.apache.webbeans.*",
+            "com.ibm.ws.*",
+            "kotlin.*"
+        );
+      }
+
+      @Override
+      public boolean skipLocation(VmMirror mirror, Location location) {
+        // TODO(shertz) we also need to skip class loaders to act like IntelliJ.
+        // Skip synthetic methods.
+        return isSyntheticMethod(mirror, location);
+      }
+
+      private static boolean isSyntheticMethod(VmMirror mirror, Location location) {
+        // We must gather the modifiers of the method. This is only possible using
+        // ReferenceType.Methods command which gather information about all methods in a class.
+        ReplyPacket reply = mirror.getMethods(location.classID);
+        int methodsCount = reply.getNextValueAsInt();
+        for (int i = 0; i < methodsCount; ++i) {
+          long methodId = reply.getNextValueAsMethodID();
+          reply.getNextValueAsString();  // skip method name
+          reply.getNextValueAsString();  // skip method signature
+          int modifiers = reply.getNextValueAsInt();
+          if (methodId == location.methodID &&
+              ((modifiers & SYNTHETIC_FLAG) != 0)) {
+            return true;
+          }
+        }
+        return false;
+      }
+    }
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
new file mode 100644
index 0000000..39091fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestExamples.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import java.util.Collections;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Test;
+
+/**
+ * Examples of debug test features.
+ */
+public class DebugTestExamples extends DebugTestBase {
+
+  /**
+   * Simple test that runs the debuggee until it exits.
+   */
+  @Test
+  public void testRun() throws Throwable {
+    runDebugTest("Arithmetic", Collections.singletonList(run()));
+  }
+
+  /**
+   * Tests that we do suspend on breakpoint then continue.
+   */
+  @Test
+  public void testBreakpoint_Hit() throws Throwable {
+    runDebugTest("Arithmetic",
+        breakpoint("Arithmetic", "bitwiseInts"),
+        run(),
+        run());
+  }
+
+  /**
+   * Tests that we can check local variables at a suspension point (breakpoint).
+   */
+  @Test
+  public void testLocalsOnBreakpoint() throws Throwable {
+    runDebugTest("Arithmetic",
+        breakpoint("Arithmetic", "bitwiseInts"),
+        run(),
+        checkLocal("x", Value.createInt(12345)),
+        checkLocal("y", Value.createInt(54321)),
+        run());
+  }
+
+  /**
+   * Tests that we can check local variables at different suspension points (breakpoint then step).
+   */
+  @Test
+  public void testLocalsOnBreakpointThenStep() throws Throwable {
+    runDebugTest("Arithmetic",
+        breakpoint("Arithmetic", "bitwiseInts"),
+        run(),
+        checkLocal("x", Value.createInt(12345)),
+        checkLocal("y", Value.createInt(54321)),
+        stepOver(),
+        checkLocal("x", Value.createInt(12345)),
+        checkLocal("y", Value.createInt(54321)),
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
new file mode 100644
index 0000000..96f4499
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import org.junit.Test;
+
+/**
+ * Tests debugging behavior with regards to exception handling
+ */
+public class ExceptionTest extends DebugTestBase {
+
+  @Test
+  public void testStepOnCatch() throws Throwable {
+    runDebugTest("Exceptions",
+        breakpoint("Exceptions", "catchException"),
+        run(),
+        checkLine(9), // line of the method call throwing the exception
+        stepOver(),
+        checkLine(10), // line of the catch declaration
+        run());
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
new file mode 100644
index 0000000..0d72ab9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import com.android.tools.r8.debug.DebugTestBase;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Test;
+
+/**
+ * Tests local variable information.
+ */
+public class LocalsTest extends DebugTestBase {
+
+  @Test
+  public void testNoLocal() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "noLocals";
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(8),
+        checkNoLocal(),
+        stepOver(),
+        checkMethod(className, methodName),
+        checkLine(9),
+        checkNoLocal(),
+        run());
+  }
+
+  @Test
+  public void testUnusedLocal() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "unusedLocals";
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(12),
+        checkNoLocal(),
+        stepOver(),
+        checkLine(13),
+        checkLocal("i", Value.createInt(Integer.MAX_VALUE)),
+        run());
+  }
+
+  @Test
+  public void testConstantLocal() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "constantLocals";
+    Value pValue = Value.createInt(10);
+    Value cValue = Value.createInt(5);
+    Value vValue = Value.createInt(pValue.getIntValue() + cValue.getIntValue());
+
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(17),
+        checkLocal("p", pValue),
+        stepOver(),
+        checkLine(18),
+        checkLocal("p", pValue),
+        checkLocal("c", cValue),
+        stepOver(),
+        checkLine(19),
+        checkLocal("p", pValue),
+        checkLocal("c", cValue),
+        checkLocal("v", vValue),
+        run());
+  }
+
+  @Test
+  public void testConstantLocalWithUpdate() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "constantLocals";
+    Value pValue = Value.createInt(10);
+    Value cValue = Value.createInt(5);
+    Value newValue = Value.createInt(5);
+    Value vValue = Value.createInt(pValue.getIntValue() + newValue.getIntValue());
+
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(17),
+        checkLocal("p", pValue),
+        stepOver(),
+        checkLine(18),
+        checkLocal("p", pValue),
+        checkLocal("c", cValue),
+        setLocal("c", newValue),
+        checkLocal("c", newValue),  // we should see the updated value
+        stepOver(),
+        checkLine(19),
+        checkLocal("p", pValue),
+        checkLocal("c", newValue),
+        checkLocal("v", vValue),
+        run());
+  }
+
+  @Test
+  public void testZeroLocals() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "zeroLocals";
+    final Value newValueForI = Value.createInt(10);
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(23),
+        checkNoLocal(),
+        stepOver(),
+        checkMethod(className, methodName),
+        checkLine(24),
+        checkLocal("i", Value.createInt(0)),
+        setLocal("i", newValueForI),
+        stepOver(),
+        checkLine(25),
+        checkLocal("i", newValueForI),
+        checkLocal("f", Value.createFloat(0)),
+        run());
+  }
+
+  @Test
+  public void testNoFlowOptimization() throws Throwable {
+    final String className = "Locals";
+    final String methodName = "noFlowOptimization";
+    final Value oldValueForI = Value.createInt(0);
+    final Value newValueForI = Value.createInt(10);
+    runDebugTest(className,
+        breakpoint(className, methodName),
+        run(),
+        checkMethod(className, methodName),
+        checkLine(29),
+        checkNoLocal(),
+        stepOver(),
+        checkMethod(className, methodName),
+        checkLine(30),
+        checkLocal("i", oldValueForI),
+        setLocal("i", newValueForI),
+        stepOver(),
+        checkLine(33),
+        checkLocal("i", newValueForI),
+        run());
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java b/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
new file mode 100644
index 0000000..0b7973e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/MultipleReturnsTest.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debug;
+
+import org.junit.Test;
+
+/**
+ * Tests debugging of method with multiple return statements.
+ */
+public class MultipleReturnsTest extends DebugTestBase {
+
+  @Test
+  public void testMultipleReturns() throws Throwable {
+    runDebugTest("MultipleReturns",
+        breakpoint("MultipleReturns", "multipleReturns"),
+        run(),
+        stepOver(),
+        checkLine(16), // this should be the 1st return statement
+        run(),
+        stepOver(),
+        checkLine(18), // this should be the 2nd return statement
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java b/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
new file mode 100644
index 0000000..a5b7d4e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/SyntheticMethodTest.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.debug;
+
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+public class SyntheticMethodTest extends DebugTestBase {
+
+  private void debugInnerAccessors(StepFilter stepFilter) throws Throwable {
+    String debuggeeClass = "InnerAccessors";
+    List<Command> commands = new ArrayList<>();
+    commands.add(breakpoint("InnerAccessors$Inner", "callPrivateMethodInOuterClass"));
+    commands.add(run());
+    commands.add(checkLine(13));
+    commands.add(stepInto(stepFilter));  // skip synthetic accessor
+    if (stepFilter == NO_FILTER) {
+      commands.add(stepInto(stepFilter));
+    }
+    commands.add(checkMethod(debuggeeClass, "privateMethod"));
+    commands.add(checkLine(8));
+    commands.add(run());
+    runDebugTest(debuggeeClass, commands);
+  }
+
+  @Test
+  public void testInnerAccessors_NoFilter() throws Throwable {
+    debugInnerAccessors(NO_FILTER);
+  }
+
+  @Test
+  public void testInnerAccessors_IntelliJ() throws Throwable {
+    debugInnerAccessors(INTELLIJ_FILTER);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTest.java b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTest.java
new file mode 100644
index 0000000..2288a84
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class ArgumentLocalsInLoopTest {
+
+  public int foo(int x) {
+    while (true) {
+      if (x <= 0) {
+        return x;
+      }
+      --x;
+    }
+  }
+
+  public static void main(String[] args) {
+    System.out.print(new ArgumentLocalsInLoopTest().foo(42));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTestRunner.java
new file mode 100644
index 0000000..250bf86
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ArgumentLocalsInLoopTestRunner.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class ArgumentLocalsInLoopTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testArgumentLocalsInLoop() throws Exception {
+    Class clazz = ArgumentLocalsInLoopTest.class;
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "0";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkFoo(inspectMethod(d8App, clazz, "int", "foo", "int"), clazz);
+    checkFoo(inspectMethod(dxApp, clazz, "int", "foo", "int"), clazz);
+  }
+
+  private void checkFoo(DebugInfoInspector info, Class clazz) {
+    String[] locals = {"this", clazz.getCanonicalName(), "x", "int"};
+    info.checkStartLine(10);
+    info.checkLineHasExactLocals(10, locals);
+    info.checkLineHasExactLocals(11, locals);
+    info.checkNoLine(12);
+    info.checkLineHasExactLocals(13, locals);
+    info.checkNoLine(14);
+    info.checkNoLine(15);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTest.java b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTest.java
new file mode 100644
index 0000000..c202bd3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class BackBranchToSelfTest {
+
+  public static int backBranchToSelf(boolean loop) {
+    do {
+      if (loop)
+        continue;
+    }
+    while (loop);
+    return 42;
+  }
+
+  public static void main(String[] args) {
+    System.out.print(backBranchToSelf(false));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTestRunner.java
new file mode 100644
index 0000000..ebd1b0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/BackBranchToSelfTestRunner.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class BackBranchToSelfTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testBackBranchToSelf() throws Exception {
+    Class clazz = BackBranchToSelfTest.class;
+
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "42";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkBackBranchToSelf(inspectMethod(d8App, clazz, "int", "backBranchToSelf", "boolean"), false);
+    checkBackBranchToSelf(inspectMethod(dxApp, clazz, "int", "backBranchToSelf", "boolean"), true);
+  }
+
+  private void checkBackBranchToSelf(DebugInfoInspector info, boolean dx) {
+    if (dx) {
+      info.checkStartLine(10);
+      // b/37494646 D8/R8 if-simplification has replaced if by a goto and lost this position.
+      info.checkLineHasExactLocals(10, "loop", "boolean");
+    } else {
+      // D8/R8 will always start at the first debuggable line.
+      info.checkStartLine(13);
+    }
+    info.checkNoLine(11);
+    info.checkNoLine(12);
+    info.checkLineHasExactLocals(13, "loop", "boolean");
+    info.checkLineHasExactLocals(14, "loop", "boolean");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTest.java
new file mode 100644
index 0000000..60f65f2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class ConstantFoldingTest {
+
+  public static int foo(int x) {
+    int res = 2;
+    {
+      int tmp = res + 19;
+      res *= tmp;
+    }
+    return res / 2 + x;
+  }
+
+  public static void main(String[] args) {
+    System.out.print(foo(21));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTestRunner.java
new file mode 100644
index 0000000..a229add
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ConstantFoldingTestRunner.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class ConstantFoldingTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testLocalsInSwitch() throws Exception {
+    Class clazz = ConstantFoldingTest.class;
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "42";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkFoo(inspectMethod(d8App, clazz, "int", "foo", "int"), false);
+    checkFoo(inspectMethod(dxApp, clazz, "int", "foo", "int"), true);
+  }
+
+  private void checkFoo(DebugInfoInspector info, boolean dx) {
+    info.checkStartLine(9);
+    info.checkLineHasExactLocals(9, "x", "int");
+    info.checkNoLine(10);
+    info.checkLineHasExactLocals(11, "x", "int", "res", "int");
+    info.checkLineHasExactLocals(12, "x", "int", "res", "int", "tmp", "int");
+    info.checkNoLine(13);
+    info.checkLineHasAtLeastLocals(14, "x", "int");
+    if (!dx) {
+      // DX fails to close the scope of "tmp".
+      info.checkLineHasExactLocals(14, "x", "int", "res", "int");
+    }
+    info.checkNoLine(15);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
new file mode 100644
index 0000000..368a8fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -0,0 +1,170 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEntryBuilder;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+public class DebugInfoInspector {
+
+  // Method kept here to aid inspection when debugging the tests.
+  private final DexEncodedMethod method;
+  private final List<DexDebugEntry> entries;
+
+  final DexDebugInfo info;
+
+  public DebugInfoInspector(DexEncodedMethod method, DexItemFactory factory) {
+    this.method = method;
+    info = method.getCode().asDexCode().getDebugInfo();
+    entries = new DexDebugEntryBuilder(method, factory).build();
+    checkConsistentEntries();
+  }
+
+  public DebugInfoInspector(DexInspector inspector, String clazz, MethodSignature method) {
+    this(inspector.clazz(clazz).method(method).getMethod(), inspector.getFactory());
+  }
+
+  public DebugInfoInspector(AndroidApp app, String clazz, MethodSignature method)
+      throws IOException, ExecutionException {
+    this(new DexInspector(app), clazz, method);
+  }
+
+  public void checkStartLine(int i) {
+    assertEquals(i, info.startLine);
+  }
+
+  public int checkLineExists(int line) {
+    int lines = checkLines(line, entry -> {});
+    assertTrue(lines > 0);
+    return lines;
+  }
+
+  public int checkLineHasExactLocals(int line, String... pairs) {
+    int lines = checkLines(line, entry -> checkLocalsEqual(entry, pairs));
+    assertTrue("Failed to find entry for line " + line, lines > 0);
+    return lines;
+  }
+
+  public int checkLineHasNoLocals(int line) {
+    return checkLineHasExactLocals(line);
+  }
+
+  public int checkLineHasAtLeastLocals(int line, String... pairs) {
+    int lines = checkLines(line, entry -> checkLocalsDefined(entry, pairs));
+    assertTrue(lines > 0);
+    return lines;
+  }
+
+  public void checkNoLine(int line) {
+    int lines = checkLines(line, entry -> {});
+    assertEquals(0, lines);
+  }
+
+  public int checkLineHasLocal(int line, String name, String type, String... typeParameters) {
+    int lines = checkLines(line, entry -> {
+      checkLocalDefined(entry, name, type, typeParameters);
+    });
+    assertTrue(lines > 0);
+    return lines;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    for (DexDebugEntry entry : entries) {
+      builder.append(entry).append("\n");
+    }
+    return builder.toString();
+  }
+
+  private void checkConsistentEntries() {
+    DexDebugEntry previousEntry = null;
+    for (DexDebugEntry entry : entries) {
+      if (previousEntry != null) {
+        assertTrue("More than one entry defined for PC " + entry.address,
+            entry.address > previousEntry.address);
+      }
+      previousEntry = entry;
+    }
+  }
+
+  private int checkLines(int line, Consumer<DexDebugEntry> check) {
+    int found = 0;
+    for (DexDebugEntry entry : entries) {
+      if (entry.line == line) {
+        found++;
+        check.accept(entry);
+      }
+    }
+    return found;
+  }
+
+  private static DebugLocalInfo checkLocalDefined(DexDebugEntry entry, String name, String type,
+      String... typeParameters) {
+    DebugLocalInfo found = null;
+    for (DebugLocalInfo local : entry.locals.values()) {
+      if (local.name.toString().equals(name)) {
+        if (found != null) {
+          fail("Line " + entry.line + ". Local defined multiple times for name: " + name);
+        }
+        assertEquals(type, local.type.toString());
+        if (typeParameters.length > 0) {
+          String desc = DescriptorUtils.javaTypeToDescriptor(type);
+          StringBuilder builder = new StringBuilder(desc.substring(0, desc.length() - 1));
+          builder.append("<");
+          for (String parameter : typeParameters) {
+            builder.append(parameter);
+          }
+          builder.append(">;");
+          assertEquals(builder.toString(), local.signature.toString());
+        }
+        found = local;
+      }
+    }
+    assertNotNull("Line " + entry.line + ". Failed to find local with name: " + name, found);
+    return found;
+  }
+
+  private static void checkLocalsDefined(DexDebugEntry entry, String... pairs) {
+    assert pairs.length % 2 == 0;
+    for (int i = 0; i < pairs.length; i += 2) {
+      checkLocalDefined(entry, pairs[i], pairs[i + 1]);
+    }
+  }
+
+  private static void checkLocalsEqual(DexDebugEntry entry, String[] pairs) {
+    assert pairs == null || pairs.length % 2 == 0;
+    int expected = pairs == null ? 0 : pairs.length / 2;
+    Set<DebugLocalInfo> remaining = new HashSet<>(entry.locals.values());
+    if (pairs != null) {
+      for (int i = 0; i < pairs.length; i += 2) {
+        DebugLocalInfo local = checkLocalDefined(entry, pairs[i], pairs[i + 1]);
+        remaining.remove(local);
+      }
+    }
+    assertEquals("Line " + entry.line + ". Found unexpected locals: " +
+            String.join(",", remaining.stream().map(Object::toString).collect(Collectors.toList())),
+        expected, expected + remaining.size());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
new file mode 100644
index 0000000..c16291a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class DebugInfoTestBase {
+
+  public static final Path DX_PREBUILT =
+      Paths.get(ToolHelper.BUILD_DIR, "test", "debuginfo_examples_dex.jar");
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException {
+    D8Command.Builder builder = D8Command.builder();
+    for (Class clazz : classes) {
+      builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
+    }
+    return ToolHelper.runD8(builder.setMode(CompilationMode.DEBUG).build());
+  }
+
+  static AndroidApp getDxCompiledSources() throws IOException {
+    return AndroidApp.fromProgramFiles(DX_PREBUILT);
+  }
+
+  public static DebugInfoInspector inspectMethod(
+      AndroidApp app, Class type, String returnType, String methodName, String... parameterTypes)
+      throws IOException, ExecutionException {
+    return new DebugInfoInspector(
+        app, type.getCanonicalName(), new MethodSignature(methodName, returnType, parameterTypes));
+  }
+
+  protected String runOnArt(AndroidApp app, String main) throws IOException {
+    Path out = temp.getRoot().toPath().resolve("out.zip");
+    app.writeToZip(out, true);
+    return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
+  }
+
+  protected String runOnJava(Class clazz) throws Exception {
+    ProcessResult result = ToolHelper.runJava(clazz);
+    if (result.exitCode != 0) {
+      System.out.println("Std out:");
+      System.out.println(result.stdout);
+      System.out.println("Std err:");
+      System.out.println(result.stderr);
+      assertEquals(0, result.exitCode);
+    }
+    return result.stdout;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTest.java
new file mode 100644
index 0000000..6df6d99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class LocalSwapTest {
+
+  public static int foo(int x, int y) {
+    int sum = x + y;
+    {
+      int t = x;
+      x = y;
+      y = t;
+    }
+    return sum + x + y;
+  }
+
+  public static void main(String[] args) {
+    System.out.print(foo(1, 2));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTestRunner.java
new file mode 100644
index 0000000..0ee33bf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalSwapTestRunner.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalSwapTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testLocalSwap() throws Exception {
+    Class clazz = LocalSwapTest.class;
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "6";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkFoo(inspectMethod(d8App, clazz, "int", "foo", "int", "int"), false);
+    checkFoo(inspectMethod(dxApp, clazz, "int", "foo", "int", "int"), true);
+  }
+
+  private void checkFoo(DebugInfoInspector info, boolean dx) {
+    info.checkStartLine(9);
+    info.checkLineHasExactLocals(9, "x", "int", "y", "int");
+    info.checkLineHasExactLocals(11, "x", "int", "y", "int", "sum", "int");
+    info.checkLineHasExactLocals(12, "x", "int", "y", "int", "sum", "int", "t", "int");
+    info.checkLineExists(13);
+    info.checkLineExists(15);
+    if (!dx) {
+      // DX fails to close the scope of local "t".
+      info.checkLineHasExactLocals(15, "x", "int", "y", "int", "sum", "int");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTest.java
new file mode 100644
index 0000000..b07fab8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTest.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class LocalsAtThrowTest {
+
+  public static int localsAtThrow(int x) {
+    int a = 1;
+    int b = 2;
+    switch (x % 3) {
+      case 1:
+        throw new RuntimeException();
+      case 2:
+        return a + b;
+    }
+    return 42;
+  }
+
+  public static void main(String[] args) {
+    System.out.print(localsAtThrow(11));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTestRunner.java
new file mode 100644
index 0000000..8df6f79
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsAtThrowTestRunner.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalsAtThrowTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testLocalsAtThrow() throws Exception {
+    Class clazz = LocalsAtThrowTest.class;
+
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "3";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkBackBranchToSelf(inspectMethod(d8App, clazz, "int", "localsAtThrow", "int"));
+    checkBackBranchToSelf(inspectMethod(dxApp, clazz, "int", "localsAtThrow", "int"));
+  }
+
+  private void checkBackBranchToSelf(DebugInfoInspector info) {
+    info.checkStartLine(9);
+    info.checkLineHasExactLocals(9, "x", "int");
+    info.checkLineHasExactLocals(10, "x", "int", "a", "int");
+    info.checkLineHasExactLocals(11, "x", "int", "a", "int", "b", "int");
+    info.checkNoLine(12);
+    info.checkLineHasExactLocals(13, "x", "int", "a", "int", "b", "int");
+    info.checkNoLine(14);
+    info.checkLineHasExactLocals(15, "x", "int", "a", "int", "b", "int");
+    info.checkNoLine(16);
+    info.checkLineHasExactLocals(17, "x", "int", "a", "int", "b", "int");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTest.java
new file mode 100644
index 0000000..010d517
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class LocalsInSwitchTest {
+
+  public static int noLocals(int x) {
+    switch (x) {
+      case 0:
+        return 0;
+      case 1:
+        return 1;
+      default:
+        return noLocals(x - 1) + noLocals(x - 2);
+    }
+  }
+
+  public static int tempInCase(int x) {
+    int res = 0;
+    for (int i = 0; i < x; ++i) {
+      int rem = x - i;
+      switch (rem) {
+        case 1:
+          return res;
+        case 5:
+          int tmp = res + x + i;
+          res += tmp;
+          break;
+        case 10:
+          i++;
+          break;
+        default:
+          res += rem;
+      }
+      res += rem % 2;
+    }
+    res *= x;
+    return res;
+  }
+
+  public static int initInCases(int x) {
+    Integer res;
+    switch (x % 3) {
+      case 0:
+        res = 42;
+      case 1:
+        res = x;
+      case 2:
+      default:
+        res = x * x;
+    }
+    return res + 1;
+  }
+
+  public static void main(String[] args) {
+    System.out.println(noLocals(10));
+    System.out.println(tempInCase(42));
+    System.out.println(initInCases(123));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTestRunner.java
new file mode 100644
index 0000000..020809a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsInSwitchTestRunner.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalsInSwitchTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testLocalsInSwitch() throws Exception {
+    Class clazz = LocalsInSwitchTest.class;
+
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "55\n1862\n15130\n";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkNoLocals(inspectMethod(d8App, clazz, "int", "noLocals", "int"));
+    checkNoLocals(inspectMethod(dxApp, clazz, "int", "noLocals", "int"));
+
+    checkTempInCase(inspectMethod(d8App, clazz, "int", "tempInCase", "int"), false);
+    checkTempInCase(inspectMethod(dxApp, clazz, "int", "tempInCase", "int"), true);
+
+    checkInitInCases(inspectMethod(d8App, clazz, "int", "initInCases", "int"));
+    checkInitInCases(inspectMethod(dxApp, clazz, "int", "initInCases", "int"));
+  }
+
+  private void checkNoLocals(DebugInfoInspector info) {
+    info.checkStartLine(9);
+    info.checkLineHasExactLocals(9, "x", "int");
+    info.checkLineHasExactLocals(11, "x", "int");
+    info.checkLineHasExactLocals(13, "x", "int");
+    info.checkLineHasExactLocals(15, "x", "int");
+  }
+
+  private void checkTempInCase(DebugInfoInspector tempInCase, boolean dx) {
+    // int res =
+    tempInCase.checkStartLine(20);
+    tempInCase.checkLineHasExactLocals(20, "x", "int");
+    // for (int i = ...
+    // The local 'i' is visible on the back edges, but not on the initial entry.
+    tempInCase.checkLineHasAtLeastLocals(21, "x", "int", "res", "int");
+    //   int rem =
+    tempInCase.checkLineHasExactLocals(22, "x", "int", "res", "int", "i", "int");
+    //   switch (rem) {
+    if (!dx) {
+      // DX contains several entries for 23, one of which does not define 'rem'. Go figure...
+      tempInCase.checkLineHasExactLocals(23, "x", "int", "res", "int", "i", "int", "rem", "int");
+    }
+    //   case 0:
+    tempInCase.checkNoLine(24);
+    //     return res
+    if (!dx) {
+      // DX does not produce a position at the return statement. Good stuff.
+      tempInCase.checkLineHasExactLocals(25, "x", "int", "res", "int", "i", "int", "rem", "int");
+    }
+    //   case 5:
+    tempInCase.checkNoLine(26);
+    //     int tmp =
+    tempInCase.checkLineHasExactLocals(27, "x", "int", "res", "int", "i", "int", "rem", "int");
+    //     res += tmp
+    tempInCase.checkLineHasExactLocals(28,
+        "x", "int", "res", "int", "i", "int", "rem", "int", "tmp", "int");
+    //     break;
+    tempInCase.checkLineHasExactLocals(29,
+        "x", "int", "res", "int", "i", "int", "rem", "int", "tmp", "int");
+    //   case 10:
+    tempInCase.checkNoLine(30);
+    //     i++
+    tempInCase.checkLineHasExactLocals(31, "x", "int", "res", "int", "i", "int", "rem", "int");
+    //     break;
+    tempInCase.checkLineHasExactLocals(32, "x", "int", "res", "int", "i", "int", "rem", "int");
+    //   default:
+    tempInCase.checkNoLine(33);
+    //     res += rem;
+    tempInCase.checkLineHasExactLocals(34, "x", "int", "res", "int", "i", "int", "rem", "int");
+    //   }
+    tempInCase.checkNoLine(35);
+    //   res += rem % 2;
+    tempInCase.checkLineHasExactLocals(36, "x", "int", "res", "int", "i", "int", "rem", "int");
+    // }
+    tempInCase.checkNoLine(37);
+    // res *= x;
+    if (!dx) {
+      // DX fails to end the scope of "i" after the loop.
+      tempInCase.checkLineHasExactLocals(38, "x", "int", "res", "int");
+    }
+    // return res;
+    if (!dx) {
+      tempInCase.checkLineHasExactLocals(39, "x", "int", "res", "int");
+    }
+  }
+
+  private void checkInitInCases(DebugInfoInspector info) {
+    info.checkNoLine(43); // No line on uninitialized local declaration.
+    info.checkStartLine(44);
+    info.checkLineHasExactLocals(44, "x", "int"); // Local "res" is still not visible in the case.
+    info.checkLineHasExactLocals(46, "x", "int"); // Ditto.
+    info.checkLineHasExactLocals(48, "x", "int"); // Ditto.
+    info.checkLineHasExactLocals(51, "x", "int"); // Ditto.
+    info.checkLineHasExactLocals(53, "x", "int", "res", "java.lang.Integer");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsRunner.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsRunner.java
new file mode 100644
index 0000000..2dd50ed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsRunner.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class LocalsWithTypeParamsRunner extends DebugInfoTestBase {
+
+  static final Class clazzMain = LocalsWithTypeParamsTest.class;
+  static final Class clazzA = A.class;
+  static final Class clazzB = B.class;
+
+  static final String nameMain = clazzMain.getCanonicalName();
+  static final String nameA = clazzA.getCanonicalName();
+  static final String nameB = clazzB.getCanonicalName();
+
+  @Test
+  public void testLocalsWithTypeParams() throws Exception {
+    AndroidApp d8App = compileWithD8(clazzMain, clazzA, clazzB);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "42";
+    assertEquals(expected, runOnJava(clazzMain));
+    assertEquals(expected, runOnArt(d8App, nameMain));
+    assertEquals(expected, runOnArt(dxApp, nameMain));
+
+    checkSyncInstance(inspectMethod(d8App, clazzA, "int", "foo", nameB));
+    checkSyncInstance(inspectMethod(dxApp, clazzA, "int", "foo", nameB));
+  }
+
+  private void checkSyncInstance(DebugInfoInspector info) {
+    // Assert that the parameter entry is null since it is explicitly introduced in the stream.
+    assertEquals(1, info.info.parameters.length);
+    assertNull(info.info.parameters[0]);
+
+    info.checkStartLine(8);
+    info.checkLineHasExactLocals(8, "this", nameA, "b", nameB);
+    info.checkLineHasLocal(8, "this", nameA, "TT;");
+    info.checkLineHasLocal(8, "b", nameB, "TT;", "Ljava/lang/String;");
+
+    info.checkLineHasExactLocals(9, "this", nameA, "b", nameB, "otherB", nameB);
+    info.checkLineHasLocal(9, "this", nameA, "TT;");
+    info.checkLineHasLocal(9, "b", nameB, "TT;", "Ljava/lang/String;");
+    info.checkLineHasLocal(9, "otherB", nameB, "Ljava/lang/String;", "Ljava/lang/String;");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsTest.java b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsTest.java
new file mode 100644
index 0000000..ea4ac2b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/LocalsWithTypeParamsTest.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+class A<T> {
+  int foo(B<T, String> b) {
+    B<String, String> otherB = new B<String, String>();
+    return (b.foo() + otherB.foo()) / 2;
+  }
+}
+
+class B<U, V> {
+  int foo() {
+    return 42;
+  }
+}
+
+public class LocalsWithTypeParamsTest {
+
+  public static void main(String[] args) {
+    System.out.print(new A<Class>().foo(new B<Class, String>()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTest.java b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTest.java
new file mode 100644
index 0000000..127211f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTest.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class ScopedExceptionsTest {
+
+  private static int scopedExceptions() {
+    try {
+      throwNPE();
+    }
+    catch (NullPointerException e) {}
+    catch (Throwable e) {
+      System.out.println("Unexpected...");
+    }
+    return 42;
+  }
+
+  private static void throwNPE() {
+    throw new NullPointerException();
+  }
+
+  public static void main(String[] args) {
+    System.out.print(scopedExceptions());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTestRunner.java
new file mode 100644
index 0000000..436334a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/ScopedExceptionsTestRunner.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class ScopedExceptionsTestRunner extends DebugInfoTestBase {
+
+  @Test
+  public void testScopedException() throws Exception {
+    Class clazz = ScopedExceptionsTest.class;
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "42";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkScopedExceptions(inspectMethod(d8App, clazz, "int", "scopedExceptions"), false);
+    checkScopedExceptions(inspectMethod(dxApp, clazz, "int", "scopedExceptions"), true);
+  }
+
+  private void checkScopedExceptions(DebugInfoInspector info, boolean dx) {
+    info.checkStartLine(10);
+    info.checkLineHasNoLocals(10);
+    info.checkNoLine(11);
+    info.checkLineHasNoLocals(12);
+    info.checkLineHasNoLocals(13);
+    info.checkLineHasExactLocals(14, "e", "java.lang.Throwable");
+    // DX does not generate a position at the end of the try-catch blocks, Java does and so does D8.
+    if (!dx) {
+      info.checkLineHasNoLocals(15);
+    }
+    info.checkLineExists(16);
+    // DX will still have an local entry for 'e' after its scope has ended.
+    if (!dx) {
+      info.checkLineHasNoLocals(16);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
new file mode 100644
index 0000000..be0a122
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTest.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+public class SynchronizedMethodTest {
+
+  private static synchronized int syncStatic(int x) {
+    if (x % 2 == 0) {
+      return 42;
+    }
+    return -Math.abs(x);
+  }
+
+  private synchronized int syncInstance(int x) {
+    if (x % 2 == 0) {
+      return 42;
+    }
+    return -Math.abs(x);
+  }
+
+  public static void main(String[] args) {
+    System.out.println(syncStatic(1234));
+    System.out.println(new SynchronizedMethodTest().syncInstance(1234));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
new file mode 100644
index 0000000..7370ca2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/SynchronizedMethodTestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.utils.AndroidApp;
+import org.junit.Test;
+
+public class SynchronizedMethodTestRunner extends DebugInfoTestBase {
+
+  static Class clazz = SynchronizedMethodTest.class;
+
+  @Test
+  public void testSynchronizedMethod() throws Exception {
+    AndroidApp d8App = compileWithD8(clazz);
+    AndroidApp dxApp = getDxCompiledSources();
+
+    String expected = "42\n42\n";
+    assertEquals(expected, runOnJava(clazz));
+    assertEquals(expected, runOnArt(d8App, clazz.getCanonicalName()));
+    assertEquals(expected, runOnArt(dxApp, clazz.getCanonicalName()));
+
+    checkSyncStatic(inspectMethod(d8App, clazz, "int", "syncStatic", "int"));
+    checkSyncStatic(inspectMethod(dxApp, clazz, "int", "syncStatic", "int"));
+
+    checkSyncInstance(inspectMethod(d8App, clazz, "int", "syncInstance", "int"));
+    checkSyncInstance(inspectMethod(dxApp, clazz, "int", "syncInstance", "int"));
+  }
+
+  private void checkSyncStatic(DebugInfoInspector info) {
+    info.checkStartLine(9);
+    info.checkLineHasExactLocals(9, "x", "int");
+    info.checkLineHasExactLocals(10, "x", "int");
+    info.checkNoLine(11);
+    info.checkLineHasExactLocals(12, "x", "int");
+    info.checkNoLine(13);
+  }
+
+  private void checkSyncInstance(DebugInfoInspector info) {
+    String[] locals = {"this", clazz.getCanonicalName(), "x", "int"};
+    info.checkStartLine(16);
+    info.checkLineHasExactLocals(16, locals);
+    info.checkLineHasExactLocals(17, locals);
+    info.checkNoLine(18);
+    info.checkLineHasExactLocals(19, locals);
+    info.checkNoLine(20);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
new file mode 100644
index 0000000..e5581a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DebugByteCodeWriterTest.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DebugByteCodeWriterTest {
+
+  ObjectToOffsetMapping emptyObjectTObjectMapping() {
+    return new ObjectToOffsetMapping(
+        0,
+        new Builder(new DexItemFactory(), null).build(),
+        new DexProgramClass[] {},
+        new DexProto[] {},
+        new DexType[] {},
+        new DexMethod[] {},
+        new DexField[] {},
+        new DexString[] {},
+        new DexCallSite[] {},
+        new DexMethodHandle[] {});
+  }
+
+  @Test
+  public void testEmptyDebugInfo() {
+    DexDebugInfo debugInfo = new DexDebugInfo(1, new DexString[]{}, new DexDebugEvent[]{});
+    DebugBytecodeWriter writer = new DebugBytecodeWriter(debugInfo, emptyObjectTObjectMapping());
+    Assert.assertEquals(3, writer.generate().length);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DexItemFactoryTest.java b/src/test/java/com/android/tools/r8/dex/DexItemFactoryTest.java
new file mode 100644
index 0000000..1f9a5a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DexItemFactoryTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.dex;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import org.junit.Test;
+
+public class DexItemFactoryTest {
+
+  @Test
+  public void commonItems() {
+    DexItemFactory factory = new DexItemFactory();
+
+    Object[] data = new Object[]{
+        "B", factory.byteDescriptor, factory.byteType,
+        "C", factory.charDescriptor, factory.charType,
+        "D", factory.doubleDescriptor, factory.doubleType,
+        "F", factory.floatDescriptor, factory.floatType,
+        "I", factory.intDescriptor, factory.intType,
+        "J", factory.longDescriptor, factory.longType,
+        "S", factory.shortDescriptor, factory.shortType,
+        "V", factory.voidDescriptor, factory.voidType,
+        "Z", factory.booleanDescriptor, factory.booleanType,
+        "Ljava/lang/String;", factory.stringDescriptor, factory.stringType,
+        "Ljava/lang/Object;", factory.objectDescriptor, factory.objectType,
+    };
+
+    for (int i = 0; i < data.length; i += 3) {
+      DexString string1 = factory.createString((String) data[i]);
+      DexString string2 = factory.createString((String) data[i]);
+      DexItem type1 = factory.createType(string1);
+      DexItem type2 = factory.createType(string2);
+      DexItem expectedDexString = (DexString) data[i + 1];
+      DexItem expectedDexType = (DexType) data[i + 2];
+
+      assertSame(expectedDexString, string1);
+      assertSame(expectedDexString, string2);
+      assertSame(expectedDexType, type1);
+      assertSame(expectedDexType, type2);
+    }
+  }
+
+  @Test
+  public void getPrimitiveTypeName() {
+    DexItemFactory factory = new DexItemFactory();
+    assertEquals("boolean", factory.booleanType.getName());
+    assertEquals("byte", factory.byteType.getName());
+    assertEquals("short", factory.shortType.getName());
+    assertEquals("char", factory.charType.getName());
+    assertEquals("int", factory.intType.getName());
+    assertEquals("float", factory.floatType.getName());
+    assertEquals("long", factory.longType.getName());
+    assertEquals("double", factory.doubleType.getName());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/DexStringTest.java b/src/test/java/com/android/tools/r8/dex/DexStringTest.java
new file mode 100644
index 0000000..fa7f332
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DexStringTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.dex;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import org.junit.Test;
+
+public class DexStringTest {
+
+  @Test
+  public void testEncodingLength() {
+    DexItemFactory factory = new DexItemFactory();
+    checkEncodedLength(factory.createString("\u0000"), 2);
+    checkEncodedLength(factory.createString("\u0001"), 1);
+    checkEncodedLength(factory.createString("\u007f"), 1);
+    checkEncodedLength(factory.createString("\u0080"), 2);
+    checkEncodedLength(factory.createString("\u07ff"), 2);
+    checkEncodedLength(factory.createString("\u0800"), 3);
+    checkEncodedLength(factory.createString("\uffff"), 3);
+    checkEncodedLength(factory.createString("\ud800\udc00"), 6);
+    checkEncodedLength(factory.createString("\udbff\udfff"), 6);
+  }
+
+  @Test
+  public void testCompare() {
+    DexItemFactory factory = new DexItemFactory();
+
+    // Test strings in lexicographic order.
+    DexString[] strings = {
+        factory.createString(""),
+        factory.createString("\u0000"),
+        factory.createString("\u0001"),
+        factory.createString("\u0060a"),  // 'a' is 0x61.
+        factory.createString("a"),
+        factory.createString("a\u0000a"),
+        factory.createString("a\u0001a"),
+        factory.createString("a\u0060a"),  // 'a' is 0x61.
+        factory.createString("aa"),
+        factory.createString("aaa"),
+        factory.createString("a\u007f"),  // U+007f is the last code point with one UTF-8 bytes.
+        factory.createString("a\u007fa"),
+        factory.createString("a\u0080"),  // U+0080 is the first code point with two UTF-8 bytes.
+        factory.createString("a\u0080a"),
+        factory.createString("a\u07ff"),  // U+07ff is the last code point with two UTF-8 bytes.
+        factory.createString("a\u07ffa"),
+        factory.createString("a\u0800"),  // U+0800 is the first code point with three UTF-8 bytes.
+        factory.createString("a\u0800a"),
+        factory.createString("a\u0801"),
+        factory.createString("a\u0801a"),
+        factory.createString("a\ud800\udc00a"),  // Surrogate pair for U+010000. Sorts per UTF-16.
+        factory.createString("a\udbff\udfffa"),  // Surrogate pair for U+10ffff. Sorts per UTF-16.
+        factory.createString("a\uffffa"),
+        factory.createString("\u007f"),  // U+007f is the last code point with one UTF-8 bytes.
+        factory.createString("\u0080"),  // U+0080 is the first code point with two UTF-8 bytes.
+        factory.createString("\u07ff"),  // U+07ff is the last code point with two UTF-8 bytes.
+        factory.createString("\u0800"),  // U+0800 is the first code point with three UTF-8 bytes.
+        factory.createString("\u0801"),  // U+0800 is the first code point with three UTF-8 bytes.
+        factory.createString("\ud800\udc00"),  // Surrogate pair for U+010000. Sorts per UTF-16.
+        factory.createString("\udbff\udfff"),  // Surrogate pair for U+10ffff. Sorts per UTF-16.
+        factory.createString("\uffff"),
+    };
+
+
+    for (int i = 0; i < strings.length; i++) {
+      for (int j = 0; j < strings.length; j++) {
+        int expected = Integer.signum(i - j);
+        check(expected, strings[i], strings[j]);
+        check(-expected, strings[j], strings[i]);
+      }
+    }
+  }
+
+  private void check(int expected, DexString s1, DexString s2) {
+    assertEquals(s1.dump() + " " + s2.dump(),
+        expected, Integer.signum(s1.toString().compareTo(s2.toString())));
+    assertEquals(s1.dump() + " " + s2.dump(),
+        expected, Integer.signum(s1.slowCompareTo(s2)));
+  }
+
+  private void checkEncodedLength(DexString s, int encodedLength) {
+    // The terminating zero is not part of the encoding,
+    int length = s.content.length;
+    assertEquals(0, s.content[length - 1]);
+    assertEquals(encodedLength, length - 1);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java b/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
new file mode 100644
index 0000000..3a4ea04
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/EncodedFloatingValueTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.utils.EncodedValueUtils;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EncodedFloatingValueTest {
+  private final double value;
+
+  public EncodedFloatingValueTest(double value) {
+    this.value = value;
+  }
+
+  @Parameters(name = "{0}")
+  public static Collection<Double> data() {
+    return Arrays.asList(
+        0.0,
+        1.0,
+        0.5,
+        Double.longBitsToDouble(1), // Lowest bit is 1 in double
+        Double.longBitsToDouble(0x10), // Bits on byte boundary are 1.
+        Double.longBitsToDouble(0x08),
+        Double.longBitsToDouble(4607071218809329336L),  // Test a real long (regression).
+        (double) (Float.intBitsToFloat(1)), // Lowest bit is 1 in float
+        (double) (Float.intBitsToFloat(0x10)), // Bits on byte boundary are 1
+        (double) (Float.intBitsToFloat(0x08))
+    );
+  }
+
+  // Create a DexFile with correct file magic followed by the argument bytes. Positions the
+  // DexFile after the file magic.
+  private DexFile createDexFileWithContent(byte[] bytes) {
+    DexOutputBuffer buffer = new DexOutputBuffer();
+    buffer.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
+    buffer.putBytes(Constants.ANDROID_PRE_N_DEX_VERSION_BYTES);
+    buffer.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
+    buffer.putBytes(bytes);
+    DexFile dexFile = new DexFile(buffer.asArray());
+    dexFile.position(DEX_MAGIC_SIZE);
+    return dexFile;
+  }
+
+  @Test
+  public void testEncodeDecodeDouble() {
+    byte[] bytes = EncodedValueUtils.encodeDouble(value);
+    assertTrue(bytes.length <= Double.BYTES);
+    DexFile dexFile = createDexFileWithContent(bytes);
+    Assert.assertEquals(value, EncodedValueUtils.parseDouble(dexFile, bytes.length), 0.0);
+  }
+
+  @Test
+  public void testEncodeDecodeFloat() {
+    byte[] bytes = EncodedValueUtils.encodeFloat((float) value);
+    assertTrue(bytes.length <= Float.BYTES);
+    DexFile dexFile = createDexFileWithContent(bytes);
+    Assert.assertEquals((float) value, EncodedValueUtils.parseFloat(dexFile, bytes.length), 0.0f);
+  }
+
+  @Test
+  public void testEncodeDecodeDoubleWithDexBuffer() {
+    DexOutputBuffer buffer = new DexOutputBuffer();
+    int length = EncodedValueUtils.putDouble(buffer, value);
+    assertTrue(length <= Double.BYTES);
+    byte[] bytes = buffer.asArray();
+    DexFile dexFile = createDexFileWithContent(bytes);
+    assertEquals(value, EncodedValueUtils.parseDouble(dexFile, length), 0.0);
+  }
+
+  @Test
+  public void testEncodeDecodeFloatWithDexBuffer() {
+    DexOutputBuffer buffer = new DexOutputBuffer();
+    int length = EncodedValueUtils.putFloat(buffer, (float) value);
+    assertTrue(length <= Float.BYTES);
+    byte[] bytes = buffer.asArray();
+    DexFile dexFile = createDexFileWithContent(bytes);
+    assertEquals((float) value, EncodedValueUtils.parseFloat(dexFile, length), 0.0f);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
new file mode 100644
index 0000000..d2aa592
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class ExtraFileTest {
+
+  private static final String EXAMPLE_DIR = ToolHelper.EXAMPLES_BUILD_DIR;
+  private static final String EXAMPLE_DEX = "memberrebinding/classes.dex";
+  private static final String EXAMPLE_LIB = "memberrebindinglib/classes.dex";
+  private static final String EXAMPLE_CLASS = "memberrebinding.Test";
+  private static final String EXAMPLE_PACKAGE_MAP = "memberrebinding/package.map";
+  private static final String EXAMPLE_PROGUARD_MAP = "memberrebinding/proguard.map";
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Ignore("b/38187737")
+  @Test
+  public void splitMemberRebindingTwoFiles()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+
+    Path out = temp.getRoot().toPath();
+    Path original = Paths.get(EXAMPLE_DIR, EXAMPLE_DEX);
+    Path packageMap = Paths.get(ToolHelper.EXAMPLES_DIR, EXAMPLE_PACKAGE_MAP);
+    Path proguardMap = Paths.get(ToolHelper.EXAMPLES_DIR, EXAMPLE_PROGUARD_MAP);
+    R8Command command =
+        R8Command.builder()
+            .addProgramFiles(original)
+            .setOutputPath(out)
+            .setProguardMapFile(proguardMap)
+            .setPackageDistributionFile(packageMap)
+            .build();
+    ToolHelper.runR8(command);
+    List<String> outs =
+        new ArrayList<>(
+            ImmutableList.of(
+                out.resolve("classes.dex").toString(),
+                out.resolve("classes2.dex").toString(),
+                EXAMPLE_DIR + EXAMPLE_LIB));
+    outs.forEach(f -> Assert.assertTrue("Failed to find file " + f, Files.exists(Paths.get(f))));
+    ToolHelper.checkArtOutputIdentical(
+        ImmutableList.of(original.toString(), EXAMPLE_DIR + EXAMPLE_LIB),
+        outs,
+        EXAMPLE_CLASS,
+        null,
+        null);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/FileNamePrefixTest.java b/src/test/java/com/android/tools/r8/dex/FileNamePrefixTest.java
new file mode 100644
index 0000000..f2fa545
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/FileNamePrefixTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FileNamePrefixTest {
+
+  private final List<String> files;
+  private final String expectedPrefix;
+
+  public FileNamePrefixTest(String[] files, String expectedPrefix) {
+    this.files = ImmutableList.copyOf(files);
+    this.expectedPrefix = expectedPrefix;
+  }
+
+  @Parameters(name = "{index}: {1}")
+  public static Collection<Object[]> data() {
+    return ImmutableList.of(
+        new Object[]{new String[]{"classes.dex", "classes2.dex"}, "classes"},
+        new Object[]{new String[]{"Classes.dex", "Classes2.dex"}, "Classes"},
+        new Object[]{new String[]{"classes.dex", "classes2.dix"}, null},
+        new Object[]{new String[]{"classes.dex", "classes2.xdex"}, null},
+        new Object[]{new String[]{"classes.dex", "classes1.dex"}, null},
+        new Object[]{new String[]{"classes.dex", "fields2.dex"}, null},
+        new Object[]{new String[]{"classes.dex", "classes2.dex", "classes4.dex"}, null}
+    );
+  }
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void computePrefix() {
+    if (expectedPrefix == null) {
+      thrown.expect(RuntimeException.class);
+    }
+    String computedPrefix = VirtualFile.deriveCommonPrefixAndSanityCheck(files);
+    if (expectedPrefix != null) {
+      Assert.assertEquals(expectedPrefix, computedPrefix);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/Leb128Test.java b/src/test/java/com/android/tools/r8/dex/Leb128Test.java
new file mode 100644
index 0000000..2b6ab9e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/Leb128Test.java
@@ -0,0 +1,99 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.dex;
+
+import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE;
+
+import com.android.tools.r8.utils.LebUtils;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Test for encoding and decoding Leb128
+ */
+@RunWith(Parameterized.class)
+public class Leb128Test {
+  private final int value;
+
+  public Leb128Test(int value) {
+    this.value = value;
+  }
+
+  @Parameters(name = "{0}")
+  public static Collection<Integer> data() {
+    return Arrays.asList(
+      0,
+      0x3f, // Uses 6 bits
+      0x7f, // Uses 7 bits
+      0xff, // Uses 8 bits
+      0x3ff, // Uses 14 bits
+      0x407f, // Uses 15 bits with 7 consecutive 0s.
+      Integer.MIN_VALUE, // 10...0 pattern
+      0xffffffc0, // 1..1000000
+      0xffffff80, // 1..10000000
+      0xffffff00, // 1..100000000
+      0xffffc07f, // Uses 15 bits with 7 consecutive 0s.
+      Integer.MIN_VALUE + 1
+    );
+  }
+
+  // Create a DexFile with correct file magic followed by the argument bytes. Positions the
+  // DexFile after the file magic.
+  private DexFile createDexFileWithContent(byte[] bytes) {
+    DexOutputBuffer buffer = new DexOutputBuffer();
+    buffer.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
+    buffer.putBytes(Constants.ANDROID_PRE_N_DEX_VERSION_BYTES);
+    buffer.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
+    buffer.putBytes(bytes);
+    DexFile dexFile = new DexFile(buffer.asArray());
+    dexFile.position(DEX_MAGIC_SIZE);
+    return dexFile;
+  }
+
+  @Test
+  public void encodeDecodeLeb128TestWithDexBuffer() {
+    if (value < 0) {
+      return;
+    }
+    DexOutputBuffer buffer = new DexOutputBuffer();
+    LebUtils.putUleb128(buffer, value);
+    Assert.assertEquals(buffer.position(), LebUtils.sizeAsUleb128(value));
+    DexFile file = createDexFileWithContent(buffer.asArray());
+    Assert.assertEquals(value, LebUtils.parseUleb128(file));
+  }
+
+  @Test
+  public void encodeDecodeLeb128Test() {
+    if (value < 0) {
+      return;
+    }
+    byte[] encoded = LebUtils.encodeUleb128(value);
+    Assert.assertEquals(encoded.length, LebUtils.sizeAsUleb128(value));
+    DexFile file = createDexFileWithContent(encoded);
+    Assert.assertEquals(value, LebUtils.parseUleb128(file));
+  }
+
+  @Test
+  public void encodeDecodeSLeb128TestWithDexBuffer() {
+    DexOutputBuffer buffer = new DexOutputBuffer();
+    LebUtils.putSleb128(buffer, value);
+    Assert.assertEquals(buffer.position(), LebUtils.sizeAsSleb128(value));
+    DexFile file = createDexFileWithContent(buffer.asArray());
+    Assert.assertEquals(value, LebUtils.parseSleb128(file));
+  }
+
+
+  @Test
+  public void encodeDecodeSLeb128Test() {
+    byte[] encoded = LebUtils.encodeSleb128(value);
+    Assert.assertEquals(encoded.length, LebUtils.sizeAsSleb128(value));
+    DexFile file = createDexFileWithContent(encoded);
+    Assert.assertEquals(value, LebUtils.parseSleb128(file));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
new file mode 100644
index 0000000..f731e39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -0,0 +1,151 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class TargetLookupTest extends SmaliTestBase {
+
+  @Test
+  public void lookupDirect() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addDefaultConstructor();
+
+    builder.addMethodRaw(
+        "  .method private static x()I",
+        "    .locals 1",
+        "    const v0, 0",
+        "    return v0",
+        "  .end method"
+    );
+
+    // Instance method invoking static method using invoke-direct. This does not run on Art, but
+    // results in an IncompatibleClassChangeError.
+    builder.addMethodRaw(
+        "  .method public y()I",
+        "    .locals 1",
+        "    invoke-direct       {p0}, " + builder.getCurrentClassDescriptor() + "->x()I",
+        "    move-result         v0",
+        "    return              v0",
+        "  .end method"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, LTest;",
+        "    invoke-direct       {v1}, " + builder.getCurrentClassDescriptor() + "-><init>()V",
+        "    :try_start",
+        "    invoke-virtual      {v1}, " + builder.getCurrentClassDescriptor() + "->y()I",
+        "    :try_end",
+        "    const-string        v1, \"ERROR\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    :return",
+        "    return-void",
+        "    .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const-string        v1, \"OK\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    goto :return"
+    );
+
+    DexApplication application = buildApplication(builder);
+    AppInfo appInfo = new AppInfo(application);
+    DexEncodedMethod method =
+        getMethod(application, DEFAULT_CLASS_NAME, "int", "x", ImmutableList.of());
+    assertNull(appInfo.lookupVirtualTarget(method.method.holder, method.method));
+    assertNull(appInfo.lookupDirectTarget(method.method));
+    assertNotNull(appInfo.lookupStaticTarget(method.method));
+
+    assertEquals("OK", runArt(application, new InternalOptions()));
+  }
+
+  @Test
+  public void lookupDirectSuper() {
+    SmaliBuilder builder = new SmaliBuilder("TestSuper");
+
+    builder.addDefaultConstructor();
+
+    builder.addMethodRaw(
+        "  .method private static x()I",
+        "    .locals 1",
+        "    const               v0, 0",
+        "    return              v0",
+        "  .end method"
+    );
+
+    builder.addClass("Test", "TestSuper");
+
+    builder.addDefaultConstructor();
+
+    // Instance method invoking static method in superclass using invoke-direct. This does not run
+    // on Art, but results in an IncompatibleClassChangeError.
+    builder.addMethodRaw(
+        "  .method public y()I",
+        "    .locals 1",
+        "    invoke-direct       {p0}, " + builder.getCurrentClassDescriptor() + "->x()I",
+        "    move-result         v0",
+        "    return              v0",
+        "  .end method"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, LTest;",
+        "    invoke-direct       {v1}, " + builder.getCurrentClassDescriptor() + "-><init>()V",
+        "    :try_start",
+        "    invoke-virtual      {v1}, " + builder.getCurrentClassDescriptor() + "->y()I",
+        "    :try_end",
+        "    const-string        v1, \"ERROR\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    :return",
+        "    return-void",
+        "    .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const-string        v1, \"OK\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    goto :return"
+    );
+
+    DexApplication application = buildApplication(builder);
+    AppInfo appInfo = new AppInfo(application);
+
+    DexMethod methodXOnTestSuper =
+        getMethod(application, "TestSuper", "int", "x", ImmutableList.of()).method;
+    DexMethod methodYOnTest =
+        getMethod(application, "Test", "int", "y", ImmutableList.of()).method;
+
+    DexType classTestSuper = methodXOnTestSuper.getHolder();
+    DexType classTest = methodYOnTest.getHolder();
+    DexProto methodXProto = methodXOnTestSuper.proto;
+    DexString methodXName = methodXOnTestSuper.name;
+    DexMethod methodXOnTest =
+        application.dexItemFactory.createMethod(classTest, methodXProto, methodXName);
+
+    assertNull(appInfo.lookupVirtualTarget(classTestSuper, methodXOnTestSuper));
+    assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTestSuper));
+    assertNull(appInfo.lookupVirtualTarget(classTest, methodXOnTest));
+
+    assertNull(appInfo.lookupDirectTarget(methodXOnTestSuper));
+    assertNull(appInfo.lookupDirectTarget(methodXOnTest));
+
+    assertNotNull(appInfo.lookupStaticTarget(methodXOnTestSuper));
+    assertNotNull(appInfo.lookupStaticTarget(methodXOnTest));
+
+    assertEquals("OK", runArt(application, new InternalOptions()));
+  }
+}
+
+
+
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
new file mode 100644
index 0000000..5902a6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArtErrorParser;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.ListUtils;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class CompilationTestBase {
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  public AndroidApp runAndCheckVerification(
+      CompilerUnderTest compiler,
+      String referenceApk,
+      String pgMap,
+      String pgConf,
+      String... inputs)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    return runAndCheckVerification(
+        compiler, referenceApk, pgMap, pgConf, Arrays.asList(inputs));
+  }
+
+  public AndroidApp runAndCheckVerification(
+      CompilerUnderTest compiler,
+      String referenceApk,
+      String pgMap,
+      String pgConf,
+      List<String> inputs)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    assertTrue(referenceApk == null || new File(referenceApk).exists());
+    AndroidApp outputApp;
+    if (compiler == CompilerUnderTest.R8) {
+      R8Command.Builder builder = R8Command.builder();
+      builder.addProgramFiles(ListUtils.map(inputs, Paths::get));
+      if (pgMap != null) {
+        builder.setProguardMapFile(Paths.get(pgMap));
+      }
+      if (pgConf != null) {
+        builder.addProguardConfigurationFiles(Paths.get(pgConf));
+      }
+      outputApp = ToolHelper.runR8(builder.build());
+    } else {
+      assert compiler == CompilerUnderTest.D8;
+      outputApp =
+          ToolHelper.runD8(
+              D8Command.builder()
+                  .addProgramFiles(ListUtils.map(inputs, Paths::get))
+                  .setMode(CompilationMode.DEBUG)
+                  .build());
+    }
+    Path out = temp.getRoot().toPath().resolve("all.zip");
+    Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+    outputApp.writeToZip(out);
+    try {
+      ToolHelper.runDex2Oat(out, oatFile);
+      return outputApp;
+    } catch (AssertionError e) {
+      if (referenceApk == null) {
+        throw e;
+      }
+      DexInspector theirs = new DexInspector(Paths.get(referenceApk));
+      DexInspector ours = new DexInspector(out);
+      List<ArtErrorInfo> errors;
+      try {
+        errors = ArtErrorParser.parse(e.getMessage());
+      } catch (ArtErrorParserException parserException) {
+        System.err.println(parserException.toString());
+        throw e;
+      }
+      if (errors.isEmpty()) {
+        throw e;
+      }
+      for (ArtErrorInfo error : errors.subList(0, errors.size() - 1)) {
+        System.err.println(new ComparisonFailure(error.getMessage(),
+            "REFERENCE\n" + error.dump(theirs, false) + "\nEND REFERENCE",
+            "PROCESSED\n" + error.dump(ours, true) + "\nEND PROCESSED").toString());
+      }
+      ArtErrorInfo error = errors.get(errors.size() - 1);
+      throw new ComparisonFailure(error.getMessage(),
+          "REFERENCE\n" + error.dump(theirs, false) + "\nEND REFERENCE",
+          "PROCESSED\n" + error.dump(ours, true) + "\nEND PROCESSED");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
new file mode 100644
index 0000000..d5f23d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class D8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
+
+  @Test
+  public void buildFromDeployJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    buildFromDeployJar(CompilerUnderTest.D8, GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
new file mode 100644
index 0000000..ca8d019
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+
+public abstract class GMSCoreCompilationTestBase extends CompilationTestBase {
+  public static final String GMSCORE_V4_DIR = "third_party/gmscore/v4/";
+  public static final String GMSCORE_V5_DIR = "third_party/gmscore/v5/";
+  public static final String GMSCORE_V6_DIR = "third_party/gmscore/v6/";
+  public static final String GMSCORE_V7_DIR = "third_party/gmscore/v7/";
+  public static final String GMSCORE_V8_DIR = "third_party/gmscore/v8/";
+  public static final String GMSCORE_V9_DIR = "third_party/gmscore/gmscore_v9/";
+  public static final String GMSCORE_V10_DIR = "third_party/gmscore/gmscore_v10/";
+
+  public static final int GMSCORE_V9_MAX_SIZE = 35000000;
+  public static final int GMSCORE_V10_MAX_SIZE = 35000000;
+
+  static final String GMSCORE_APK = "GMSCore.apk";
+
+  // Files pertaining to the full GMSCore build.
+  static final String PG_MAP = "GmsCore_prod_alldpi_release_all_locales_proguard.map";
+  static final String PG_CONF = "GmsCore_prod_alldpi_release_all_locales_proguard.config";
+  static final String DEPLOY_JAR = "GmsCore_prod_alldpi_release_all_locales_deploy.jar";
+  static final String REFERENCE_APK = "noshrink_x86_GmsCore_prod_alldpi_release_unsigned.apk";
+
+  public void runR8AndCheckVerification(String version)
+      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+    runAndCheckVerification(CompilerUnderTest.R8, version);
+  }
+
+  public void runAndCheckVerification(CompilerUnderTest compiler, String version)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(
+        compiler, version + GMSCORE_APK, null, null, Paths.get(version, GMSCORE_APK).toString());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
new file mode 100644
index 0000000..ecc7451
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class GMSCoreDeployJarVerificationTest extends GMSCoreCompilationTestBase {
+
+  public void buildFromDeployJar(CompilerUnderTest compiler, String base, boolean hasReference)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(
+        compiler, hasReference ? base + REFERENCE_APK : null, null, null, base + DEPLOY_JAR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java b/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
new file mode 100644
index 0000000..1f20559
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8DisassemblerTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static com.android.tools.r8.utils.AndroidApp.DEFAULT_PROGUARD_MAP_FILE;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.experimental.theories.Theories;
+import org.junit.runner.RunWith;
+
+// Invoke R8 on the dex files extracted from GMSCore.apk to disassemble the dex code.
+@RunWith(Theories.class)
+public class R8DisassemblerTest {
+
+  static final String APP_DIR = "third_party/gmscore/v5/";
+
+  public boolean deobfuscate;
+
+  @Test
+  public void disassemble() throws IOException, ExecutionException, ProguardRuleParserException,
+      CompilationException {
+    // This test only ensures that we do not break disassembling of dex code. It does not
+    // check the generated code. To make it fast, we get rid of the output.
+    PrintStream originalOut = System.out;
+    System.setOut(new PrintStream(new OutputStream() {
+      public void write(int b) { /* ignore*/ }
+    }));
+
+    try {
+      R8Command.Builder builder = R8Command.builder();
+      if (deobfuscate) {
+        builder.setProguardMapFile(Paths.get(APP_DIR, DEFAULT_PROGUARD_MAP_FILE));
+      }
+      builder.addProgramFiles(
+          Files.list(Paths.get(APP_DIR))
+              .filter(FileUtils::isDexFile)
+              .collect(Collectors.toList()));
+      R8.disassemble(builder.build());
+    } finally {
+      // Restore System.out for good measure.
+      System.setOut(originalOut);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
new file mode 100644
index 0000000..1481bc8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreDeterministicTest extends GMSCoreCompilationTestBase {
+
+  private AndroidApp doRun()
+      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+    R8Command command =
+        R8Command.builder().addProgramFiles(Paths.get(GMSCORE_V7_DIR, GMSCORE_APK)).build();
+    return ToolHelper.runR8(command, options -> options.testing.randomizeCallGraphLeaves = true);
+  }
+
+  @Test
+  public void deterministic()
+      throws ExecutionException, IOException, ProguardRuleParserException, InterruptedException,
+          CompilationException {
+
+    // Run two independent compilations.
+    AndroidApp app1 = doRun();
+    AndroidApp app2 = doRun();
+
+    // Verify that the result of the two compilations was the same.
+    try (Closer closer = Closer.create()) {
+      List<InternalResource> files1 = app1.getDexProgramResources();
+      List<InternalResource> files2 = app2.getDexProgramResources();
+      assertEquals(files1.size(), files2.size());
+      for (int index = 0; index < files1.size(); index++) {
+        InputStream file1 = files1.get(index).getStream(closer);
+        InputStream file2 = files2.get(index).getStream(closer);
+        byte[] bytes1 = ByteStreams.toByteArray(file1);
+        byte[] bytes2 = ByteStreams.toByteArray(file2);
+        assertArrayEquals("File index " + index, bytes1, bytes2);
+      }
+    }
+
+    // Check that the generated bytecode runs through the dex2oat verifier with no errors.
+    Path combinedInput = temp.getRoot().toPath().resolve("all.jar");
+    Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+    app1.writeToZip(combinedInput);
+    ToolHelper.runDex2Oat(combinedInput, oatFile);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
new file mode 100644
index 0000000..b6d3dac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreFixedPointTest extends GMSCoreCompilationTestBase {
+
+  @Test
+  public void fixedPoint()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    // First compilation.
+    AndroidApp app = AndroidApp.fromProgramDirectory(Paths.get(GMSCORE_V7_DIR));
+    AndroidApp app1 = ToolHelper.runR8(app);
+
+    // Second compilation.
+    // Add option --skip-outline-opt for second compilation. The second compilation can find
+    // additional outlining opportunities as member rebinding from the first compilation can move
+    // methods.
+    // See b/33410508 and b/33475705.
+    AndroidApp app2 = ToolHelper.runR8(app1, options -> options.outline.enabled = false);
+
+    // TODO: Require that the results of the two compilations are the same.
+    assertEquals(
+        app1.getDexProgramResources().size(),
+        app2.getDexProgramResources().size());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
new file mode 100644
index 0000000..f88ea49
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreLookupTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Before;
+import org.junit.Test;
+
+public class R8GMSCoreLookupTest {
+
+  static final String APP_DIR = "third_party/gmscore/v5/";
+  private AndroidApp app;
+  private DexApplication program;
+  private AppInfoWithSubtyping appInfo;
+
+  @Before
+  public void readGMSCore() throws IOException, ExecutionException {
+    app = AndroidApp.fromProgramDirectory(Paths.get(APP_DIR));
+    ExecutorService executorService = Executors.newSingleThreadExecutor();
+    Timing timing = new Timing("ReadGMSCore");
+    program = new ApplicationReader(app, new InternalOptions(), timing).read(executorService);
+    appInfo = new AppInfoWithSubtyping(program);
+  }
+
+  private void testVirtualLookup(DexProgramClass clazz, DexEncodedMethod method) {
+    // Check lookup will produce the same result.
+    DexMethod id = method.method;
+    assertEquals(appInfo.lookupVirtualTarget(id.holder, method.method), method);
+
+    // Check lookup targets with include method.
+    Set<DexEncodedMethod> targets = appInfo.lookupVirtualTargets(method.method);
+    assertTrue(targets.contains(method));
+  }
+
+  private void testInterfaceLookup(DexProgramClass clazz, DexEncodedMethod method) {
+    Set<DexEncodedMethod> targets = appInfo.lookupVirtualTargets(method.method);
+    assertFalse(targets.isEmpty());
+  }
+
+  private void testLookup(DexProgramClass clazz) {
+    assert appInfo.subtypes(clazz.type)
+        != null : "Application class must have non null subtypes.";
+    if (clazz.isInterface()) {
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        testInterfaceLookup(clazz, method);
+      }
+    } else {
+      for (DexEncodedMethod method : clazz.virtualMethods()) {
+        testVirtualLookup(clazz, method);
+      }
+    }
+  }
+
+  @Test
+  public void testLookup() {
+    program.classes().forEach(this::testLookup);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..2571490
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+public class R8GMSCoreTreeShakeJarVerificationTest extends GMSCoreCompilationTestBase {
+
+  public void buildAndTreeShakeFromDeployJar(String base, boolean hasReference, int maxSize)
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    AndroidApp app = runAndCheckVerification(
+        CompilerUnderTest.R8,
+        hasReference ? base + REFERENCE_APK : null,
+        null,
+        base + PG_CONF,
+        // Don't pass any inputs. The input will be read from the -injars in the Proguard
+        // configuration file.
+        ImmutableList.of());
+    int bytes = 0;
+    try (Closer closer = Closer.create()) {
+      for (InternalResource dex : app.getDexProgramResources()) {
+        bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
+      }
+    }
+    assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
new file mode 100644
index 0000000..5894981
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
+
+  @Test
+  public void buildFromDeployJar()
+      // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    buildFromDeployJar(CompilerUnderTest.R8, GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..5253ac2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10TreeShakeJarVerificationTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV10TreeShakeJarVerificationTest
+    extends R8GMSCoreTreeShakeJarVerificationTest {
+
+  @Test
+  public void buildAndTreeShakeFromDeployJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
+    buildAndTreeShakeFromDeployJar(GMSCORE_V10_DIR, false, GMSCORE_V10_MAX_SIZE);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
new file mode 100644
index 0000000..d7de82e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV4VerificationTest extends GMSCoreCompilationTestBase {
+  @Test
+  public void verify()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runR8AndCheckVerification(GMSCORE_V4_DIR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
new file mode 100644
index 0000000..6a098a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV5VerificationTest extends GMSCoreCompilationTestBase {
+  @Test
+  public void verify()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runR8AndCheckVerification(GMSCORE_V5_DIR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
new file mode 100644
index 0000000..b7ca295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV6VerificationTest extends GMSCoreCompilationTestBase {
+  @Test
+  public void verify()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runR8AndCheckVerification(GMSCORE_V6_DIR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
new file mode 100644
index 0000000..f65a8e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV7VerificationTest extends GMSCoreCompilationTestBase {
+
+  @Test
+  public void verify()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runR8AndCheckVerification(GMSCORE_V7_DIR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
new file mode 100644
index 0000000..bc0dbe8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV8VerificationTest extends GMSCoreCompilationTestBase {
+
+  @Test
+  public void verify()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runR8AndCheckVerification(GMSCORE_V8_DIR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
new file mode 100644
index 0000000..d1e6844
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
+
+  @Test
+  public void buildFromDeployJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    buildFromDeployJar(CompilerUnderTest.R8, GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..2f5ed63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class R8GMSCoreV9TreeShakeJarVerificationTest extends R8GMSCoreTreeShakeJarVerificationTest {
+
+  @Test
+  public void buildAndTreeShakeFromDeployJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    buildAndTreeShakeFromDeployJar(GMSCORE_V9_DIR, true, GMSCORE_V9_MAX_SIZE);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
new file mode 100644
index 0000000..08b3892
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationBase.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+public abstract class YouTubeCompilationBase extends CompilationTestBase {
+  static final String BASE = "third_party/youtube/youtube.android_11.47/";
+  static final String APK = "YouTubeRelease_unsigned.apk";
+  static final String DEPLOY_JAR = "YouTubeRelease_deploy.jar";
+  static final String PG_JAR = "YouTubeRelease_proguard.jar";
+  static final String PG_MAP = "YouTubeRelease_proguard.map";
+  static final String PG_CONF = "YouTubeRelease_proguard.config";
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
new file mode 100644
index 0000000..b40ba07
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class YouTubeDeployJarVerificationTest extends YouTubeCompilationBase {
+
+  @Test
+  public void buildFromDeployJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(CompilerUnderTest.R8, BASE + APK, null, null, BASE + DEPLOY_JAR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
new file mode 100644
index 0000000..19bfe25
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class YouTubeDexVerificationTest extends YouTubeCompilationBase {
+
+  @Test
+  public void buildFromDex()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(CompilerUnderTest.R8, BASE + APK, BASE + PG_MAP, null, BASE + APK);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
new file mode 100644
index 0000000..0b1e553
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class YouTubeProguardJarVerificationTest extends YouTubeCompilationBase {
+
+  @Test
+  public void buildFromProguardJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    runAndCheckVerification(CompilerUnderTest.R8, BASE + APK, BASE + PG_MAP, null, BASE + PG_JAR);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
new file mode 100644
index 0000000..c51129c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.internal;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalResource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class YouTubeTreeShakeJarVerificationTest extends YouTubeCompilationBase {
+
+  @Test
+  @Ignore("b/35656577")
+  public void buildAndTreeShakeFromDeployJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    int maxSize = 16000000;
+    AndroidApp app = runAndCheckVerification(
+        CompilerUnderTest.R8, BASE + APK, null, BASE + PG_CONF, BASE + DEPLOY_JAR);
+    int bytes = 0;
+    try (Closer closer = Closer.create()) {
+      for (InternalResource dex : app.getDexProgramResources()) {
+        bytes += ByteStreams.toByteArray(dex.getStream(closer)).length;
+      }
+    }
+    assertTrue("Expected max size of " + maxSize + ", got " + bytes, bytes < maxSize);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
new file mode 100644
index 0000000..50cee1b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/BasicBlockIteratorTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class BasicBlockIteratorTest extends SmaliTestBase {
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  /**
+   * Simple test IR, which has three blocks:
+   *
+   * First block: Argument instructions
+   * Second block: Add instruction
+   * Third block: Return instruction
+   *
+   */
+  IRCode simpleCode() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "int";
+    List<String> parameters = ImmutableList.of("int", "int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        1,
+        "    add-int             v0, p0, p1",
+        "    return              p0"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Build the code, and split the code into three blocks.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    InstructionListIterator iter = blocks.next().listIterator();
+    iter.nextUntil(i -> !i.isArgument());
+    iter.previous();
+    iter.split(1, code, blocks);
+    return code;
+  }
+
+  @Test
+  public void removeBeforeNext() {
+    IRCode code = simpleCode();
+
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    thrown.expect(IllegalStateException.class);
+    blocks.remove();
+  }
+
+  @Test
+  public void removeTwice() {
+    IRCode code = simpleCode();
+
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    blocks.next();
+    blocks.next();
+    blocks.remove();
+    thrown.expect(IllegalStateException.class);
+    blocks.remove();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java
new file mode 100644
index 0000000..e12d003
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -0,0 +1,1233 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Test;
+
+public class InlineTest extends SmaliTestBase {
+
+  TestApplication codeForMethodReplaceTest(int a, int b) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int"),
+        0,
+        "    invoke-static       { p0, p1 }, LTest;->a(II)I",
+        "    move-result         p0",
+        "    return              p0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of("int", "int"),
+        1,
+        "    add-int             p0, p0, p1",
+        "    return              p0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of("int", "int"),
+        1,
+        "    if-eq               p0, p1, :eq",
+        "    const/4             v0, 1",
+        "    return              v0",
+        "    :eq",
+        "    const/4             v0, 0",
+        "    return              v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + a,
+        "    const/4             v2, " + b,
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodA = getMethod(application, signatureA);
+    IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodB = getMethod(application, signatureB);
+    IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new SmaliTestBase.TestApplication(application, method, code,
+        ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+  }
+
+  public void runInlineTest(int a, int b, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForMethodReplaceTest(a, b);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining a.
+    test = codeForMethodReplaceTest(a, b);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining b (where a is actually called).
+    test = codeForMethodReplaceTest(a, b);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inline() {
+    runInlineTest(1, 1, 2, 0);
+    runInlineTest(1, 2, 3, 1);
+  }
+
+  TestApplication codeForMethodReplaceReturnVoidTest(int a, int b) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int"),
+        0,
+        "    invoke-static       { p0, p1 }, LTest;->a(II)V",
+        "    return              p0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "void",
+        "a",
+        ImmutableList.of("int", "int"),
+        1,
+        "    add-int             p0, p0, p1",
+        "    return-void         "
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + a,
+        "    const/4             v2, " + b,
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodA = getMethod(application, signatureA);
+    IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code,
+        ImmutableList.of(codeA), valueNumberGenerator, options);
+  }
+
+  @Test
+  public void inlineReturnVoid() {
+    // Run code without inlining.
+    TestApplication test = codeForMethodReplaceReturnVoidTest(1, 2);
+    String result = test.run();
+    assertEquals(Integer.toString(1), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining a.
+    test = codeForMethodReplaceReturnVoidTest(1, 2);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+    result = test.run();
+    assertEquals(Integer.toString(1), result);
+  }
+
+  TestApplication codeForMultipleMethodReplaceTest(int a, int b) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int"),
+        0,
+        "    invoke-static       { p0, p1 }, LTest;->a(II)I",
+        "    move-result         p0",
+        "    invoke-static       { p0, p1 }, LTest;->a(II)I",
+        "    move-result         p0",
+        "    invoke-static       { p0, p1 }, LTest;->a(II)I",
+        "    move-result         p0",
+        "    return              p0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of("int", "int"),
+        1,
+        "    add-int             p0, p0, p1",
+        "    return              p0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of("int", "int"),
+        1,
+        "    mul-int             p0, p0, p1",
+        "    return              p0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + a,
+        "    const/4             v2, " + b,
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    // Build three copies of a and b for inlining three times.
+    List<IRCode> additionalCode = new ArrayList<>();
+    for (int i = 0; i < 3; i++) {
+      DexEncodedMethod methodA = getMethod(application, signatureA);
+      IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+      additionalCode.add(codeA);
+    }
+
+    for (int i = 0; i < 3; i++) {
+      DexEncodedMethod methodB = getMethod(application, signatureB);
+      IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+      additionalCode.add(codeB);
+    }
+
+    return new TestApplication(application, method, code,
+        additionalCode, valueNumberGenerator, options);
+  }
+
+  public void runInlineMultipleTest(int a, int b, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForMultipleMethodReplaceTest(a, b);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining all invokes with a.
+    test = codeForMultipleMethodReplaceTest(a, b);
+    ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+    Iterator<IRCode> inlinee = test.additionalCode.listIterator();  // IR code for a's
+    List<BasicBlock> blocksToRemove = new ArrayList<>();
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      iterator = block.listIterator();
+      Instruction invoke = iterator.nextUntil(instruction -> instruction.isInvoke());
+      if (invoke != null) {
+        iterator.previous();
+        iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+        assert blocksToRemove.isEmpty();
+      }
+    }
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining all invokes with b.
+    test = codeForMultipleMethodReplaceTest(a, b);
+    blocksIterator = test.code.blocks.listIterator();
+    inlinee = test.additionalCode.listIterator(3);  // IR code for b's
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      iterator = block.listIterator();
+      Instruction invoke = iterator.nextUntil(instruction -> instruction.isInvoke());
+      if (invoke != null) {
+        iterator.previous();
+        iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+        assert blocksToRemove.isEmpty();
+      }
+    }
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineMultiple() {
+    runInlineMultipleTest(1, 1, 4, 1);
+    runInlineMultipleTest(1, 2, 7, 8);
+  }
+
+  TestApplication codeForMethodReplaceTestWithCatchHandler(int a, int b, boolean twoGuards) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = twoGuards ?
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : "    ";
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int"),
+        0,
+        "    :try_start",
+        "    invoke-static       { p0, p1 }, LTest;->a(II)I",
+        "    :try_end",
+        "    move-result         p0",
+        "    :return",
+        "    return              p0",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        "    :catch",
+        "    const/4             p0, -1",
+        "    goto :return"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of("int", "int"),
+        1,
+        "    add-int             p0, p0, p1",
+        "    return              p0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of("int", "int"),
+        1,
+        "    if-eq               p0, p1, :eq",
+        "    const/4             v0, 1",
+        "    return              v0",
+        "    :eq",
+        "    const/4             v0, 0",
+        "    return              v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + a,
+        "    const/4             v2, " + b,
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodA = getMethod(application, signatureA);
+    IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodB = getMethod(application, signatureB);
+    IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code,
+        ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+  }
+
+  public void runInlineCallerHasCatchHandlersTest(
+      int a, int b, boolean twoGuards, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining a.
+    test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
+    iterator = test.code.blocks.get(1).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining b (where a is actually called).
+    test = codeForMethodReplaceTestWithCatchHandler(a, b, twoGuards);
+    iterator = test.code.blocks.get(1).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineCallerHasCatchHandlers() {
+    runInlineCallerHasCatchHandlersTest(1, 1, false, 2, 0);
+    runInlineCallerHasCatchHandlersTest(1, 2, false, 3, 1);
+    runInlineCallerHasCatchHandlersTest(1, 1, true, 2, 0);
+    runInlineCallerHasCatchHandlersTest(1, 2, true, 3, 1);
+  }
+
+  TestApplication codeForInlineCanThrow(int a, int b, boolean twoGuards) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = twoGuards ?
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : "    ";
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int"),
+        0,
+        "    invoke-static       { p0, p1 }, LTest;->a(II)I",
+        "    move-result         p0",
+        "    return              p0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of("int", "int"),
+        0,
+        "    div-int             p0, p0, p1",
+        "    return              p0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of("int", "int"),
+        0,
+        "    :try_start",
+        "    div-int             p0, p0, p1",
+        "    :try_end",
+        "    :return",
+        "    return              p0",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        "    :catch",
+        "    const/4             p0, -1",
+        "    goto :return"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + a,
+        "    const/4             v2, " + b,
+        "    :try_start",
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    :try_end",
+        "    move-result         v1",
+        "    :print_result",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const/4             v1, -2",
+        "    goto :print_result"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodA = getMethod(application, signatureA);
+    IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodB = getMethod(application, signatureB);
+    IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code,
+        ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+  }
+
+  public void runInlineCanThrow(
+      int a, int b, boolean twoGuards, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForInlineCanThrow(a, b, twoGuards);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining a.
+    test = codeForInlineCanThrow(a, b, twoGuards);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining b (where a is actually called).
+    test = codeForInlineCanThrow(a, b, twoGuards);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(instruction -> instruction.isInvoke());
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineCanThrow() {
+    runInlineCanThrow(2, 2, false, 1, 1);
+    runInlineCanThrow(2, 0, false, -2, -1);
+    runInlineCanThrow(2, 2, true, 1, 1);
+    runInlineCanThrow(2, 0, true, -2, -1);
+  }
+
+  private TestApplication codeForInlineAlwaysThrows(boolean twoGuards) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = twoGuards ?
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : "    ";
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of(),
+        1,
+        "    invoke-static       { }, LTest;->a()I",
+        "    move-result         v0",
+        "    return              v0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of(),
+        1,
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of(),
+        1,
+        "    :try_start",
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0",
+        "    :try_end",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        "    :catch",
+        "    const/4             v0, -1",
+        "    return              v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    :try_start",
+        "    invoke-static       { }, LTest;->method()I",
+        "    :try_end",
+        "    move-result         v1",
+        "    :print_result",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void",
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const/4             v1, -2",
+        "    goto :print_result"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodA = getMethod(application, signatureA);
+    IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodB = getMethod(application, signatureB);
+    IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code,
+        ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+  }
+
+  private void runInlineAlwaysThrows(boolean twoGuards, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining a.
+    test = codeForInlineAlwaysThrows(twoGuards);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(Instruction::isInvoke);
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining b (where a is actually called).
+    test = codeForInlineAlwaysThrows(twoGuards);
+    iterator = test.code.blocks.get(0).listIterator();
+    iterator.nextUntil(Instruction::isInvoke);
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineAlwaysThrows() {
+    runInlineAlwaysThrows(false, -2, -2);
+    runInlineAlwaysThrows(true, -2, -1);
+  }
+
+  private TestApplication codeForInlineAlwaysThrowsMultiple(boolean twoGuards) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = twoGuards ?
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : "    ";
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of(),
+        1,
+        "    invoke-static       { }, LTest;->a()I",
+        "    invoke-static       { }, LTest;->a()I",
+        "    invoke-static       { }, LTest;->a()I",
+        "    move-result         v0",
+        "    return              v0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of(),
+        1,
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of(),
+        1,
+        "    :try_start",
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0",
+        "    :try_end",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        "    :catch",
+        "    const/4             v0, -1",
+        "    return              v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    :try_start",
+        "    invoke-static       { }, LTest;->method()I",
+        "    :try_end",
+        "    move-result         v1",
+        "    :print_result",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void",
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const/4             v1, -2",
+        "    goto :print_result"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    // Build three copies of a and b for inlining three times.
+    List<IRCode> additionalCode = new ArrayList<>();
+    for (int i = 0; i < 3; i++) {
+      DexEncodedMethod methodA = getMethod(application, signatureA);
+      IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+      additionalCode.add(codeA);
+    }
+
+    for (int i = 0; i < 3; i++) {
+      DexEncodedMethod methodB = getMethod(application, signatureB);
+      IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+      additionalCode.add(codeB);
+    }
+
+    return new TestApplication(
+        application, method, code, additionalCode, valueNumberGenerator, options);
+  }
+
+  private void runInlineAlwaysThrowsMultiple(boolean twoGuards, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining all invokes with a.
+    test = codeForInlineAlwaysThrowsMultiple(twoGuards);
+    ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+    Iterator<IRCode> inlinee = test.additionalCode.listIterator();  // IR code for a's.
+    List<BasicBlock> blocksToRemove = new ArrayList<>();
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
+      iterator = block.listIterator();
+      Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+      if (invoke != null) {
+        iterator.previous();
+        iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+        assert !blocksToRemove.isEmpty();
+      }
+    }
+    test.code.removeBlocks(blocksToRemove);
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining all invokes with b.
+    test = codeForInlineAlwaysThrowsMultiple(twoGuards);
+    blocksIterator = test.code.blocks.listIterator();
+    inlinee = test.additionalCode.listIterator(3);  // IR code for b's.
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
+      iterator = block.listIterator();
+      Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+      if (invoke != null) {
+        iterator.previous();
+        iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+        assert !blocksToRemove.isEmpty();
+      }
+    }
+    test.code.removeBlocks(blocksToRemove);
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineAlwaysThrowsMultiple() {
+    runInlineAlwaysThrowsMultiple(false, -2, -2);
+    runInlineAlwaysThrowsMultiple(true, -2, -1);
+  }
+
+  private TestApplication codeForInlineAlwaysThrowsMultipleWithControlFlow(
+      int a, boolean twoGuards) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = twoGuards ?
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : "    ";
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int"),
+        1,
+        "    const/4             v0, 0",
+        "    if-ne               v0, p0, :not_zero",
+        "    invoke-static       { }, LTest;->a()I",
+        "    :not_zero",
+        "    const/4             v0, 1",
+        "    if-ne               v0, p0, :not_one",
+        "    invoke-static       { }, LTest;->a()I",
+        "    :not_one",
+        "    invoke-static       { }, LTest;->a()I",
+        "    move-result         v0",
+        "    return              v0"
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of(),
+        1,
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0"
+    );
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of(),
+        1,
+        "    :try_start",
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0",
+        "    :try_end",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        "    :catch",
+        "    const/4             v0, -1",
+        "    return              v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + a,
+        "    :try_start",
+        "    invoke-static       { v1 }, LTest;->method(I)I",
+        "    :try_end",
+        "    move-result         v1",
+        "    :print_result",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void",
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const/4             v1, -2",
+        "    goto :print_result"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    // Build three copies of a and b for inlining three times.
+    List<IRCode> additionalCode = new ArrayList<>();
+    for (int i = 0; i < 3; i++) {
+      DexEncodedMethod methodA = getMethod(application, signatureA);
+      IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+      additionalCode.add(codeA);
+    }
+
+    for (int i = 0; i < 3; i++) {
+      DexEncodedMethod methodB = getMethod(application, signatureB);
+      IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+      additionalCode.add(codeB);
+    }
+
+    return new TestApplication(
+        application, method, code, additionalCode, valueNumberGenerator, options);
+  }
+
+  private void runInlineAlwaysThrowsMultipleWithControlFlow(
+      int a, boolean twoGuards, int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForInlineAlwaysThrows(twoGuards);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining all invokes with a.
+    test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
+    ListIterator<BasicBlock> blocksIterator = test.code.blocks.listIterator();
+    Iterator<IRCode> inlinee = test.additionalCode.listIterator();  // IR code for a's.
+    List<BasicBlock> blocksToRemove = new ArrayList<>();
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
+      iterator = block.listIterator();
+      Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+      if (invoke != null) {
+        iterator.previous();
+        iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+        assert !blocksToRemove.isEmpty();
+      }
+    }
+    test.code.removeBlocks(blocksToRemove);
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining all invokes with b.
+    test = codeForInlineAlwaysThrowsMultipleWithControlFlow(a, twoGuards);
+    blocksIterator = test.code.blocks.listIterator();
+    inlinee = test.additionalCode.listIterator(3);  // IR code for b's.
+    while (blocksIterator.hasNext()) {
+      BasicBlock block = blocksIterator.next();
+      if (blocksToRemove.contains(block)) {
+        continue;
+      }
+      iterator = block.listIterator();
+      Instruction invoke = iterator.nextUntil(Instruction::isInvoke);
+      if (invoke != null) {
+        iterator.previous();
+        iterator.inlineInvoke(test.code, inlinee.next(), blocksIterator, blocksToRemove);
+        assert !blocksToRemove.isEmpty();
+      }
+    }
+    test.code.removeBlocks(blocksToRemove);
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineAlwaysThrowsMultipleWithControlFlow() {
+    runInlineAlwaysThrowsMultipleWithControlFlow(0, false, -2, -2);
+    runInlineAlwaysThrowsMultipleWithControlFlow(0, true, -2, -1);
+    runInlineAlwaysThrowsMultipleWithControlFlow(1, false, -2, -2);
+    runInlineAlwaysThrowsMultipleWithControlFlow(1, true, -2, -1);
+    runInlineAlwaysThrowsMultipleWithControlFlow(2, false, -2, -2);
+    runInlineAlwaysThrowsMultipleWithControlFlow(2, true, -2, -1);
+  }
+
+  private TestApplication codeForInlineWithHandlersCanThrow(int a, int b, int c,
+      boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = "";
+    String secondGuardCode = "";
+    String callerCatchAllGuard = "";
+    String callerCatchAllCode = "";
+
+    if (twoGuards) {
+      String secondGuardLabel = "catch2";
+      secondGuard =
+          "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :" + secondGuardLabel;
+      secondGuardCode =
+          "    :" + secondGuardLabel + "\n" +
+          "    const               p0, -12\n" +
+          "    goto :return";
+    }
+
+    if (callerHasCatchAll) {
+      String catchAllLabel = "catch_all";
+      callerCatchAllGuard = "    .catchall {:try_start .. :try_end} :" + catchAllLabel;
+      callerCatchAllCode =
+          "    :" + catchAllLabel + "\n" +
+          "    const               p0, -13\n" +
+          "    goto :return";
+    }
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int", "int"),
+        0,
+        "    :try_start",
+        "    invoke-static       { p0, p1, p2 }, LTest;->a(III)I",
+        "    :try_end",
+        "    move-result         p0",
+        "    :return",
+        "    return              p0",
+        "    .catch Ljava/lang/NullPointerException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        callerCatchAllGuard,
+        "    :catch",
+        "    const               p0, -11",
+        "    goto :return",
+        secondGuardCode,
+        callerCatchAllCode
+    );
+
+    MethodSignature signatureA = builder.addStaticMethod(
+        "int",
+        "a",
+        ImmutableList.of("int", "int", "int"),
+        1,
+        "    const               v0, 4",
+        "    div-int             p0, v0, p0",
+        "    const               v0, 8",
+        "    div-int             p1, v0, p1",
+        "    add-int             p0, p0, p1",
+        "    const               v0, 16",
+        "    div-int             p2, v0, p2",
+        "    add-int             p0, p0, p2",
+        "    const               v0, 3",
+        "    if-ne               v0, p0, :not_three",
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0",
+        "    :not_three",
+        "    return              p0"
+    );
+
+    String inlineeSecondGuard = "";
+    String inlineeSecondGuardCode = "";
+    if (twoGuards) {
+      String secondGuardLabel = "catch2";
+      inlineeSecondGuard =
+          "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :" + secondGuardLabel;
+      inlineeSecondGuardCode =
+          "    :" + secondGuardLabel + "\n" +
+          "    const               p0, -2\n" +
+          "    goto :return";
+    }
+
+    String inlineeCatchAllGuard = "";
+    String inlineeCatchAllCode = "";
+    if (inlineeHasCatchAll) {
+      String catchAllLabel = "catch_all";
+      inlineeCatchAllGuard = "    .catchall {:try_start .. :try_end} :" + catchAllLabel;
+      inlineeCatchAllCode =
+          "    :" + catchAllLabel + "\n" +
+          "    const               p0, -3\n" +
+          "    goto :return";
+    }
+
+    MethodSignature signatureB = builder.addStaticMethod(
+        "int",
+        "b",
+        ImmutableList.of("int", "int", "int"),
+        1,
+        "    :try_start",
+        "    const               v0, 4",
+        "    div-int             p0, v0, p0",
+        "    const               v0, 8",
+        "    div-int             p1, v0, p1",
+        "    add-int             p0, p0, p1",
+        "    const               v0, 16",
+        "    div-int             p2, v0, p2",
+        "    add-int             p0, p0, p2",
+        "    const               v0, 3",
+        "    if-ne               v0, p0, :not_three",
+        "    new-instance v0, Ljava/lang/Exception;",
+        "    invoke-direct { v0 }, Ljava/lang/Exception;-><init>()V",
+        "    throw v0",
+        "    :not_three",
+        "    :try_end",
+        "    :return",
+        "    return              p0",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        inlineeSecondGuard,
+        inlineeCatchAllGuard,
+        "    :catch",
+        "    const/4             p0, -1",
+        "    goto :return",
+        inlineeSecondGuardCode,
+        inlineeCatchAllCode
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const               v1, " + a,
+        "    const               v2, " + b,
+        "    const               v3, " + c,
+        "    :try_start",
+        "    invoke-static       { v1, v2, v3 }, LTest;->method(III)I",
+        "    :try_end",
+        "    move-result         v1",
+        "    :print_result",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch2",
+        "    .catchall {:try_start .. :try_end} :catch_all",
+        "    :catch",
+        "    const               v1, -21",
+        "    goto :print_result",
+        "    :catch2",
+        "    const               v1, -22",
+        "    goto :print_result",
+        "    :catch_all",
+        "    const               v1, -23",
+        "    goto :print_result"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodA = getMethod(application, signatureA);
+    IRCode codeA = methodA.buildIR(valueNumberGenerator, new InternalOptions());
+
+    DexEncodedMethod methodB = getMethod(application, signatureB);
+    IRCode codeB = methodB.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code,
+        ImmutableList.of(codeA, codeB), valueNumberGenerator, options);
+  }
+
+  private void runInlineWithHandlersCanThrow(int a, int b, int c,
+      boolean twoGuards, boolean callerHasCatchAll, boolean inlineeHasCatchAll,
+      int expectedA, int expectedB) {
+    // Run code without inlining.
+    TestApplication test = codeForInlineWithHandlersCanThrow(
+        a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
+    String result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    InstructionListIterator iterator;
+
+    // Run code inlining a.
+    test = codeForInlineWithHandlersCanThrow(
+        a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
+    iterator = test.code.blocks.get(1).listIterator();
+    iterator.nextUntil(Instruction::isInvoke);
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(0));
+    result = test.run();
+    assertEquals(Integer.toString(expectedA), result);
+
+    // Run code inlining b (where a is actually called).
+    test = codeForInlineWithHandlersCanThrow(
+        a, b, c, twoGuards, callerHasCatchAll, inlineeHasCatchAll);
+    iterator = test.code.blocks.get(1).listIterator();
+    iterator.nextUntil(Instruction::isInvoke);
+    iterator.previous();
+    iterator.inlineInvoke(test.code, test.additionalCode.get(1));
+    result = test.run();
+    assertEquals(Integer.toString(expectedB), result);
+  }
+
+  @Test
+  public void inlineCanWithHandlersThrow() {
+    // The base generated code will be:
+    //
+    //  int method(int a, int b, int c) {
+    //    try {
+    //      return a(a, b, c);  // Either a or b will be inlined here.
+    //    } catch (NullPointerException e) {
+    //      return -11;
+    //    }
+    //    // More handlers can be added.
+    //  }
+    //
+    //  int a(int a, int b, int c) {
+    //    int result = 4 / a + 8 / b + 16 / c;
+    //    if (result == 3) throw new Exception();
+    //    return result
+    //  }
+    //
+    //  int b(int a, int b, int c) {
+    //    try {
+    //      int result = 4 / a + 8 / b + 16 / c;
+    //      if (result == 3) throw new Exception();
+    //      return result
+    //    } catch (ArithmeticException e) {
+    //      return -1;
+    //    }
+    //    // More handlers can be added.
+    //  }
+    //
+    //  void main(String[] args) {
+    //    try {
+    //    } catch (ArithmeticException e) {
+    //      return -21;
+    //    } catch (Exception e) {
+    //      return -22;
+    //    } catch (Throwable e) {  // Smali/dex catchall.
+    //      return -23;
+    //    }
+    //  }
+    //
+    // The flags (secondGuard, callerHasCatchAll and inlineeHasCatchAll) will add more catch
+    // handlers
+    List<Boolean> allBooleans = ImmutableList.of(true, false);
+    for (boolean secondGuard : allBooleans) {
+      for (boolean callerHasCatchAll : allBooleans) {
+        for (boolean inlineeHasCatchAll : allBooleans) {
+          // This throws no exception, but always returns 6.
+          runInlineWithHandlersCanThrow(
+              2, 4, 8, secondGuard, callerHasCatchAll, inlineeHasCatchAll, 6, 6);
+
+          // This is result for calling a.
+          int resulta =
+              secondGuard ? -12
+                  : (callerHasCatchAll ? -13 : -21);
+          // This is result for calling b.
+          int resultb = - 1;
+          // This group all throw ArithmeticException.
+          runInlineWithHandlersCanThrow(
+              0, 4, 8, secondGuard, callerHasCatchAll, inlineeHasCatchAll, resulta, resultb);
+          runInlineWithHandlersCanThrow(
+              2, 0, 8, secondGuard, callerHasCatchAll, inlineeHasCatchAll, resulta, resultb);
+          runInlineWithHandlersCanThrow(
+              2, 4, 0, secondGuard, callerHasCatchAll, inlineeHasCatchAll, resulta, resultb);
+        }
+      }
+    }
+
+    // The following group will throw Exception from the inlinee.
+    runInlineWithHandlersCanThrow(4, 8, 16, false, false, false, -22, -22);
+    runInlineWithHandlersCanThrow(4, 8, 16, true, false, false, -12, -2);
+    runInlineWithHandlersCanThrow(4, 8, 16, false, true, false, -13, -13);
+    runInlineWithHandlersCanThrow(4, 8, 16, true, true, false, -12, -2);
+    runInlineWithHandlersCanThrow(4, 8, 16, false, false, true, -22, -3);
+    runInlineWithHandlersCanThrow(4, 8, 16, true, false, true, -12, -2);
+    runInlineWithHandlersCanThrow(4, 8, 16, false, true, true, -13, -3);
+    runInlineWithHandlersCanThrow(4, 8, 16, true, true, true, -12, -2);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
new file mode 100644
index 0000000..1e35475
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/InstructionIteratorTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class InstructionIteratorTest extends SmaliTestBase {
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  /**
+   * Simple test IR, which has three blocks:
+   *
+   * First block: Argument instructions
+   * Second block: Add instruction
+   * Third block: Return instruction
+   *
+   */
+  IRCode simpleCode() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "int";
+    List<String> parameters = ImmutableList.of("int", "int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        1,
+        "    add-int             v0, p0, p1",
+        "    return              p0"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Build the code, and split the code into three blocks.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    InstructionListIterator iter = blocks.next().listIterator();
+    iter.nextUntil(i -> !i.isArgument());
+    iter.previous();
+    iter.split(1, code, blocks);
+    return code;
+  }
+
+  @Test
+  public void removeBeforeNext() {
+    IRCode code = simpleCode();
+
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    ListIterator<Instruction> instructions = blocks.next().listIterator();
+    thrown.expect(IllegalStateException.class);
+    instructions.remove();
+  }
+
+  @Test
+  public void removeTwice() {
+    IRCode code = simpleCode();
+
+    ListIterator<BasicBlock> blocks = code.listIterator();
+    blocks.next();
+    ListIterator<Instruction> instructions = blocks.next().listIterator();
+    instructions.next();
+    instructions.remove();
+    thrown.expect(IllegalStateException.class);
+    instructions.remove();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
new file mode 100644
index 0000000..291c2c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/SplitBlockTest.java
@@ -0,0 +1,469 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+
+public class SplitBlockTest extends SmaliTestBase {
+
+  TestApplication codeWithoutCatchHandlers() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "int";
+    List<String> parameters = ImmutableList.of("int", "int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        0,
+        "    add-int             p0, p0, p0",
+        "    sub-int             p1, p1, p0",
+        "    mul-int             p0, p0, p1",
+        "    return              p0"
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 1",
+        "    const/4             v2, 5",
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code, valueNumberGenerator, options);
+  }
+
+  @Test
+  public void noCatchHandlers() {
+    final int initialBlockCount = 1;
+    final int argumentInstructions = 2;
+    final int firstBlockInstructions = 6;
+    // Try split between all non-argument instructions in the first block.
+    for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
+      TestApplication test = codeWithoutCatchHandlers();
+      IRCode code = test.code;
+      assertEquals(initialBlockCount, code.blocks.size());
+
+      BasicBlock block = code.blocks.get(0);
+      int instructionCount = block.getInstructions().size();
+      assertEquals(firstBlockInstructions, instructionCount);
+
+      assertEquals(argumentInstructions, test.countArgumentInstructions());
+      assertEquals(firstBlockInstructions, block.getInstructions().size());
+      assertTrue(!block.getInstructions().get(i).isArgument());
+
+      InstructionListIterator iterator = test.listIteratorAt(block, i);
+      BasicBlock newBlock = iterator.split(code);
+      assertTrue(code.isConsistentSSA());
+
+      assertEquals(initialBlockCount + 1, code.blocks.size());
+      assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+      assertEquals(instructionCount - i, code.blocks.get(1).getInstructions().size());
+      assertSame(newBlock, code.blocks.get(1));
+
+      // Run code and check result (code in the test object is updated).
+      String result = test.run();
+      assertEquals("6", result);
+    }
+  }
+
+  @Test
+  public void noCatchHandlersSplitThree() {
+    final int initialBlockCount = 1;
+    final int argumentInstructions = 2;
+    final int firstBlockInstructions = 6;
+    // Try split out all non-argument instructions in the first block.
+    for (int i = argumentInstructions; i < firstBlockInstructions - 1; i++) {
+      TestApplication test = codeWithoutCatchHandlers();
+      IRCode code = test.code;
+      assertEquals(initialBlockCount, code.blocks.size());
+
+      BasicBlock block = code.blocks.get(0);
+      int instructionCount = block.getInstructions().size();
+      assertEquals(firstBlockInstructions, instructionCount);
+
+      assertEquals(argumentInstructions, test.countArgumentInstructions());
+      assertEquals(firstBlockInstructions, block.getInstructions().size());
+      assertTrue(!block.getInstructions().get(i).isArgument());
+
+      InstructionListIterator iterator = test.listIteratorAt(block, i);
+      BasicBlock newBlock = iterator.split(1, code);
+      assertTrue(code.isConsistentSSA());
+
+      assertEquals(initialBlockCount + 2, code.blocks.size());
+      assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+      assertEquals(2, code.blocks.get(1).getInstructions().size());
+      assertEquals(instructionCount - i - 1, code.blocks.get(2).getInstructions().size());
+      assertSame(newBlock, code.blocks.get(1));
+
+      // Run code and check result (code in the test object is updated).
+      String result = test.run();
+      assertEquals("6", result);
+    }
+  }
+
+  TestApplication codeWithCatchHandlers(boolean shouldThrow, boolean twoGuards) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String secondGuard = twoGuards ?
+        "    .catch Ljava/lang/Exception; {:try_start .. :try_end} :catch" : "    ";
+
+    String returnType = "int";
+    List<String> parameters = ImmutableList.of("int", "int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        0,
+        "    :try_start",
+        "    add-int             p0, p0, p0",
+        "    add-int             p1, p1, p1",
+        "    div-int             p0, p0, p1",
+        "    :try_end",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        secondGuard,
+        "    :return",
+        "    return              p0",
+        "    :catch",
+        "    const/4             p0, -1",
+        "    goto :return"
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 2",
+        "    const/4             v2, " + (shouldThrow ? "0" : "1"),
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+    return new TestApplication(application, method, code, valueNumberGenerator, options);
+  }
+
+  public void hasCatchandlerIfThrowing(BasicBlock block) {
+    boolean throwing = false;
+    for (Instruction instruction : block.getInstructions()) {
+      throwing |= instruction.instructionTypeCanThrow();
+    }
+    assertEquals(throwing, block.hasCatchHandlers());
+  }
+
+  public void runCatchHandlerTest(boolean codeThrows, boolean twoGuards) {
+    final int secondBlockInstructions = 4;
+    final int initialBlockCount = 5;
+    // Try split between all instructions in second block.
+    for (int i = 1; i < secondBlockInstructions; i++) {
+      TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
+      IRCode code = test.code;
+      assertEquals(initialBlockCount, code.blocks.size());
+
+      BasicBlock block = code.blocks.get(1);
+      int instructionCount = block.getInstructions().size();
+      assertEquals(secondBlockInstructions, instructionCount);
+
+      InstructionListIterator iterator = test.listIteratorAt(block, i);
+      BasicBlock newBlock = iterator.split(code);
+      assertTrue(code.isConsistentSSA());
+
+      assertEquals(initialBlockCount + 1, code.blocks.size());
+      assertEquals(i + 1, code.blocks.get(1).getInstructions().size());
+      assertEquals(instructionCount - i, newBlock.getInstructions().size());
+      assertSame(newBlock, code.blocks.get(2));
+
+      code.blocks.forEach(this::hasCatchandlerIfThrowing);
+
+      // Run code and check result (code in the test object is updated).
+      String result = test.run();
+      assertEquals(codeThrows ? "-1" : "2", result);
+    }
+  }
+
+  @Test
+  public void catchHandlers() {
+    runCatchHandlerTest(false, false);
+    runCatchHandlerTest(true, false);
+    runCatchHandlerTest(false, true);
+    runCatchHandlerTest(true, true);
+  }
+
+  public void runCatchHandlerSplitThreeTest(boolean codeThrows, boolean twoGuards) {
+    final int secondBlockInstructions = 4;
+    final int initialBlockCount = 5;
+    // Try split out all instructions in second block.
+    for (int i = 1; i < secondBlockInstructions - 1; i++) {
+      TestApplication test = codeWithCatchHandlers(codeThrows, twoGuards);
+      IRCode code = test.code;
+      assertEquals(initialBlockCount, code.blocks.size());
+
+      BasicBlock block = code.blocks.get(1);
+      int instructionCount = block.getInstructions().size();
+      assertEquals(secondBlockInstructions, instructionCount);
+
+      InstructionListIterator iterator = test.listIteratorAt(block, i);
+      BasicBlock newBlock = iterator.split(1, code);
+      assertTrue(code.isConsistentSSA());
+
+      assertEquals(initialBlockCount + 2, code.blocks.size());
+      assertEquals(i + 1, code.blocks.get(1).getInstructions().size());
+      assertEquals(2, newBlock.getInstructions().size());
+      assertEquals(instructionCount - i - 1, code.blocks.get(3).getInstructions().size());
+      assertSame(newBlock, code.blocks.get(2));
+
+      code.blocks.forEach(this::hasCatchandlerIfThrowing);
+
+      // Run code and check result (code in the test object is updated).
+      String result = test.run();
+      assertEquals(codeThrows ? "-1" : "2", result);
+    }
+  }
+
+  @Test
+  public void catchHandlersSplitThree() {
+    runCatchHandlerSplitThreeTest(false, false);
+    runCatchHandlerSplitThreeTest(true, false);
+    runCatchHandlerSplitThreeTest(false, true);
+    runCatchHandlerSplitThreeTest(true, true);
+  }
+
+  TestApplication codeWithIf(boolean hitTrueBranch) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "int";
+    List<String> parameters = ImmutableList.of("int", "int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        0,
+        "    if-eq               p0, p1, :eq",
+        "    const/4             p0, 1",
+        "    return              p0",
+        "    :eq",
+        "    const/4             p0, 0",
+        "    return              p0"
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 2",
+        "    const/4             v2, " + (hitTrueBranch ? "2" : "3"),
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code, valueNumberGenerator, options);
+  }
+
+  public void runWithIfTest(boolean hitTrueBranch) {
+    final int initialBlockCount = 4;
+    final int argumentInstructions = 2;
+    final int firstBlockInstructions = 3;
+    // Try split between all non-argument instructions in the first block.
+    for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
+      TestApplication test = codeWithIf(hitTrueBranch);
+      IRCode code = test.code;
+      assertEquals(initialBlockCount, code.blocks.size());
+
+      BasicBlock block = code.blocks.get(0);
+      int instructionCount = block.getInstructions().size();
+      assertEquals(firstBlockInstructions, instructionCount);
+
+      assertEquals(argumentInstructions, test.countArgumentInstructions());
+      assertEquals(firstBlockInstructions, block.getInstructions().size());
+      assertTrue(!block.getInstructions().get(i).isArgument());
+
+      InstructionListIterator iterator = test.listIteratorAt(block, i);
+      BasicBlock newBlock = iterator.split(code);
+      assertTrue(code.isConsistentSSA());
+
+      assertEquals(initialBlockCount + 1, code.blocks.size());
+      assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+      assertEquals(instructionCount - i, newBlock.getInstructions().size());
+      assertSame(newBlock, code.blocks.get(1));
+
+      // Run code and check result (code in the test object is updated).
+      String result = test.run();
+      assertEquals(hitTrueBranch ? "0" : "1", result);
+    }
+  }
+
+  @Test
+  public void withIf() {
+    runWithIfTest(false);
+    runWithIfTest(true);
+  }
+
+  public void splitBeforeReturn(boolean hitTrueBranch) {
+    TestApplication test = codeWithIf(hitTrueBranch);
+    IRCode code = test.code;
+    // Locate the exit block and split before the return (the first instruction in the block).
+    BasicBlock originalReturnBlock = code.getNormalExitBlock();
+    BasicBlock newReturnBlock = originalReturnBlock.listIterator().split(code);
+    // Modify the code to make the inserted block add the constant 10 to the original return value.
+    Value newConstValue = new Value(test.valueNumberGenerator.next(), -1, MoveType.SINGLE, null);
+    Value newReturnValue = new Value(test.valueNumberGenerator.next(), -1, MoveType.SINGLE, null);
+    Value oldReturnValue = newReturnBlock.listIterator().next().asReturn().returnValue();
+    newReturnBlock.listIterator().next().asReturn().returnValue().replaceUsers(newReturnValue);
+    Instruction constInstruction = new ConstNumber(ConstType.INT, newConstValue, 10);
+    Instruction addInstruction = new Add(
+        NumericType.INT,
+        newReturnValue,
+        oldReturnValue,
+        newConstValue);
+    InstructionListIterator iterator = originalReturnBlock.listIterator();
+    iterator.add(constInstruction);
+    iterator.add(addInstruction);
+
+    // Run code and check result (code in the test object is updated).
+    String result = test.run();
+    assertEquals(hitTrueBranch ? "10" : "11", result);
+  }
+
+  @Test
+  public void splitBeforeReturn() {
+    splitBeforeReturn(false);
+    splitBeforeReturn(true);
+  }
+
+  TestApplication codeWithSwitch(boolean hitCase) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "int";
+    List<String> parameters = ImmutableList.of("int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        0,
+        "    packed-switch       p0, :packed_switch_data",
+        "    const/4             p0, 0x5",
+        "    goto                :return",
+        "    :case_0",
+        "    const/4             p0, 0x2",
+        "    goto                :return",
+        "    :case_1",
+        "    const/4             p0, 0x3",
+        "    :return",
+        "    return              p0",
+        "    :packed_switch_data",
+        "    .packed-switch 0x0",
+        "      :case_0",
+        "      :case_1",
+        "    .end packed-switch"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, " + (hitCase ? "1" : "2"),
+        "    invoke-static       { v1 }, LTest;->method(I)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication application = buildApplication(builder, options);
+
+    // Return the processed method for inspection.
+    ValueNumberGenerator valueNumberGenerator = new ValueNumberGenerator();
+    DexEncodedMethod method = getMethod(application, signature);
+    IRCode code = method.buildIR(valueNumberGenerator, new InternalOptions());
+
+    return new TestApplication(application, method, code, valueNumberGenerator, options);
+  }
+
+  public void runWithSwitchTest(boolean hitCase) {
+    final int initialBlockCount = 5;
+    final int argumentInstructions = 1;
+    final int firstBlockInstructions = 2;
+    // Try split between all non-argument instructions in the first block.
+    for (int i = argumentInstructions; i < firstBlockInstructions; i++) {
+      TestApplication test = codeWithSwitch(hitCase);
+      IRCode code = test.code;
+      assertEquals(initialBlockCount, code.blocks.size());
+
+      BasicBlock block = code.blocks.get(0);
+      int instructionCount = block.getInstructions().size();
+      assertEquals(firstBlockInstructions, instructionCount);
+
+      assertEquals(argumentInstructions, test.countArgumentInstructions());
+      assertEquals(firstBlockInstructions, block.getInstructions().size());
+      assertTrue(!block.getInstructions().get(i).isArgument());
+
+      InstructionListIterator iterator = test.listIteratorAt(block, i);
+      BasicBlock newBlock = iterator.split(code);
+      assertTrue(code.isConsistentSSA());
+
+      assertEquals(initialBlockCount + 1, code.blocks.size());
+      assertEquals(i + 1, code.blocks.get(0).getInstructions().size());
+      assertEquals(instructionCount - i, newBlock.getInstructions().size());
+      assertSame(newBlock, code.blocks.get(1));
+
+      // Run code and check result (code in the test object is updated).
+      String result = test.run();
+      assertEquals(hitCase ? "3" : "5", result);
+    }
+  }
+
+  @Test
+  public void withSwitch() {
+    runWithSwitchTest(false);
+    runWithSwitchTest(true);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/deterministic/DeterministicProcessingTest.java b/src/test/java/com/android/tools/r8/ir/deterministic/DeterministicProcessingTest.java
new file mode 100644
index 0000000..eaf9fe3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/deterministic/DeterministicProcessingTest.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.deterministic;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class DeterministicProcessingTest extends SmaliTestBase {
+
+  @Test
+  public void test()
+      throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+    final int ITERATIONS = 10;
+    R8Command.Builder builder =
+        R8Command.builder()
+            .addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class))
+            .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()));
+    List<byte[]> results = new ArrayList<>();
+    for (int i = 0; i < ITERATIONS; i++) {
+      AndroidApp result =
+          ToolHelper.runR8(
+              builder.build(), options -> options.testing.randomizeCallGraphLeaves = true);
+      List<byte[]> dex = result.writeToMemory();
+      assertEquals(1, dex.size());
+      results.add(dex.get(0));
+      System.out.println(dex.get(0).length);
+    }
+    for (int i = 0; i < ITERATIONS - 1; i++) {
+      assertArrayEquals(results.get(i), results.get(i + 1));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/deterministic/TestClass.java b/src/test/java/com/android/tools/r8/ir/deterministic/TestClass.java
new file mode 100644
index 0000000..5bbfa51
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/deterministic/TestClass.java
@@ -0,0 +1,70 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.deterministic;
+
+public class TestClass {
+
+  // This will be inlined, causing some of the calls below to go away providing more
+  // inlining opportunities.
+  public static boolean alwaysFalse() {
+    return false;
+  }
+
+  public static int a0() {
+    return b0();
+  }
+
+  public static int b0() {
+    if (alwaysFalse()) {
+      a0();
+      return 0;
+    }
+    return 1;
+  }
+
+  public static int a1() {
+    return b1();
+  }
+
+  public static int b1() {
+    if (alwaysFalse()) {
+      a1();
+      return 0;
+    }
+    return 1;
+  }
+
+  public static int a2() {
+    return b2();
+  }
+
+  public static int b2() {
+    return c2();
+  }
+
+  public static int c2() {
+    if (alwaysFalse()) {
+      a2();
+      return 0;
+    }
+    return 1;
+  }
+
+  public static int a3() {
+    return b3();
+  }
+
+  public static int b3() {
+    return c3();
+  }
+
+  public static int c3() {
+    if (alwaysFalse()) {
+      a3();
+      return 0;
+    }
+    return 1;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
new file mode 100644
index 0000000..7eebf9b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/IdenticalAfterRegisterAllocationTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.MoveType;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Value;
+import org.junit.Test;
+
+public class IdenticalAfterRegisterAllocationTest {
+
+  private static class MockRegisterAllocator implements RegisterAllocator {
+    @Override
+    public void allocateRegisters(boolean debug) {
+    }
+
+    @Override
+    public int registersUsed() {
+      return 0;
+    }
+
+    @Override
+    public int getRegisterForValue(Value value, int instructionNumber) {
+      // Use the value number as the register number.
+      return value.getNumber();
+    }
+
+    @Override
+    public boolean argumentValueUsesHighRegister(Value value, int instructionNumber) {
+      return false;
+    }
+  }
+
+  @Test
+  public void equalityOfConstantOperands() {
+    RegisterAllocator allocator = new MockRegisterAllocator();
+    Value value0 = new Value(0, -1, MoveType.SINGLE, null);
+    ConstNumber const0 = new ConstNumber(ConstType.INT, value0, 0);
+    Value value1 = new Value(1, -1, MoveType.SINGLE, null);
+    ConstNumber const1 = new ConstNumber(ConstType.INT, value1, 1);
+    Value value2 = new Value(2, -1, MoveType.SINGLE, null);
+    ConstNumber const2 = new ConstNumber(ConstType.INT, value2, 2);
+    Value value3 = new Value(2, -1, MoveType.SINGLE, null);
+    Add add0 = new Add(NumericType.INT, value3, value0, value1);
+    Add add1 = new Add(NumericType.INT, value3, value0, value2);
+    value0.computeNeedsRegister();
+    assertTrue(value0.needsRegister());
+    value1.computeNeedsRegister();
+    assertFalse(value1.needsRegister());
+    value2.computeNeedsRegister();
+    assertFalse(value2.needsRegister());
+    value3.computeNeedsRegister();
+    assertTrue(value3.needsRegister());
+    // value1 and value2 represent different constants and the additions are therefore
+    // not equivalent.
+    assertFalse(add0.identicalAfterRegisterAllocation(add1, allocator));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
new file mode 100644
index 0000000..26b6e18
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -0,0 +1,352 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.regalloc;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.MoveType;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import org.junit.Test;
+
+public class RegisterMoveSchedulerTest {
+
+  private static class CollectMovesIterator implements InstructionListIterator {
+
+    private LinkedList<Instruction> list = new LinkedList<>();
+    private ListIterator<Instruction> it = list.listIterator();
+
+    public Move get(int i) {
+      return list.get(i).asMove();
+    }
+
+    public int size() {
+      return list.size();
+    }
+
+    @Override
+    public void replaceCurrentInstruction(Instruction newInstruction) {
+      throw new Unimplemented();
+    }
+
+    @Override
+    public boolean hasNext() {
+      return it.hasNext();
+    }
+
+    @Override
+    public Instruction next() {
+      return it.next();
+    }
+
+    @Override
+    public boolean hasPrevious() {
+      return it.hasPrevious();
+    }
+
+    @Override
+    public Instruction previous() {
+      return it.previous();
+    }
+
+    @Override
+    public int nextIndex() {
+      return it.nextIndex();
+    }
+
+    @Override
+    public int previousIndex() {
+      return it.previousIndex();
+    }
+
+    @Override
+    public void remove() {
+      it.remove();
+    }
+
+    @Override
+    public void detach() {
+      remove();
+    }
+
+    @Override
+    public void set(Instruction instruction) {
+      it.set(instruction);
+    }
+
+    @Override
+    public void add(Instruction instruction) {
+      it.add(instruction);
+    }
+
+    @Override
+    public BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator) {
+      throw new Unimplemented();
+    }
+
+    @Override
+    public BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator) {
+      throw new Unimplemented();
+    }
+
+    @Override
+    public BasicBlock inlineInvoke(
+        IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator,
+        List<BasicBlock> blocksToRemove) {
+      throw new Unimplemented();
+    }
+  }
+
+  @Test
+  public void testSingleParallelMove() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(0, 1, MoveType.SINGLE));
+    scheduler.addMove(new RegisterMove(1, 0, MoveType.SINGLE));
+    scheduler.schedule();
+    assertEquals(3, moves.size());
+    Move tempMove = moves.get(0);
+    Move firstMove = moves.get(1);
+    Move secondMove = moves.get(2);
+    assertEquals(MoveType.SINGLE, tempMove.outType());
+    assertEquals(MoveType.SINGLE, firstMove.outType());
+    assertEquals(MoveType.SINGLE, secondMove.outType());
+    assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(
+        tempMove.src().asFixedRegisterValue().getRegister(),
+        firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(
+        firstMove.src().asFixedRegisterValue().getRegister(),
+        secondMove.dest().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testWideParallelMove() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(0, 2, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(2, 0, MoveType.WIDE));
+    scheduler.schedule();
+    assertEquals(3, moves.size());
+    Move tempMove = moves.get(0);
+    Move firstMove = moves.get(1);
+    Move secondMove = moves.get(2);
+    assertEquals(MoveType.WIDE, tempMove.outType());
+    assertEquals(MoveType.WIDE, firstMove.outType());
+    assertEquals(MoveType.WIDE, secondMove.outType());
+    assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(
+        tempMove.src().asFixedRegisterValue().getRegister(),
+        firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(
+        firstMove.src().asFixedRegisterValue().getRegister(),
+        secondMove.dest().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testMixedParralelMove() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(1, 0, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(0, 1, MoveType.SINGLE));
+    scheduler.schedule();
+    assertEquals(3, moves.size());
+    Move tempMove = moves.get(0).asMove();
+    Move firstMove = moves.get(1).asMove();
+    Move secondMove = moves.get(2).asMove();
+    assertEquals(MoveType.WIDE, tempMove.outType());
+    assertEquals(MoveType.SINGLE, firstMove.outType());
+    assertEquals(MoveType.WIDE, secondMove.outType());
+    assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(
+        tempMove.src().asFixedRegisterValue().getRegister(),
+        firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(
+        firstMove.src().asFixedRegisterValue().getRegister(),
+        secondMove.dest().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testMixedParralelMove2() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(0, 1, MoveType.SINGLE));
+    scheduler.addMove(new RegisterMove(1, 0, MoveType.WIDE));
+    scheduler.schedule();
+    assertEquals(3, moves.size());
+    Move tempMove = moves.get(0).asMove();
+    Move firstMove = moves.get(1).asMove();
+    Move secondMove = moves.get(2).asMove();
+    assertEquals(MoveType.WIDE, tempMove.outType());
+    assertEquals(MoveType.SINGLE, firstMove.outType());
+    assertEquals(MoveType.WIDE, secondMove.outType());
+    assertEquals(temp, tempMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(
+        tempMove.src().asFixedRegisterValue().getRegister(),
+        firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(temp, secondMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(
+        firstMove.src().asFixedRegisterValue().getRegister(),
+        secondMove.dest().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testSlideWideMoves() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(0, 1, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(2, 3, MoveType.WIDE));
+    scheduler.schedule();
+    assertEquals(2, moves.size());
+    Move firstMove = moves.get(0).asMove();
+    Move secondMove = moves.get(1).asMove();
+    assertEquals(MoveType.WIDE, firstMove.outType());
+    assertEquals(MoveType.WIDE, secondMove.outType());
+    assertEquals(0, firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(1, firstMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(2, secondMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(3, secondMove.src().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testSlideWideMoves2() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(2, 1, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(0, 3, MoveType.WIDE));
+    scheduler.schedule();
+    assertEquals(3, moves.size());
+    Move firstMove = moves.get(0).asMove();
+    Move secondMove = moves.get(1).asMove();
+    Move thirdMove = moves.get(2).asMove();
+    assertEquals(MoveType.WIDE, firstMove.outType());
+    assertEquals(MoveType.WIDE, secondMove.outType());
+    assertEquals(MoveType.WIDE, thirdMove.outType());
+    assertEquals(42, firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(1, firstMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(0, secondMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(3, secondMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(2, thirdMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(42, thirdMove.src().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testWideBlockedByTwoSingle() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(2, 0, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(0, 2, MoveType.SINGLE));
+    scheduler.addMove(new RegisterMove(1, 3, MoveType.SINGLE));
+    scheduler.schedule();
+    assertEquals(4, moves.size());
+    Move firstMove = moves.get(0).asMove();
+    Move secondMove = moves.get(1).asMove();
+    Move thirdMove = moves.get(2).asMove();
+    Move fourthMove = moves.get(3).asMove();
+    assertEquals(MoveType.WIDE, firstMove.outType());
+    assertEquals(MoveType.SINGLE, secondMove.outType());
+    assertEquals(MoveType.SINGLE, thirdMove.outType());
+    assertEquals(MoveType.WIDE, fourthMove.outType());
+    assertEquals(temp, firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(0, secondMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(1, thirdMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(2, fourthMove.dest().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void testSingleBlockedBySecondPartOfWide() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(0, 2, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(3, 0, MoveType.SINGLE));
+    scheduler.schedule();
+    assertEquals(3, moves.size());
+    Move firstMove = moves.get(0).asMove();
+    Move secondMove = moves.get(1).asMove();
+    Move thirdMove = moves.get(2).asMove();
+    assertEquals(MoveType.WIDE, firstMove.outType());
+    assertEquals(MoveType.SINGLE, secondMove.outType());
+    assertEquals(MoveType.WIDE, thirdMove.outType());
+    assertEquals(temp, firstMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(2, firstMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(3, secondMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(0, secondMove.src().asFixedRegisterValue().getRegister());
+    assertEquals(0, thirdMove.dest().asFixedRegisterValue().getRegister());
+    assertEquals(temp, thirdMove.src().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void multipleWideMoves() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(14, 11, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(16, 13, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(10, 17, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(12, 19, MoveType.WIDE));
+    scheduler.schedule();
+    // In order to resolve these moves, we need to use two temporary register pairs.
+    assertEquals(6, moves.size());
+    assertEquals(42, moves.get(0).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(11, moves.get(0).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(44, moves.get(1).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(13, moves.get(1).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(12, moves.get(2).asMove().dest().asFixedRegisterValue().getRegister());
+  }
+
+  @Test
+  public void multipleLiveTempRegisters() {
+    CollectMovesIterator moves = new CollectMovesIterator();
+    int temp = 42;
+    RegisterMoveScheduler scheduler = new RegisterMoveScheduler(moves, temp);
+    scheduler.addMove(new RegisterMove(26, 22, MoveType.SINGLE));
+    scheduler.addMove(new RegisterMove(29, 24, MoveType.WIDE));
+    scheduler.addMove(new RegisterMove(28, 26, MoveType.OBJECT));
+    scheduler.addMove(new RegisterMove(23, 28, MoveType.WIDE));
+    scheduler.schedule();
+    // For this example we need recursive unblocking.
+    assertEquals(6, moves.size());
+    assertEquals(42, moves.get(0).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(26, moves.get(0).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(26, moves.get(1).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(22, moves.get(1).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(43, moves.get(2).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(28, moves.get(2).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(28, moves.get(3).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(42, moves.get(3).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(29, moves.get(4).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(24, moves.get(4).asMove().src().asFixedRegisterValue().getRegister());
+    assertEquals(23, moves.get(5).asMove().dest().asFixedRegisterValue().getRegister());
+    assertEquals(43, moves.get(5).asMove().src().asFixedRegisterValue().getRegister());
+  }
+
+  // Debugging aid.
+  private void printMoves(List<Instruction> moves) {
+    System.out.println("Generated moves:");
+    System.out.println("----------------");
+    for (Instruction move : moves) {
+      System.out.println(move.asMove().dest().asFixedRegisterValue().getRegister() + " <- " +
+          move.asMove().src().asFixedRegisterValue().getRegister() + " (" + move.outType() + ")");
+    }
+    System.out.println("----------------");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar
new file mode 100644
index 0000000..2990356
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
new file mode 100644
index 0000000..96df2b8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSetRegressionTest.java
@@ -0,0 +1,77 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jar.UnicodeSetRegression;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArtErrorParser;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
+import com.android.tools.r8.utils.DexInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class UnicodeSetRegressionTest {
+
+  private static final String JAR_FILE =
+      "src/test/java/com/android/tools/r8/jar/UnicodeSetRegression/UnicodeSet.jar";
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  private AndroidApp dexFromDX() throws IOException {
+    return ToolHelper.runDexer(JAR_FILE, temp.newFolder("dx-dex").getPath());
+  }
+
+  @Test
+  public void testUnicodeSetFromDex()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    Path combinedInput = temp.getRoot().toPath().resolve("all.zip");
+    Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+    ToolHelper.runR8(dexFromDX(), combinedInput);
+    ToolHelper.runDex2Oat(combinedInput, oatFile);
+  }
+
+  @Test
+  public void testUnicodeSetFromJar()
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+    Path combinedInput = temp.getRoot().toPath().resolve("all.zip");
+    Path oatFile = temp.getRoot().toPath().resolve("all.oat");
+    AndroidApp result = ToolHelper.runR8(JAR_FILE, combinedInput.toString());
+    try {
+      ToolHelper.runDex2Oat(combinedInput, oatFile);
+    } catch (AssertionError e) {
+      AndroidApp fromDexApp = ToolHelper.runR8(dexFromDX());
+      DexInspector fromDex = new DexInspector(fromDexApp);
+      DexInspector fromJar = new DexInspector(result);
+      List<ArtErrorInfo> errors;
+      try {
+        errors = ArtErrorParser.parse(e.getMessage());
+      } catch (ArtErrorParserException parserException) {
+        System.err.println(parserException.toString());
+        throw e;
+      }
+      if (errors.isEmpty()) {
+        throw e;
+      }
+      for (ArtErrorInfo error : errors.subList(0, errors.size() - 1)) {
+        System.err.println(new ComparisonFailure(error.getMessage(),
+            "REFERENCE\n" + error.dump(fromDex, false) + "\nEND REFERENCE",
+            "PROCESSED\n" + error.dump(fromJar, true) + "\nEND PROCESSED").toString());
+      }
+      ArtErrorInfo error = errors.get(errors.size() - 1);
+      throw new ComparisonFailure(error.getMessage(),
+          "REFERENCE\n" + error.dump(fromDex, false) + "\nEND REFERENCE",
+          "PROCESSED\n" + error.dump(fromJar, true) + "\nEND PROCESSED");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
new file mode 100644
index 0000000..72bbaed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/DebugLocalTests.java
@@ -0,0 +1,339 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.debuginfo.DebugInfoInspector;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class DebugLocalTests extends JasminTestBase {
+
+  @Test
+  public void testSwap() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+    MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of("Ljava/lang/String;"), "V",
+        // The first three vars are out-of-order to verify that the order is not relied on.
+        ".var 5 is t I from L4 to L6",
+        ".var 1 is bar Ljava/lang/String; from L0 to L9",
+        ".var 0 is this LTest; from L0 to L9",
+        ".var 2 is x I from L1 to L9",
+        ".var 3 is y I from L2 to L9",
+        ".var 4 is z I from L3 to L9",
+        ".var 5 is foobar Ljava/lang/String; from L7 to L9",
+        ".limit locals 6",
+        ".limit stack 2",
+        "L0:",
+        ".line 23",
+        " iconst_1",
+        " istore 2",
+        "L1:",
+        ".line 24",
+        " iconst_2",
+        " istore 3",
+        "L2:",
+        ".line 25",
+        " iconst_3",
+        " istore 4",
+        "L3:",
+        " .line 27",
+        " iload 3",
+        " istore 5",
+        "L4:",
+        " .line 28",
+        " iload 2",
+        " istore 3",
+        "L5:",
+        " .line 29",
+        " iload 5",
+        " istore 2",
+        "L6:",
+        " .line 32",
+        " new java/lang/StringBuilder",
+        " dup",
+        " invokespecial java/lang/StringBuilder/<init>()V",
+        " ldc \"And the value of y is: \"",
+        " invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        " iload 2",
+        " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+        " iload 3",
+        " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+        " iload 4",
+        " invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;",
+        " invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;",
+        " astore 5",
+        "L7:",
+        " .line 34",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  aload 5",
+        "  invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+        "L8:",
+        " .line 35",
+        " return",
+        "L9:");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 1",
+        "  new Test",
+        "  dup",
+        "  invokespecial Test/<init>()V",
+        "  ldc \"Fsg\"",
+        "  invokevirtual Test/foo(Ljava/lang/String;)V",
+        "  return");
+
+    String expected = "And the value of y is: 213";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+
+    AndroidApp jasminApp = builder.build();
+    AndroidApp d8App = ToolHelper.runD8(jasminApp);
+
+    DexInspector inspector = new DexInspector(d8App);
+    ClassSubject classSubject = inspector.clazz("Test");
+    MethodSubject methodSubject = classSubject.method(foo);
+    DexCode code = methodSubject.getMethod().getCode().asDexCode();
+    DexDebugInfo info = code.getDebugInfo();
+    assertEquals(23, info.startLine);
+    assertEquals(1, info.parameters.length);
+    assertEquals("bar", info.parameters[0].toString());
+
+    // TODO(zerny): Verify the debug computed locals information.
+
+    String artResult = runOnArt(d8App, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+  @Test
+  public void testNoLocalInfoOnStack() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+    MethodSignature foo = clazz.addVirtualMethod("foo", ImmutableList.of(), "I",
+        ".var 0 is this LTest; from Init to End",
+        ".var 1 is x I from XStart to XEnd",
+        ".limit locals 2",
+        ".limit stack 1",
+        "Init:",
+        ".line 1",
+        "  ldc 0",
+        "  istore 1",
+        "XStart:",
+        ".line 2",
+        "  ldc 42",
+        "  istore 1",
+        "  iload 1",
+        "XEnd:",
+        ".line 3",
+        "  ireturn",
+        "End:");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 1",
+        "  new Test",
+        "  dup",
+        "  invokespecial Test/<init>()V",
+        "  invokevirtual Test/foo()I",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  swap",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "42";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+
+    AndroidApp jasminApp = builder.build();
+    AndroidApp d8App = ToolHelper.runD8(jasminApp);
+
+    DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+    info.checkStartLine(1);
+    info.checkLineHasExactLocals(1, "this", "Test");
+    info.checkLineHasExactLocals(2, "this", "Test", "x", "int");
+    info.checkLineHasExactLocals(3, "this", "Test");
+
+    String artResult = runOnArt(d8App, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+  // Check that we properly handle switching a local slot from one variable to another.
+  @Test
+  public void checkLocalChange() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+        ".limit stack 2",
+        ".limit locals 2",
+        ".var 0 is param I from MethodStart to MethodEnd",
+        ".var 1 is x I from LabelXStart to LabelXEnd",
+        ".var 1 is y I from LabelYStart to LabelYEnd",
+
+        "MethodStart:",
+        ".line 1",
+
+        "LabelXStart:",
+        "  ldc 0",
+        "  istore 1",
+        ".line 2",
+        "  invokestatic Test/ensureLine()V",
+        "LabelXEnd:",
+
+        "  iload 0",
+        "  lookupswitch",
+        "  1: Case1",
+        "  default: CaseDefault",
+
+        "Case1:",
+        "  ldc 42",
+        "  istore 1",
+        "LabelYStart:",
+        ".line 3",
+        "  invokestatic Test/ensureLine()V",
+        "  goto AfterSwitch",
+
+        "CaseDefault:",
+        "  ldc -42",
+        "  istore 1",
+        ".line 4",
+        "  invokestatic Test/ensureLine()V",
+
+        "AfterSwitch:",
+        ".line 5",
+        "  iload 1",
+        "  ireturn",
+        "LabelYEnd:",
+
+        "MethodEnd:"
+    );
+
+    clazz.addStaticMethod("ensureLine", ImmutableList.of(), "V",
+        ".limit stack 0",
+        ".limit locals 0",
+        "  return");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  ldc 0",
+        "  invokestatic Test/foo(I)I",
+        "  ldc 1",
+        "  invokestatic Test/foo(I)I",
+        "  pop",
+        "  return");
+
+    String expected = "";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+
+    AndroidApp jasminApp = builder.build();
+    AndroidApp d8App = ToolHelper.runD8(jasminApp);
+    String artResult = runOnArt(d8App, clazz.name);
+    assertEquals(expected, artResult);
+
+    DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+    info.checkStartLine(1);
+    info.checkLineHasExactLocals(1, "param", "int");
+    info.checkLineHasExactLocals(2, "param", "int", "x", "int");
+    info.checkLineHasExactLocals(3, "param", "int", "y", "int");
+    info.checkLineHasExactLocals(4, "param", "int", "y", "int");
+    info.checkLineHasExactLocals(5, "param", "int", "y", "int");
+  }
+
+  @Test
+  public void testLocalManyRanges() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+        ".limit stack 2",
+        ".limit locals 2",
+        ".var 0 is param I from Init to End",
+        ".var 1 is x I from LabelStart1 to LabelEnd1",
+        ".var 1 is x I from LabelStart2 to LabelEnd2",
+        ".var 1 is x I from LabelStart3 to LabelEnd3",
+        ".var 1 is x I from LabelStart4 to LabelEnd4",
+        "Init:",
+        ".line 1",
+        "  iload 0",
+        "  istore 1",
+
+        "LabelStart1:",
+        ".line 2",
+        "  invokestatic Test/ensureLine()V",
+        "LabelEnd1:",
+        ".line 3",
+        "  invokestatic Test/ensureLine()V",
+
+        "LabelStart2:",
+        ".line 4",
+        "  invokestatic Test/ensureLine()V",
+        "LabelEnd2:",
+        ".line 5",
+        "  invokestatic Test/ensureLine()V",
+
+        "LabelStart3:",
+        ".line 6",
+        "  invokestatic Test/ensureLine()V",
+        "LabelEnd3:",
+        ".line 7",
+        "  invokestatic Test/ensureLine()V",
+
+        "LabelStart4:",
+        ".line 8",
+        "  invokestatic Test/ensureLine()V",
+        "LabelEnd4:",
+        ".line 9",
+        "  invokestatic Test/ensureLine()V",
+        "  iload 1",
+        "  ireturn",
+        "End:"
+    );
+
+    clazz.addStaticMethod("ensureLine", ImmutableList.of(), "V",
+        ".limit stack 0",
+        ".limit locals 0",
+        "  return");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 42",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "42";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+
+    AndroidApp jasminApp = builder.build();
+    AndroidApp d8App = ToolHelper.runD8(jasminApp);
+    String artResult = runOnArt(d8App, clazz.name);
+    assertEquals(expected, artResult);
+
+    DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+    info.checkStartLine(1);
+    info.checkLineHasExactLocals(1, "param", "int");
+    info.checkLineHasExactLocals(2, "param", "int", "x", "int");
+    info.checkLineHasExactLocals(3, "param", "int");
+    info.checkLineHasExactLocals(4, "param", "int", "x", "int");
+    info.checkLineHasExactLocals(5, "param", "int");
+    info.checkLineHasExactLocals(6, "param", "int", "x", "int");
+    info.checkLineHasExactLocals(7, "param", "int");
+    info.checkLineHasExactLocals(8, "param", "int", "x", "int");
+    info.checkLineHasExactLocals(9, "param", "int");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
new file mode 100644
index 0000000..984e40e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class InvalidDebugInfoTests extends JasminTestBase {
+
+  // This is a regression test for invalid live-ranges of locals generated by some old Java
+  // compilers. The issue is that a local slot may have been initialized outside the live-scope of
+  // the variable and then the subsequent live-scope of the variable extends beyond its actual
+  // liveness. In the example below the variable 'y' is initialized outside its range (it is thus
+  // associated with the local 'x' (the SSA value is unaffected by the istore). Finally the 'return'
+  // forces a read of all supposedly live variables before exiting. Here the attempt to read 'y'
+  // will actually be a read of 'x'.
+  @Test
+  public void testInvalidInfoThrow() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I"), "V",
+        ".limit stack 3",
+        ".limit locals 4",
+        ".var 0 is x I from LabelInit to LabelExit",
+        ".var 1 is y I from LabelLocalStart to LabelExit",
+        ".var 2 is e Ljava/lang/Exception; from LabelCatchStart to LabelCatchEnd",
+        // var 3 is the jsr address
+        "LabelInit:",
+        "LabelTryStart:",
+        "  ldc 84",
+        "  iload 0",
+        "  dup",
+        "  istore 1", // init local[1] to value of local[0] (eg, 'x' since 'y' is not live yet).
+        "  idiv",
+        "  istore 0",
+        "LabelLocalStart:",
+        "  jsr LabelPrint",
+        "  goto LabelExit",
+        "LabelTryEnd:",
+        "LabelCatchStart:",
+        "  astore 2",
+        "  jsr LabelPrint",
+        "  return", // y is not actually live here.
+        "LabelCatchEnd:",
+        "LabelPrint:",
+        "  astore 3",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iload 0",
+        "  invokevirtual java/io/PrintStream/println(I)V",
+        "  ret 3",
+        "LabelExit:",
+        "  return",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatchStart"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "  ldc 2",
+        "  invokestatic Test/foo(I)V",
+        "  ldc 0",
+        "  invokestatic Test/foo(I)V",
+        "  return");
+
+    String expected = "42\n0\n";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArtD8(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+  // Regression test to check that we properly add UninitializedLocal SSA values for methods that
+  // have arguments without local info. To witness this bug, we also need "invalid" debug info, eg,
+  // in this test the scope of "y" (local 2) spans the exceptional edge in which it is not live.
+  @Test
+  public void testInvalidInfoBug37722432() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I","I"), "V",
+        ".limit stack 2",
+        ".limit locals 3",
+        ".var 0 is x I from LabelInit to LabelExit",
+        // Synthesized arg (no local info)
+        ".var 2 is y I from LabelLocalStart to LabelExit",
+        ".catch java/lang/Exception from LabelInit to LabelCatch using LabelCatch",
+        "LabelInit:", // Start of try block targets catch with a state excluding 'y'.
+        "  ldc 84",
+        "  iload 0",
+        "  idiv",
+        "  istore 2",
+        "LabelLocalStart:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iload 2",
+        "  invokevirtual java/io/PrintStream/println(I)V",
+        "  return",
+        "LabelCatch:", // Catch target appears to include 'y' but actually does not.
+        "  pop",
+        "  return",
+        "LabelExit:"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  ldc 2",
+        "  ldc 2",
+        "  invokestatic Test/foo(II)V",
+        "  ldc 0",
+        "  ldc 0",
+        "  invokestatic Test/foo(II)V",
+        "  return");
+
+    String expected = "42\n";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArtD8(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvokeSpecialTests.java b/src/test/java/com/android/tools/r8/jasmin/InvokeSpecialTests.java
new file mode 100644
index 0000000..197b4d1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/InvokeSpecialTests.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class InvokeSpecialTests extends JasminTestBase {
+
+  @Test
+  public void testPrivateInvokeSpecial() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addPrivateVirtualMethod("foo", ImmutableList.of(), "I",
+        ".limit stack 1",
+        ".limit locals 1",
+        "  ldc 42",
+        "  ireturn");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  new Test",
+        "  dup",
+        "  invokespecial Test/<init>()V",
+        "  invokespecial Test/foo()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "42";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void testPublicInvokeSpecial() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addVirtualMethod("foo", ImmutableList.of(), "I",
+        ".limit stack 1",
+        ".limit locals 1",
+        "  ldc 42",
+        "  ireturn");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  new Test",
+        "  dup",
+        "  invokespecial Test/<init>()V",
+        "  invokespecial Test/foo()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "42";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+
+    // TODO(zerny): Should we fail early on the above code? Art fails with a verification error
+    // because Test.foo is expected to be in the direct method table.
+    thrown.expect(AssertionError.class);
+    String artResult = runOnArt(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
new file mode 100644
index 0000000..e7511e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -0,0 +1,150 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import jasmin.ClassFile;
+import java.io.ByteArrayOutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JasminBuilder {
+
+  public static class ClassBuilder {
+    public final String name;
+    private final List<String> methods = new ArrayList<>();
+    private boolean makeInit = false;
+
+    public ClassBuilder(String name) {
+      this.name = name;
+    }
+
+    public MethodSignature addVirtualMethod(
+        String name,
+        List<String> argumentTypes,
+        String returnType,
+        String... lines) {
+      makeInit = true;
+      return addMethod("public", name, argumentTypes, returnType, lines);
+    }
+
+    public MethodSignature addPrivateVirtualMethod(
+        String name,
+        List<String> argumentTypes,
+        String returnType,
+        String... lines) {
+      makeInit = true;
+      return addMethod("private", name, argumentTypes, returnType, lines);
+    }
+
+    public MethodSignature addStaticMethod(
+        String name,
+        List<String> argumentTypes,
+        String returnType,
+        String... lines) {
+      return addMethod("public static", name, argumentTypes, returnType, lines);
+    }
+
+    public MethodSignature addMainMethod(String... lines) {
+      return addStaticMethod("main", ImmutableList.of("[Ljava/lang/String;"), "V", lines);
+    }
+
+    private MethodSignature addMethod(
+        String access,
+        String name,
+        List<String> argumentTypes,
+        String returnType,
+        String... lines) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(".method ").append(access).append(" ").append(name)
+          .append(StringUtils.join(argumentTypes, "", BraceType.PARENS))
+          .append(returnType).append("\n");
+      for (String line : lines) {
+        builder.append(line).append("\n");
+      }
+      builder.append(".end method\n");
+      methods.add(builder.toString());
+
+      String returnJavaType = DescriptorUtils.descriptorToJavaType(returnType);
+      String[] argumentJavaTypes = new String[argumentTypes.size()];
+      for (int i = 0; i < argumentTypes.size(); i++) {
+        argumentJavaTypes[i] = DescriptorUtils.descriptorToJavaType(argumentTypes.get(i));
+      }
+      return new MethodSignature(name, returnJavaType, argumentJavaTypes);
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(".source ").append(name).append(".j\n");
+      builder.append(".class public ").append(name).append("\n");
+      builder.append(".super java/lang/Object\n");
+      if (makeInit) {
+        builder
+            .append(".method public <init>()V\n")
+            .append(".limit locals 1\n")
+            .append(".limit stack 1\n")
+            .append("  aload 0\n")
+            .append("  invokespecial java/lang/Object/<init>()V\n")
+            .append("  return\n")
+            .append(".end method\n");
+      }
+      for (String method : methods) {
+        builder.append(method).append("\n");
+      }
+      return builder.toString();
+    }
+  }
+
+  private final List<ClassBuilder> classes = new ArrayList<>();
+
+  public JasminBuilder() {}
+
+  public ClassBuilder addClass(String name) {
+    ClassBuilder builder = new ClassBuilder(name);
+    classes.add(builder);
+    return builder;
+  }
+
+  public ImmutableList<ClassBuilder> getClasses() {
+    return ImmutableList.copyOf(classes);
+  }
+
+  private static byte[] compile(ClassBuilder builder) throws Exception {
+    ClassFile file = new ClassFile();
+    file.readJasmin(new StringReader(builder.toString()), builder.name, false);
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    file.write(out);
+    return out.toByteArray();
+  }
+
+  public AndroidApp build() throws Exception {
+    AndroidApp.Builder builder = AndroidApp.builder();
+    for (ClassBuilder clazz : classes) {
+      builder.addClassProgramData(compile(clazz));
+    }
+    return builder.build();
+  }
+
+  public DexApplication read() throws Exception {
+    return read(new InternalOptions());
+  }
+
+  public DexApplication read(InternalOptions options) throws Exception {
+    DexItemFactory factory = new DexItemFactory();
+    Timing timing = new Timing("JasminTest");
+    return new ApplicationReader(build(), options, timing).read();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
new file mode 100644
index 0000000..7db5451
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import jasmin.ClassFile;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class JasminTestBase {
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  protected ProcessResult runOnJavaRaw(JasminBuilder builder, String main) throws Exception {
+    File out = temp.newFolder("classes");
+    for (ClassBuilder clazz : builder.getClasses()) {
+      ClassFile file = new ClassFile();
+      file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+      file.write(new FileOutputStream(out.toPath().resolve(clazz.name + ".class").toFile()));
+    }
+    return ToolHelper.runJava(ImmutableList.of(out.getPath()), main);
+  }
+
+  protected String runOnJava(JasminBuilder builder, String main) throws Exception {
+    ProcessResult result = runOnJavaRaw(builder, main);
+    if (result.exitCode != 0) {
+      System.out.println("Std out:");
+      System.out.println(result.stdout);
+      System.out.println("Std err:");
+      System.out.println(result.stderr);
+      assertEquals(0, result.exitCode);
+    }
+    return result.stdout;
+  }
+
+  protected String runOnArt(JasminBuilder builder, String main) throws Exception {
+    // TODO(zerny): Make the compiler depend on tool flag?
+    return runOnArtR8(builder, main, new InternalOptions());
+  }
+
+  protected AndroidApp compileWithD8(JasminBuilder builder) throws Exception {
+    return ToolHelper.runD8(builder.build());
+  }
+
+  protected String runOnArtD8(JasminBuilder builder, String main) throws Exception {
+    return runOnArt(compileWithD8(builder), main);
+  }
+
+  protected String runOnArtR8(JasminBuilder builder, String main, InternalOptions options)
+      throws Exception {
+    DexApplication app = builder.read();
+    app = process(app, options);
+    AppInfo info = new AppInfo(app);
+    AndroidApp outputApp =
+        R8.writeApplication(
+            Executors.newSingleThreadExecutor(),
+            app,
+            info,
+            NamingLens.getIdentityLens(),
+            null,
+            null,
+            options);
+    return runOnArt(outputApp, main);
+  }
+
+  private ProcessResult runDx(JasminBuilder builder, File classes, Path dex) throws Exception {
+    for (ClassBuilder clazz : builder.getClasses()) {
+      ClassFile file = new ClassFile();
+      file.readJasmin(new StringReader(clazz.toString()), clazz.name, true);
+      file.write(new FileOutputStream(classes.toPath().resolve(clazz.name + ".class").toFile()));
+    }
+    List<String> args = new ArrayList<>();
+    args.add("--output=" + dex.toString());
+    args.add(classes.toString());
+    System.out.println("running: dx " + StringUtils.join(args, " "));
+    return ToolHelper.runDX(args.toArray(new String[args.size()]));
+  }
+
+  protected ProcessResult runOnArtDxRaw(JasminBuilder builder) throws Exception {
+    return runDx(builder, temp.newFolder("classes_for_dx"),
+        temp.getRoot().toPath().resolve("classes.dex"));
+  }
+
+  protected String runOnArtDx(JasminBuilder builder, String main) throws Exception {
+    Path dex = temp.getRoot().toPath().resolve("classes.dex");
+    ProcessResult result = runDx(builder, temp.newFolder("classes_for_dx"), dex);
+    if (result.exitCode != 0) {
+      System.out.println("Std out:");
+      System.out.println(result.stdout);
+      System.out.println("Std err:");
+      System.out.println(result.stderr);
+      assertEquals(0, result.exitCode);
+    }
+    return ToolHelper.runArtNoVerificationErrors(dex.toString(), main);
+  }
+
+  protected String runOnArt(AndroidApp app, String main) throws IOException {
+    Path out = temp.getRoot().toPath().resolve("out.zip");
+    app.writeToZip(out);
+    return ToolHelper.runArtNoVerificationErrors(ImmutableList.of(out.toString()), main, null);
+  }
+
+  protected static DexApplication process(DexApplication app, InternalOptions options)
+      throws IOException, ProguardRuleParserException, ExecutionException {
+    return new R8(options).optimize(app, new AppInfoWithSubtyping(app));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineDebugInfoTests.java
new file mode 100644
index 0000000..d0d1e83
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineDebugInfoTests.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.debuginfo.DebugInfoInspector;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class JumpSubroutineDebugInfoTests extends JasminTestBase {
+
+  @Test
+  public void testJsrWithStraightlineAndDebugInfoCode() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    MethodSignature foo = clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+        ".limit stack 3",
+        ".limit locals 3",
+        ".var 0 is x I from LabelInit to LabelExit",
+        "LabelInit:",
+        ".line 1",
+        "  ldc 0",
+        "  ldc 1",
+        "  jsr LabelSub",
+        "  ldc 2",
+        "  jsr LabelSub",
+        "  ldc 3",
+        "  jsr LabelSub",
+        ".line 2",
+        "  ireturn",
+        "LabelSub:",
+        ".line 3",
+        "  astore 1",
+        "  iadd",
+        "  istore 0", // store and load in local 'x' to ensure we don't optimize out the subroutine.
+        ".line 4",
+        "  iload 0",
+        "  ret 1",
+        "LabelExit:");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 1",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "6";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    AndroidApp d8App = compileWithD8(builder);
+    String artResult = runOnArt(d8App, clazz.name);
+    assertEquals(expected, artResult);
+
+    DebugInfoInspector info = new DebugInfoInspector(d8App, clazz.name, foo);
+    // The first debuggable line will be line 3.
+    info.checkStartLine(3);
+    // Check the subroutine line is duplicated 3 times.
+    assertEquals(3, info.checkLineHasExactLocals(3, "x", "int"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
new file mode 100644
index 0000000..34ddd69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/JumpSubroutineTests.java
@@ -0,0 +1,1540 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.AndroidApp;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class JumpSubroutineTests extends JasminTestBase {
+
+  private void runTest(JasminBuilder builder, String main, String expected) throws Exception {
+    String javaResult = runOnJava(builder, main);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, main);
+    assertEquals(expected, artResult);
+    String dxArtResult = runOnArtDx(builder, main);
+    assertEquals(expected, dxArtResult);
+  }
+
+  private void expectDxFailure(JasminBuilder builder) throws Exception {
+    // This expects this dx failure:
+    // Uncaught translation error: com.android.dex.util.ExceptionWithContext: returning from
+    // invalid subroutine
+    // 1 error; aborting
+    ProcessResult result = runOnArtDxRaw(builder);
+    assertNotEquals(0, result.exitCode);
+    assertTrue(result.stderr.contains("Uncaught translation error"));
+    assertTrue(result.stderr.contains("invalid subroutine"));
+  }
+
+  @Test
+  /*
+   *  Compilation of the following code with JDK 1.3.0_05 (on Windows).
+   *
+   *  package test;
+   *
+   *  class Test {
+   *    public static void main(String[] args) {
+   *      try {
+   *        System.out.println(0);
+   *      } finally {
+   *        System.out.println(2);
+   *      }
+   *    }
+   *  }
+   */
+  public void testJsrJava130TryFinally() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 3",
+        "TryStart:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_0",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  jsr Finally",
+        "  goto Return",
+        "TryEnd:",
+        "Catch:",
+        "  astore_1",
+        "  jsr Finally",
+        "  aload_1",
+        "  athrow",
+        "Finally:",
+        "  astore_2",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_1",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  ret 2",
+        "Return:",
+        "  return",
+        ".catch all from TryStart to TryEnd using Catch");
+
+    runTest(builder, clazz.name, "01");
+  }
+
+  @Test
+  /*
+   *  Compilation of the following code with JDK 1.3.0_05 (on Windows).
+   *
+   *  package test;
+   *
+   *  class Test {
+   *    public static void main(String[] args) {
+   *      try {
+   *        System.out.println(0);
+   *        try {
+   *          System.out.println(1);
+   *        } finally {
+   *          System.out.println(2);
+   *        }
+   *      } finally {
+   *        System.out.println(3);
+   *      }
+   *    }
+   *  }
+   */
+  public void testJsrJava130TryFinallyNested() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 5",
+        "TryStart:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_0",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "TryStartInner:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_1",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  jsr FinallyInner",
+        "  goto DoneInner",
+        "TryEndInner:",
+        "CatchInner:",
+        "  astore_1",
+        "  jsr FinallyInner",
+        "  aload_1",
+        "  athrow",
+        "FinallyInner:",
+        "  astore_2",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_2",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  ret 2",
+        "DoneInner:",
+        "  jsr Finally",
+        "  goto Return",
+        "TryEnd:",
+        "Catch:",
+        "  astore_3",
+        "  jsr Finally",
+        "  aload_3",
+        "  athrow",
+        "Finally:",
+        "  astore 4",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iconst_3",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  ret 4",
+        "Return:",
+        "  return",
+        ".catch all from TryStartInner to TryEndInner using CatchInner",
+        ".catch all from TryStart to TryEnd using Catch");
+
+
+    runTest(builder, clazz.name, "0123");
+  }
+
+  @Test
+  public void testJsrWithStraightlineCode() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "I",
+        ".limit stack 3",
+        ".limit locals 3",
+        "  ldc 0",
+        "  ldc 1",
+        "  jsr LabelSub",
+        "  ldc 2",
+        "  jsr LabelSub",
+        "  ldc 3",
+        "  jsr LabelSub",
+        "  ireturn",
+        "LabelSub:",
+        "  astore 1",
+        "  iadd",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  invokestatic Test/foo()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, Integer.toString(3 * 4 / 2));
+  }
+
+  @Test
+  public void testJsrWithStraightlineCodeMultiple() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "I",
+        ".limit stack 3",
+        ".limit locals 3",
+        "  ldc 0",
+        "  ldc 1",
+        "  jsr LabelSub1",
+        "  ldc 2",
+        "  jsr LabelSub2",
+        "  ldc 3",
+        "  jsr LabelSub3",
+        "  ireturn",
+        "LabelSub1:",
+        "  astore 1",
+        "  iadd",
+        "  ret 1",
+        "LabelSub2:",
+        "  astore 1",
+        "  iadd",
+        "  ret 1",
+        "LabelSub3:",
+        "  astore 1",
+        "  iadd",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  invokestatic Test/foo()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, Integer.toString(3 * 4 / 2));
+  }
+
+  @Test
+  public void testJsrWithStraightlineCodeMultiple2() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "I",
+        ".limit stack 4",
+        ".limit locals 3",
+        "  ldc 0",
+        "  ldc 1",
+        "  jsr LabelSub",
+        "  ldc 2",
+        "  jsr LabelSub",
+        "  ldc 3",
+        "  jsr LabelSub",
+        "  ldc 4",
+        "  ldc 5",
+        "  jsr LabelSub2",
+        "  ldc 6",
+        "  ldc 7",
+        "  jsr LabelSub2",
+        "  ldc 8",
+        "  ldc 9",
+        "  jsr LabelSub2",
+        "  ireturn",
+        "LabelSub:",
+        "  astore 1",
+        "  iadd",
+        "  ret 1",
+        "LabelSub2:",
+        "  astore 1",
+        "  iadd",
+        "  iadd",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  invokestatic Test/foo()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, Integer.toString(9 * 10 / 2));
+  }
+
+  @Test
+  public void testJsrWithControlFlowCode() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 2",
+        ".limit locals 2",
+        "  ldc 0",
+        "  jsr LabelSub",
+        "  ldc 1",
+        "  jsr LabelSub",
+        "  return",
+        "LabelSub:",
+        "  astore 1",
+        "  ifeq LabelZero",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"Got non-zero\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "  goto LabelRet",
+        "LabelZero:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"Got zero\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "LabelRet:",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 0",
+        ".limit locals 1",
+        "  invokestatic Test/foo()V",
+        "  return");
+
+    runTest(builder, clazz.name, "Got zero\nGot non-zero\n");
+  }
+
+  @Test
+  public void testJsrWithNestedJsr() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 2",
+        ".limit locals 3",
+        "  ldc 0",
+        "  jsr LabelSub",  // index 1.
+        "  ldc 1",
+        "  jsr LabelSub",  // index 3.
+        "  return",
+        "LabelSub:",
+        "  astore 1",
+        "  ifeq LabelZero",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"Got non-zero, calling nested\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "  jsr LabelSub2",  // index 11.
+        "  goto LabelRet",
+        "LabelZero:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"Got zero\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "LabelRet:",
+        "  ret 1",
+        "LabelSub2:",
+        "  astore 2",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"In nested subroutine\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "  ret 2");
+
+    clazz.addMainMethod(
+        ".limit stack 0",
+        ".limit locals 1",
+        "  invokestatic Test/foo()V",
+        "  return");
+
+    runTest(builder, clazz.name, "Got zero\nGot non-zero, calling nested\nIn nested subroutine\n");
+  }
+
+  @Test
+  public void testJsrWithNestedJsrPopReturnAddress() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 1",
+        ".limit locals 1",
+        "  jsr LabelSub1",
+        "  return",
+        "LabelSub1:",
+        "  astore 0",
+        "  jsr LabelSub2",
+        "LabelSub2:",
+        "  pop",
+        "  ret 0");
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "  invokestatic Test/foo()V",
+        "  return");
+
+    String expected = "";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    assertEquals(expected, artResult);
+    // This fails with dx.
+    expectDxFailure(builder);
+  }
+
+  @Test
+  public void testJsrWithNestedPopReturnAddress2() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 1",
+        ".limit locals 1",
+        "  jsr LabelSub1",
+        "LabelSub1:",
+        "  pop",
+        "  jsr LabelSub2",
+        "  return",
+        "LabelSub2:",
+        "  astore 0",
+        "  ret 0");
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "  invokestatic Test/foo()V",
+        "  return");
+
+    runTest(builder, clazz.name, "");
+  }
+
+  @Test
+  public void testJsrJustThrows() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 2",
+        ".limit locals 1",
+        "  jsr Jsr",
+        // The return target is the same as the jsr target.
+        "Jsr:",
+        "  pop",  // Return address is not used.
+        "  new java/lang/Exception",
+        "  dup",
+        "  invokenonvirtual java/lang/Exception.<init>()V",
+        "  athrow");
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "TryStart:",
+        "  invokestatic Test/foo()V",
+        "  return",
+        "TryEnd:",
+        "Catch:",
+        "  pop",
+        "  return",
+        ".catch java/lang/Exception from TryStart to TryEnd using Catch");
+
+    runTest(builder, clazz.name, "");
+  }
+
+  @Test
+  public void testJsrJustThrows2() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 2",
+        ".limit locals 1",
+        "  jsr Jsr",
+        // The jsr does not return, so this is dead code.
+        "  pop",
+        "  pop",
+        "  pop",
+        "  pop",
+        "  pop",
+        "  pop",
+        "  return",
+        "Jsr:",
+        "  pop",  // Return address is not used.
+        "  new java/lang/Exception",
+        "  dup",
+        "  invokenonvirtual java/lang/Exception.<init>()V",
+        "  athrow");
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "TryStart:",
+        "  invokestatic Test/foo()V",
+        "  return",
+        "TryEnd:",
+        "Catch:",
+        "  pop",
+        "  return",
+        ".catch java/lang/Exception from TryStart to TryEnd using Catch");
+
+    runTest(builder, clazz.name, "");
+  }
+
+  @Test
+  public void testJsrWithException() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 3",
+        ".limit locals 2",
+        "  ldc 0",
+        "  jsr LabelSub",
+        "  ldc 1",
+        "  jsr LabelSub",
+        "  return",
+        "LabelSub:",
+        "  astore 1",
+        "  ldc 42",
+        "  swap",
+        "  ldc 42",
+        "  swap",
+        "LabelTryStart:",
+        "  idiv",
+        "  pop",
+        "LabelTryEnd:",
+        "  pop",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"Divided by non-zero\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "  goto LabelRet",
+        "LabelCatch:",
+        "  pop",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc \"Divided by zero\"",
+        "  invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V",
+        "  goto LabelRet",
+        "LabelRet:",
+        "  ret 1",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch");
+
+    clazz.addMainMethod(
+        ".limit stack 0",
+        ".limit locals 1",
+        "  invokestatic Test/foo()V",
+        "  return");
+
+    runTest(builder, clazz.name, "Divided by zero\nDivided by non-zero\n");
+  }
+
+  @Test
+  public void testJsrWithAddressManipulation() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of(), "V",
+        ".limit stack 4",
+        ".limit locals 4",
+        "  ldc 0",
+        "  jsr LabelSub",
+        "  ldc 1",
+        "  jsr LabelSub",
+        "  return",
+        "LabelSub:",
+        "  ldc \"junk\"",
+        "  swap",
+        "  dup",
+        // stack is now ..., arg, "junk", addr, addr
+        "  astore 3",
+        "  astore 2",
+        "  astore 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  swap",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  ret 2");
+
+    clazz.addMainMethod(
+        ".limit stack 0",
+        ".limit locals 1",
+        "  invokestatic Test/foo()V",
+        "  return");
+
+    runTest(builder, clazz.name, "01");
+  }
+
+  @Test
+  public void testJsrWithSharedExceptionHandler() throws Exception {
+    // Regression test for b/37659886
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I"), "V",
+        ".limit stack 4",
+        ".limit locals 2",
+        "LabelTryStart:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iload 0",
+        "  jsr LabelSub",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return",
+        "LabelSub:",
+        "  astore 1",
+        "  dup",
+        "  ldc 168",
+        "  swap",
+        "  idiv", // First throwing shares the handler with JSR.
+        "LabelTryEnd1:",
+        "  swap",
+        "  idiv", // Second throwing is in the outer handler, but still opened at the point of JSR.
+        "  ret 1",
+        "LabelTryEnd2:",
+        "LabelCatch1:",
+        "  return",
+        "LabelCatch2:",
+        "  return",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd1 using LabelCatch1",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd2 using LabelCatch2");
+
+    clazz.addMainMethod(
+        ".limit stack 1",
+        ".limit locals 1",
+        "  ldc 2",
+        "  invokestatic Test/foo(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, "42");
+  }
+
+  @Test
+  public void regressJsrHitParentCatchHandler() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("test", ImmutableList.of(), "I",
+        ".limit stack 2",
+        ".limit locals 2",
+        "  ldc 10",
+        "  istore 1",
+        "TryStart:",
+        "  iinc 1 -1",
+        "  iload 1",
+        "  ifeq LabelReturn",
+        "  jsr Jsr",
+        "  goto LabelReturn",
+        "Jsr:",
+        "  astore 0",
+        "  new java/lang/Exception",
+        "  dup",
+        "  invokenonvirtual java/lang/Exception.<init>()V",
+        "  athrow",
+        "  ret 0",
+        "LabelReturn:",
+        "  ldc 0",
+        "  ireturn",
+        "Catch:",
+        "  pop",
+        "  goto TryStart",
+        "TryEnd:",
+        ".catch java/lang/Exception from TryStart to TryEnd using Catch"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  invokestatic Test/test()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, "0");
+  }
+
+  private void generateRegressJsrHitParentCatchHandler2(
+      JasminBuilder.ClassBuilder clazz, String name, String exception, boolean decrementInOuter) {
+    String outer = decrementInOuter ? "  iinc 0 -1" : "";
+    String inner = decrementInOuter ? "" : "  iinc 0 -1";
+    clazz.addStaticMethod(name, ImmutableList.of(), "I",
+        ".limit stack 3",
+        ".limit locals 1",
+        "  ldc 10",
+        "  istore 0",
+        "TryStart:",
+        outer,
+        "  iload 0",
+        "  ifeq LabelReturn1",
+        "  jsr Jsr1",
+        "Jsr1:",
+        "  pop",  // Return value is not used.
+        "Jsr1Retry:",
+        inner,
+        "  iload 0",
+        "  ifeq LabelReturn0",
+        "  jsr Jsr2",
+        "  ldc 2",
+        "  ireturn",
+        "Jsr2:",
+        "  pop",  // Return value is not used.
+        "  new " + exception,
+        "  dup",
+        "  invokenonvirtual " + exception + ".<init>()V",
+        "  athrow",
+        "Jsr2Catch:",
+        "  pop",
+        "  goto Jsr1Retry",
+        "Jsr2End:",
+        "LabelReturn0:",
+        "  ldc 0",
+        "  ireturn",
+        "LabelReturn1:",
+        "  ldc 1",
+        "  ireturn",
+        "Catch:",
+        "  pop",
+        "  goto TryStart",
+        "TryEnd:",
+        ".catch java/lang/Exception from Jsr2 to Jsr2End using Jsr2Catch",
+        ".catch java/lang/Throwable from TryStart to TryEnd using Catch"
+    );
+  }
+
+  @Test
+  public void regressJsrHitParentCatchHandler2() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    generateRegressJsrHitParentCatchHandler2(clazz, "test1", "java/lang/Exception", false);
+    generateRegressJsrHitParentCatchHandler2(clazz, "test2", "java/lang/Throwable", true);
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  invokestatic Test/test1()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  invokestatic Test/test2()I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, "01");
+  }
+
+  @Test
+  // Reduction of b/38156139 causing the infinite loop.
+  //
+  // The original code is this method:
+  // https://github.com/cbeust/testng/blob/4a8459e36f2b0ed057ffa7e470f1057e8e5b0ff9/src/main/java/org/testng/internal/Invoker.java#L1066
+  // compiled with some ancient version of javac generating code with jsr for try/finally.
+  public void regress38156139() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+        ".limit stack 2",
+        ".limit locals 3",
+        "  ldc 10",
+        "  istore 2",
+        "LabelLoopStart:",
+        "  iinc 2 -1",
+        "  iload 2",
+        "  ifeq LabelReturn",
+        "LabelTryStart:",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  iload 2",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  jsr LabelFinally",
+        "  goto LabelLoopStart",
+        "LabelTryEnd:",
+        "LabelReturn:",
+        "  ldc 0",
+        "  ireturn",
+        "LabelCatch:",
+        "  pop",
+        "  jsr LabelFinally",
+        "  goto LabelLoopStart",
+        "LabelFinally:",
+        "  astore 1",
+        "  iload 0",
+        "  ifeq LabelZero",
+        "  goto LabelLoopStart",  // Jump to loop start without invoking ret.
+        "LabelZero:",
+        "  ret 1",  // Invoke ret, which will also continue at the loop start.
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 1",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    runTest(builder, clazz.name, "98765432109876543210");
+  }
+
+  @Test
+  public void regress37767254() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    // This is the code for the method org.apache.log4j.net.SocketAppender$Connector.run() from
+    // log4j version 1.2.
+    //
+    // https://github.com/apache/log4j/blob/v1_2-branch/src/main/java/org/apache/log4j/net/SocketAppender.java#L373
+    //
+    clazz.addVirtualMethod("run", ImmutableList.of(), "V",
+        ".limit stack 4",
+        ".limit locals 4",
+        ".var 0 is this Lorg/apache/log4j/net/SocketAppender$Connector; from L0 to L26",
+        ".var 1 is socket Ljava/net/Socket; from L5 to L13",
+        ".var 2 is e Ljava/io/IOException; from L22 to L1",
+        "L0:",
+        ".line 367",
+        "  goto L1",
+        "L2:",
+        ".line 368",
+        ".line 369",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  getfield org/apache/log4j/net/SocketAppender.reconnectionDelay I",
+        "  i2l",
+        "  invokestatic java/lang/Thread.sleep(J)V",
+        "L3:",
+        ".line 370",
+        "  new java/lang/StringBuffer",
+        "  dup",
+        "  ldc \"Attempting connection to \"",
+        "  invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+        "  invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
+        "  invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+        "  invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
+        "  invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+        "L4:",
+        ".line 371",
+        "  new java/net/Socket",
+        "  dup",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  getfield org/apache/log4j/net/SocketAppender.port I",
+        "  invokenonvirtual java/net/Socket.<init>(Ljava/net/InetAddress;I)V",
+        "  astore 1",
+        "L5:",
+        ".line 372",
+        "  aload 0",
+        "  astore 2",
+        "  aload 2",
+        "  monitorenter",
+        "L6:",
+        ".line 373",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  new java/io/ObjectOutputStream",
+        "  dup",
+        "  aload 1",
+        "  invokevirtual java/net/Socket.getOutputStream()Ljava/io/OutputStream;",
+        "  invokenonvirtual java/io/ObjectOutputStream.<init>(Ljava/io/OutputStream;)V",
+        "  putfield org/apache/log4j/net/SocketAppender.oos Ljava/io/ObjectOutputStream;",
+        "L7:",
+        ".line 374",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  aconst_null",
+        "  invokestatic org/apache/log4j/net/SocketAppender.access$1(Lorg/apache/log4j/net/SocketAppender;Lorg/apache/log4j/net/SocketAppender$Connector;)V",
+        "L8:",
+        ".line 375",
+        "  ldc \"Connection established. Exiting connector thread.\"",
+        "  invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+        "L9:",
+        ".line 376",
+        "  jsr L10",
+        "  goto L11",
+        "L12:",
+        ".line 372",
+        "  aload 2",
+        "  monitorexit",
+        "  athrow",
+        "L10:",
+        "  astore 3",
+        "  aload 2",
+        "  monitorexit",
+        "  ret 3",
+        "L13:",
+        ".line 378",
+        "  pop",
+        "L14:",
+        ".line 379",
+        "  ldc \"Connector interrupted. Leaving loop.\"",
+        "  invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+        "L15:",
+        ".line 380",
+        "  return",
+        "L16:",
+        ".line 381",
+        "  pop",
+        "L17:",
+        ".line 382",
+        "  new java/lang/StringBuffer",
+        "  dup",
+        "  ldc \"Remote host \"",
+        "  invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+        "  invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
+        "  invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+        "L18:",
+        ".line 383",
+        "  ldc \" refused connection.\"",
+        "  invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+        "  invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
+        "L19:",
+        ".line 382",
+        "  invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+        "L20:",
+        ".line 368",
+        "  goto L1",
+        "L21:",
+        ".line 384",
+        "  astore 2",
+        "L22:",
+        ".line 385",
+        "  new java/lang/StringBuffer",
+        "  dup",
+        "  ldc \"Could not connect to \"",
+        "  invokenonvirtual java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.this$0 Lorg/apache/log4j/net/SocketAppender;",
+        "  getfield org/apache/log4j/net/SocketAppender.address Ljava/net/InetAddress;",
+        "  invokevirtual java/net/InetAddress.getHostName()Ljava/lang/String;",
+        "  invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+        "L23:",
+        ".line 386",
+        "  ldc \". Exception is \"",
+        "  invokevirtual java/lang/StringBuffer.append(Ljava/lang/String;)Ljava/lang/StringBuffer;",
+        "  aload 2",
+        "  invokevirtual java/lang/StringBuffer.append(Ljava/lang/Object;)Ljava/lang/StringBuffer;",
+        "  invokevirtual java/lang/StringBuffer.toString()Ljava/lang/String;",
+        "L24:",
+        ".line 385",
+        "  invokestatic org/apache/log4j/helpers/LogLog.debug(Ljava/lang/String;)V",
+        "L25:",
+        ".line 368",
+        "  goto L1",
+        "L1:",
+        ".line 367",
+        "  aload 0",
+        "  getfield org/apache/log4j/net/SocketAppender$Connector.interrupted Z",
+        "  ifeq L2",
+        "L11:",
+        ".line 365",
+        "  return",
+        "L26:",
+        ".catch all from L6 to L12 using L12",
+        ".catch java/lang/InterruptedException from L2 to L13 using L13",
+        ".catch java/net/ConnectException from L2 to L13 using L16",
+        ".catch java/io/IOException from L2 to L13 using L21"
+    );
+
+    // Check that the code compiles without an infinite loop. It cannot run by itself.
+    AndroidApp app = compileWithD8(builder);
+    assertNotNull(app);
+  }
+
+  @Test
+  public void regress37888855Reduced() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    // This is the code for the method
+    //
+    // void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
+    //
+    // from struts2/lib/core-3.1.1.jar
+    //
+    clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"),
+        "V",
+        ".limit stack 3",
+        ".limit locals 15",
+        "TryStart1:",
+        "TryStart2:",
+        "  goto Finally2Start",
+        "TryEnd2:",
+        "Catch2:",
+        "  astore 6",
+        "  jsr Finally2Code",  // 6
+        "  aload 6",
+        "  athrow",
+        "Finally2Code:",  // 9
+        "  astore 5",
+        "  ret 5",
+        "Finally2Start:",
+        "  jsr Finally2Code",  // 13
+        "Finally2End:",
+        "  goto Finally1Start",
+        "TryEnd1:",
+        "Catch1:",
+        "  astore 8",
+        "  jsr Finally1Code",  // 19
+        "L19:",
+        "  aload 8",
+        "  athrow",
+        "Finally1Code:",
+        "  astore 7",
+        "Try3Start:",
+        "  goto Finally3Start",
+        "Try3End:",
+        "Catch3:",
+        "  astore 14",  // Catch
+        "  jsr Finally3Code",  // 30
+        "  aload 14",
+        "  athrow",
+        "Finally3Code:",  // Finally
+        "  astore 13",
+        "  ret 13",
+        "Finally3Start:",
+        "  jsr Finally3Code",  // 37
+        "Finally3End:",
+        "  ret 7",
+        "Finally1Start:",
+        "  jsr Finally1Code",  // 41
+        "Finally1End:",
+        "  return",
+        ".catch all from TryStart2 to TryEnd2 using Catch2",  // Block
+        ".catch all from Finally2Start to Finally2End using Catch2",  // Finally
+        ".catch all from TryStart1 to TryEnd1 using Catch1",  // Block
+        ".catch all from Finally1Start to Finally1End using Catch1",  // Finally
+        ".catch all from Try3Start to Try3End using Catch3",  // Block
+        ".catch all from Finally3Start to Finally3End using Catch3"  // Finally
+    );
+
+    // Check that the code compiles without an infinite loop. It cannot run by itself.
+    AndroidApp app = compileWithD8(builder);
+    assertNotNull(app);
+  }
+
+  @Test
+  public void regress37888855() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    // This is the code for the method
+    //
+    // void org.eclipse.jdt.internal.core.JavaModelOperation.run(org.eclipse.core.runtime.IProgressMonitor)
+    //
+    // from struts2/lib/core-3.1.1.jar
+    //
+    clazz.addVirtualMethod("run", ImmutableList.of("Lorg/eclipse/core/runtime/IProgressMonitor;"), "V",
+        ".limit stack 3",
+        ".limit locals 15",
+        ".var 0 is this Lorg/eclipse/jdt/internal/core/JavaModelOperation; from L0 to L50",
+        ".var 1 is monitor Lorg/eclipse/core/runtime/IProgressMonitor; from L0 to L50",
+        ".var 2 is manager Lorg/eclipse/jdt/internal/core/JavaModelManager; from L1 to L50",
+        ".var 3 is deltaProcessor Lorg/eclipse/jdt/internal/core/DeltaProcessor; from L2 to L50",
+        ".var 4 is previousDeltaCount I from L1 to L50",
+        ".var 9 is i I from L22 to L27",
+        ".var 10 is size I from L23 to L27",
+        ".var 9 is i I from L28 to L38",
+        ".var 10 is length I from L29 to L38",
+        ".var 11 is element Lorg/eclipse/jdt/core/IJavaElement; from L32 to L37",
+        ".var 12 is openable Lorg/eclipse/jdt/internal/core/Openable; from L33 to L37",
+        "L0:",
+        ".line 705",
+        "  invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
+        "  astore 2",
+        "L1:",
+        ".line 706",
+        "  aload 2",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
+        "  astore 3",
+        "L2:",
+        ".line 707",
+        "  aload 3",
+        "  getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+        "  invokevirtual java/util/ArrayList.size()I",
+        "  istore 4",
+        "L3:",
+        ".line 709",
+        "  aload 0",
+        "  aload 1",
+        "  putfield org/eclipse/jdt/internal/core/JavaModelOperation.progressMonitor Lorg/eclipse/core/runtime/IProgressMonitor;",
+        "L4:",
+        ".line 710",
+        "  aload 0",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.pushOperation(Lorg/eclipse/jdt/internal/core/JavaModelOperation;)V",
+        "L5:",
+        ".line 712",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.canModifyRoots()Z",
+        "  ifeq L6",
+        "L7:",
+        ".line 715",
+        "  invokestatic org/eclipse/jdt/internal/core/JavaModelManager.getJavaModelManager()Lorg/eclipse/jdt/internal/core/JavaModelManager;",
+        "  getfield org/eclipse/jdt/internal/core/JavaModelManager.deltaState Lorg/eclipse/jdt/internal/core/DeltaProcessingState;",
+        "  invokevirtual org/eclipse/jdt/internal/core/DeltaProcessingState.initializeRoots()V",
+        "L6:",
+        ".line 718",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.executeOperation()V",
+        "  goto L8",
+        "L9:",
+        ".line 719",
+        "  astore 6",
+        "  jsr L10",
+        "L11:",
+        ".line 723",
+        "  aload 6",
+        "  athrow",
+        "L10:",
+        ".line 719",
+        "  astore 5",
+        "L12:",
+        ".line 720",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.isTopLevelOperation()Z",
+        "  ifeq L13",
+        "L14:",
+        ".line 721",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.runPostActions()V",
+        "L13:",
+        ".line 723",
+        "  ret 5",
+        "L8:",
+        "  jsr L10",
+        "L15:",
+        "  goto L16",
+        "L17:",
+        ".line 724",
+        "  astore 8",
+        "  jsr L18",
+        "L19:",
+        ".line 764",
+        "  aload 8",
+        "  athrow",
+        "L18:",
+        ".line 724",
+        "  astore 7",
+        "L20:",
+        ".line 727",
+        "  aload 2",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelManager.getDeltaProcessor()Lorg/eclipse/jdt/internal/core/DeltaProcessor;",
+        "  astore 3",
+        "L21:",
+        ".line 730",
+        "  iload 4",
+        "  istore 9",
+        "L22:",
+        "  aload 3",
+        "  getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+        "  invokevirtual java/util/ArrayList.size()I",
+        "  istore 10",
+        "L23:",
+        "  goto L24",
+        "L25:",
+        ".line 731",
+        "  aload 3",
+        "  aload 3",
+        "  getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+        "  iload 9",
+        "  invokevirtual java/util/ArrayList.get(I)Ljava/lang/Object;",
+        "  checkcast org/eclipse/jdt/core/IJavaElementDelta",
+        "  invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.updateJavaModel(Lorg/eclipse/jdt/core/IJavaElementDelta;)V",
+        "L26:",
+        ".line 730",
+        "  iinc 9 1",
+        "L24:",
+        "  iload 9",
+        "  iload 10",
+        "  if_icmplt L25",
+        "L27:",
+        ".line 737",
+        "  iconst_0",
+        "  istore 9",
+        "L28:",
+        "  aload 0",
+        "  getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
+        "  arraylength",
+        "  istore 10",
+        "L29:",
+        "  goto L30",
+        "L31:",
+        ".line 738",
+        "  aload 0",
+        "  getfield org/eclipse/jdt/internal/core/JavaModelOperation.resultElements [Lorg/eclipse/jdt/core/IJavaElement;",
+        "  iload 9",
+        "  aaload",
+        "  astore 11",
+        "L32:",
+        ".line 739",
+        "  aload 11",
+        "  invokeinterface org/eclipse/jdt/core/IJavaElement.getOpenable()Lorg/eclipse/jdt/core/IOpenable; 0",
+        "  checkcast org/eclipse/jdt/internal/core/Openable",
+        "  astore 12",
+        "L33:",
+        ".line 740",
+        "  aload 12",
+        "  instanceof org/eclipse/jdt/internal/core/CompilationUnit",
+        "  ifeq L34",
+        "  aload 12",
+        "  checkcast org/eclipse/jdt/internal/core/CompilationUnit",
+        "  invokevirtual org/eclipse/jdt/internal/core/CompilationUnit.isWorkingCopy()Z",
+        "  ifne L35",
+        "L34:",
+        ".line 741",
+        "  aload 12",
+        "  invokevirtual org/eclipse/jdt/internal/core/Openable.getParent()Lorg/eclipse/jdt/core/IJavaElement;",
+        "  checkcast org/eclipse/jdt/internal/core/JavaElement",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaElement.close()V",
+        "L35:",
+        ".line 743",
+        "  aload 11",
+        "  invokeinterface org/eclipse/jdt/core/IJavaElement.getElementType()I 0",
+        "  tableswitch 3",
+        "    L36",
+        "    L36",
+        "    default: L37",
+        "L36:",
+        ".line 746",
+        "  aload 11",
+        "  invokeinterface org/eclipse/jdt/core/IJavaElement.getJavaProject()Lorg/eclipse/jdt/core/IJavaProject; 0",
+        "  checkcast org/eclipse/jdt/internal/core/JavaProject",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaProject.resetCaches()V",
+        "L37:",
+        ".line 737",
+        "  iinc 9 1",
+        "L30:",
+        "  iload 9",
+        "  iload 10",
+        "  if_icmplt L31",
+        "L38:",
+        ".line 755",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.isTopLevelOperation()Z",
+        "  ifeq L39",
+        "L40:",
+        ".line 756",
+        "  aload 3",
+        "  getfield org/eclipse/jdt/internal/core/DeltaProcessor.javaModelDeltas Ljava/util/ArrayList;",
+        "  invokevirtual java/util/ArrayList.size()I",
+        "  iload 4",
+        "  if_icmpgt L41",
+        "  aload 3",
+        "  getfield org/eclipse/jdt/internal/core/DeltaProcessor.reconcileDeltas Ljava/util/HashMap;",
+        "  invokevirtual java/util/HashMap.isEmpty()Z",
+        "  ifne L39",
+        "L41:",
+        ".line 757",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.hasModifiedResource()Z",
+        "  ifne L39",
+        "L42:",
+        ".line 758",
+        "  aload 3",
+        "  aconst_null",
+        "  iconst_0",
+        "  invokevirtual org/eclipse/jdt/internal/core/DeltaProcessor.fire(Lorg/eclipse/jdt/core/IJavaElementDelta;I)V",
+        "  goto L39",
+        "L43:",
+        ".line 761",
+        "  astore 14",
+        "  jsr L44",
+        "L45:",
+        ".line 763",
+        "  aload 14",
+        "  athrow",
+        "L44:",
+        ".line 761",
+        "  astore 13",
+        "L46:",
+        ".line 762",
+        "  aload 0",
+        "  invokevirtual org/eclipse/jdt/internal/core/JavaModelOperation.popOperation()Lorg/eclipse/jdt/internal/core/JavaModelOperation;",
+        "  pop",
+        "L47:",
+        ".line 763",
+        "  ret 13",
+        "L39:",
+        "  jsr L44",
+        "L48:",
+        ".line 764",
+        "  ret 7",
+        "L16:",
+        "  jsr L18",
+        "L49:",
+        ".line 765",
+        "  return",
+        "L50:",
+        ".catch all from L5 to L9 using L9",
+        ".catch all from L8 to L15 using L9",
+        ".catch all from L3 to L17 using L17",
+        ".catch all from L16 to L49 using L17",
+        ".catch all from L20 to L43 using L43",
+        ".catch all from L39 to L48 using L43"
+    );
+
+    // Check that the code compiles without an infinite loop. It cannot run by itself.
+    AndroidApp app = compileWithD8(builder);
+    assertNotNull(app);
+  }
+
+  // Some jsr tests that fails with org.objectweb.asm.commons.JSRInlinerAdapter.
+
+  @Test
+  // This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
+  public void testJsrWithNestedJsrRetBasedOnControlFlow() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+        ".limit stack 2",
+        ".limit locals 3",
+        "  jsr LabelSub1",
+        "  ldc 1",
+        "  ireturn",
+        "LabelSub1:",
+        "  astore 1",
+        "  jsr LabelSub2",
+        "  ldc 0",
+        "  ireturn",
+        "LabelSub2:",
+        "  astore 2",
+        "  iload 0",
+        "  ifeq LabelZero",
+        "  ret 2",
+        "LabelZero:",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 1",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "10";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    // The ASM jsr inliner does not get the control-flow dependent ret right in his case.
+    assertNotEquals(expected, artResult);
+    // This fails with dx.
+    expectDxFailure(builder);
+  }
+
+  @Test
+  // This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
+  public void testJsrWithNestedRetBasedOnControlFlow2() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+        ".limit stack 2",
+        ".limit locals 3",
+        "  jsr LabelSub1",
+        "  ldc 1",
+        "  ireturn",
+        "LabelSub1:",
+        "  jsr LabelSub2",
+        "  ldc 0",
+        "  ireturn",
+        "LabelSub2:",
+        "  astore 2",
+        "  astore 1",
+        "  iload 0",
+        "  ifeq LabelZero",
+        "  ret 2",
+        "LabelZero:",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 1",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "10";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    // The ASM jsr inliner does not get the control-flow dependent ret right in his case.
+    assertNotEquals(expected, artResult);
+    // This fails with dx.
+    expectDxFailure(builder);
+  }
+
+  @Test
+  // This test is based on the example on http://asm.ow2.org/doc/developer-guide.html.
+  public void testJsrWithNestedRetBasedOnControlFlow3() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+        ".limit stack 2",
+        ".limit locals 3",
+        "  jsr LabelSub1",
+        "  ldc 1",
+        "  ireturn",
+        "LabelSub1:",
+        "  jsr LabelSub2",
+        "  ldc 0",
+        "  ireturn",
+        "LabelSub2:",
+        "  swap",
+        "  astore 1",
+        "  astore 2",
+        "  iload 0",
+        "  ifeq LabelZero",
+        "  ret 2",
+        "LabelZero:",
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 1",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "10";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    // The ASM jsr inliner does not get the control-flow dependent ret right in his case.
+    assertNotEquals(expected, artResult);
+    // This fails with dx.
+    expectDxFailure(builder);
+  }
+
+  // Some jsr tests that fails bytecode verification on the Java VM.
+
+  @Test
+  public void testReuseAddr() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 2",
+        "  jsr LabelSub1",
+        "  return",
+        "LabelSub1:",
+        "  jsr LabelSub2",
+        "  dup",
+        "  astore 0",
+        "  ret 0",
+        "LabelSub2:",
+        "  dup",
+        "  astore 0",
+        "  ret 0");
+
+    // This does not run on the Java VM (verification error).
+    assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
+  }
+
+  @Test
+  public void testSwitchAddr() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addMainMethod(
+        ".limit stack 3",
+        ".limit locals 2",
+        "  jsr LabelSub1",
+        "  astore 0",
+        "  ret 0",
+        "LabelSub1:",
+        "  jsr LabelSub2",
+        "  return",
+        "LabelSub2:",
+        "  swap",
+        "  astore 0",
+        "  ret 0");
+
+    // This does not run on the Java VM (verification error).
+    assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
+  }
+
+  @Test
+  public void testJsrPreserveRetAddressOverJsr() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("Z"), "I",
+        ".limit stack 2",
+        ".limit locals 2",
+        "  jsr LabelSub1",
+        "  ldc 0",
+        "  ireturn",
+        "LabelSub1:",
+        "  iload 0",
+        "  ifeq StoreInFirst",
+        "  pop",
+        "  goto NextJsr",
+        "StoreInFirst:",
+        "  astore 1",
+        "NextJsr:",
+        "  jsr LabelSub2",
+        "  ldc 1",
+        "  ireturn",
+        "LabelSub2:",
+        "  iload 0",
+        "  ifeq DontStoreInSecond",  // Same as StoreInFirst
+        "  astore 1",
+        "  goto Ret",
+        "DontStoreInSecond:",
+        "  pop",
+        "Ret:",
+        // There will always be an address in local 1 at this point.
+        "  ret 1");
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 1",
+        "  invokestatic Test/foo(Z)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    // This does not run on the Java VM (verification error).
+    assertNotEquals(0, runOnJavaRaw(builder, clazz.name));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/TryCatchStateTests.java b/src/test/java/com/android/tools/r8/jasmin/TryCatchStateTests.java
new file mode 100644
index 0000000..92f99f8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/TryCatchStateTests.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class TryCatchStateTests extends JasminTestBase {
+
+  @Test
+  public void testTryCatchStackHeight() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+        ".limit stack 5",
+        ".limit locals 1",
+        "  ldc 42",
+        "  ldc 12",
+        "  iload 0", // {42, 12, i}
+        "LabelTryStart:",
+        "  idiv", // {42, 12/i}
+        "LabelTryEnd:",
+        "  swap",
+        "  pop", // {12/i}
+        "  goto LabelRet",
+        "LabelCatch:", // Entry stack is {java/lang/Throwable}
+        "  pop",
+        "  ldc 0", // {0}
+        "  goto LabelRet",
+        "LabelRet:",
+        "  ireturn",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/println(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 2",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "0\n6";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+  @Test
+  public void testTryCatchLocals() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+        ".limit stack 5",
+        ".limit locals 2",
+        "  ldc 42",
+        "  ldc 12",
+        "  iload 0", // {42, 12, i}
+        "  ldc 0",
+        "  istore 1", // Must initialize local before entry to try-catch block.
+        "LabelTryStart:",
+        "  swap",
+        "  istore 1",
+        "  idiv", // {42/i}
+        "LabelTryEnd:",
+        "  goto LabelRet",
+        "LabelCatch:", // Entry stack is {java/lang/Throwable}
+        "  pop",
+        "  iload 1", // {12}
+        "  goto LabelRet",
+        "LabelRet:",
+        "  ireturn",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/println(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 2",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "12\n21";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+
+  @Test
+  public void testTryCatchOnUnreachableLabel() throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("foo", ImmutableList.of("I"), "I",
+        ".limit stack 5",
+        ".limit locals 2",
+        "  ldc 42",
+        "  ldc 12",
+        "  iload 0", // {42, 12, i}
+        "  ldc 0",
+        "  istore 1", // Must initialize local before entry to try-catch block.
+        "  goto RealStart",
+        "LabelTryStart:", // Start the catch range on unreachable label.
+        "  goto RealStart",
+        "RealStart:",
+        "  swap",
+        "  istore 1",
+        "  idiv", // {42/i}
+        "LabelTryEnd:",
+        "  goto LabelRet",
+        "LabelCatch:", // Entry stack is {java/lang/Throwable}
+        "  pop",
+        "  iload 1", // {12}
+        "  goto LabelRet",
+        "LabelRet:",
+        "  ireturn",
+        ".catch java/lang/Exception from LabelTryStart to LabelTryEnd using LabelCatch"
+    );
+
+    clazz.addMainMethod(
+        ".limit stack 2",
+        ".limit locals 1",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 0",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/println(I)V",
+        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "  ldc 2",
+        "  invokestatic Test/foo(I)I",
+        "  invokevirtual java/io/PrintStream/print(I)V",
+        "  return");
+
+    String expected = "12\n21";
+    String javaResult = runOnJava(builder, clazz.name);
+    assertEquals(expected, javaResult);
+    String artResult = runOnArt(builder, clazz.name);
+    assertEquals(expected, artResult);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
new file mode 100644
index 0000000..1e6e3f7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -0,0 +1,1116 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.jdwp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Assume;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Wrapper for the art JDWP tests.
+ *
+ * A new version of this file can be generated using ./tools/create-jdwp-tests.py.
+ */
+public class RunJdwpTests {
+
+  enum Tool {
+    JAVAC,
+    DX,
+    D8
+  }
+
+  // Run all tests (default will only run smoke tests).
+  static final boolean RUN_ALL_TESTS = false;
+
+  // Print test output for passing tests (failing tests output is always printed).
+  static final boolean PRINT_STREAMS = true;
+
+  static final String RUN_SCRIPT = "tools/run-jdwp-tests.py";
+  static final String DEX_LIB = "third_party/jdwp-tests/apache-harmony-jdwp-tests-hostdex.jar";
+  static final String JAR_LIB = "third_party/jdwp-tests/apache-harmony-jdwp-tests-host.jar";
+
+  interface TestPredicate {
+    boolean test(DexVm dexVm, Tool tool);
+  }
+
+  static boolean isAndroidMOrAbove(DexVm dexVm, Tool tool) {
+    return dexVm.isNewerThan(DexVm.ART_5_1_1);
+  }
+
+  static boolean isAndroidNOrAbove(DexVm dexVm, Tool tool) {
+    return dexVm.isNewerThan(DexVm.ART_6_0_1);
+  }
+
+  static boolean isAndroidOOrAbove(DexVm dexVm, Tool tool) {
+    return dexVm.isNewerThan(DexVm.ART_7_0_0);
+  }
+
+  static boolean isLatestRuntime(DexVm dexVm, Tool tool) {
+    return dexVm == DexVm.ART_DEFAULT;
+  }
+
+  static final Map<String, TestPredicate> FAILING_TESTS =
+      ImmutableMap.<String, TestPredicate>builder()
+          .put("ArrayReference.SetValues003Test", RunJdwpTests::isAndroidNOrAbove)
+          .put("ClassType.InvokeMethodAfterMultipleThreadSuspensionTest",
+              RunJdwpTests::isAndroidNOrAbove)
+          .put("ClassType.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("ClassType.NewInstanceAfterMultipleThreadSuspensionTest",
+              RunJdwpTests::isAndroidNOrAbove)
+          .put("ClassType.NewInstanceStringTest", RunJdwpTests::isAndroidOOrAbove)
+          .put("ClassType.NewInstanceTagTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("ClassType.NewInstanceWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.BreakpointTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.Breakpoint002Test", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.BreakpointOnCatchTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.ClassPrepare002Test", RunJdwpTests::isAndroidOOrAbove)
+          .put("Events.CombinedExceptionEventsTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.ExceptionCaughtTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.ExceptionUncaughtTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.EventWithExceptionTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("Events.FieldAccessTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.FieldModificationTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.MethodEntryTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.MethodExitTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.MethodExitWithReturnValueTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.SingleStepTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("Events.SingleStepWithPendingExceptionTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("InterfaceType.InvokeMethodTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("Method.IsObsoleteTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("Method.VariableTableWithGenericTest", RunJdwpTests::isAndroidOOrAbove)
+          .put("ObjectReference.InvokeMethodDefaultTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("ObjectReference.InvokeMethodDefault002Test", RunJdwpTests::isAndroidNOrAbove)
+          .put("ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest",
+              RunJdwpTests::isAndroidNOrAbove)
+          .put("ObjectReference.InvokeMethodWithSuspensionTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("ReferenceType.GetValues006Test", RunJdwpTests::isAndroidOOrAbove)
+          .put("ReferenceType.ClassLoaderTest", RunJdwpTests::isAndroidNOrAbove)
+          .put("StackFrame.GetValuesTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("StackFrame.SetValuesTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("StackFrame.SetValues002Test", RunJdwpTests::isAndroidMOrAbove)
+          .put("VirtualMachine.CapabilitiesNewTest", RunJdwpTests::isLatestRuntime)
+          .put("VirtualMachine.ClassPathsTest", RunJdwpTests::isAndroidMOrAbove)
+          .put("VirtualMachine.DisposeDuringInvokeTest", RunJdwpTests::isAndroidMOrAbove)
+          .build();
+
+  // The smoke tests are the set of tests that fail if there is no debugging info in the dex files.
+  // We avoid running the remaining tests as part of the D8/R8 testing to reduce test time by >30m.
+  static final Set<String> SMOKE_TESTS = ImmutableSet.of(
+      "EventModifiers.InstanceOnlyModifierTest",
+      "Method.VariableTableTest",
+      "Method.VariableTableWithGenericTest",
+      "ObjectReference.ReferringObjectsTest",
+      // This test assumes specific register allocation to make sure that a temporary object
+      // is unreachable at a specific point in a method. That is not guaranteed. See b/36921933.
+      // Currently doesn't fail but may start failing again with a count one higher than expected.
+      "ReferenceType.InstancesTest",
+      "StackFrame.GetValues002Test",
+      "StackFrame.GetValuesTest",
+      "StackFrame.SetValues002Test",
+      "StackFrame.SetValuesTest",
+      "VirtualMachine.InstanceCountsTest"
+  );
+
+  private static File d8Out = null;
+
+  @ClassRule
+  public static TemporaryFolder temp = new TemporaryFolder();
+
+  @BeforeClass
+  public static void compileLibraries() throws IOException, CompilationException {
+    // Selects appropriate jar according to min api level for the selected runtime.
+    int minApi = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    Path jdwpTestsJar = ToolHelper.getJdwpTestsJarPath(minApi);
+
+    d8Out = temp.newFolder("d8-out");
+    D8.run(
+        D8Command.builder()
+            .addProgramFiles(jdwpTestsJar)
+            .setOutputPath(d8Out.toPath())
+            .setMinApiLevel(minApi)
+            .setMode(CompilationMode.DEBUG)
+            .build());
+  }
+
+  String getTestLib(Tool tool) {
+    if (tool == Tool.JAVAC) {
+      return JAR_LIB;
+    }
+    if (tool == Tool.DX) {
+      return DEX_LIB;
+    }
+    assert tool == Tool.D8;
+    return d8Out.toPath().resolve("classes.dex").toString();
+  }
+
+  DexVm getDexVm() {
+    return ToolHelper.getDexVm();
+  }
+
+  private void skipIfNeeded(String test, Tool tool) {
+    // Is it part of smoke tests ?
+    if (!RUN_ALL_TESTS) {
+      Assume.assumeTrue("Skipping non-smoke test " + test, SMOKE_TESTS.contains(test));
+    }
+    if (tool != Tool.JAVAC) {
+      // Can we run the test on the current ART runtime ?
+      Assume.assumeTrue("Skipping test " + test + " because ART is not supported",
+          ToolHelper.artSupported());
+    }
+  }
+
+  void runTest(String test, Tool tool) throws IOException {
+    skipIfNeeded(test, tool);
+    System.out.println("Running test " + test + " for tool " + tool);
+    String lib = getTestLib(tool);
+
+    List<String> command;
+    if (tool == Tool.JAVAC) {
+      String run = "org.junit.runner.JUnitCore";
+      String pkg = "org.apache.harmony.jpda.tests.jdwp";
+      command = Arrays.asList(
+          ToolHelper.getJavaExecutable(),
+          "-cp", System.getProperty("java.class.path") + ":" + lib,
+          run, pkg + "." + test);
+    } else {
+      command = Arrays.asList(
+          RUN_SCRIPT, "--classpath=" + lib, "--version=" + ToolHelper.getDexVm(), test);
+    }
+    ProcessBuilder builder = new ProcessBuilder(command);
+    ProcessResult result = ToolHelper.runProcess(builder);
+    if (FAILING_TESTS.containsKey(test) && !FAILING_TESTS.get(test).test(getDexVm(), tool)) {
+      if (PRINT_STREAMS || result.exitCode == 0) {
+        printStreams(result);
+      }
+      assertNotEquals("Expected test " + test + " to fail but it succeeded", 0, result.exitCode);
+    } else if (PRINT_STREAMS || result.exitCode != 0) {
+      printStreams(result);
+      assertEquals(0, result.exitCode);
+    }
+  }
+
+  private void printStreams(ProcessResult result) {
+    System.out.println("Test STDOUT");
+    System.out.println(result.stdout);
+    System.out.println("Test STDERR");
+    System.out.println(result.stderr);
+  }
+
+  @Test
+  public void testArrayReference_GetValuesTest_D8() throws IOException {
+    runTest("ArrayReference.GetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testArrayReference_LengthTest_D8() throws IOException {
+    runTest("ArrayReference.LengthTest", Tool.D8);
+  }
+
+  @Test
+  public void testArrayReference_SetValues002Test_D8() throws IOException {
+    runTest("ArrayReference.SetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testArrayReference_SetValues003Test_D8() throws IOException {
+    runTest("ArrayReference.SetValues003Test", Tool.D8);
+  }
+
+  @Test
+  public void testArrayReference_SetValuesTest_D8() throws IOException {
+    runTest("ArrayReference.SetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testArrayType_NewInstanceTest_D8() throws IOException {
+    runTest("ArrayType.NewInstanceTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassLoaderReference_VisibleClassesTest_D8() throws IOException {
+    runTest("ClassLoaderReference.VisibleClassesTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassObjectReference_ReflectedType002Test_D8() throws IOException {
+    runTest("ClassObjectReference.ReflectedType002Test", Tool.D8);
+  }
+
+  @Test
+  public void testClassObjectReference_ReflectedTypeTest_D8() throws IOException {
+    runTest("ClassObjectReference.ReflectedTypeTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_InvokeMethod002Test_D8() throws IOException {
+    runTest("ClassType.InvokeMethod002Test", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_InvokeMethod003Test_D8() throws IOException {
+    runTest("ClassType.InvokeMethod003Test", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_InvokeMethodAfterMultipleThreadSuspensionTest_D8()
+      throws IOException {
+    runTest("ClassType.InvokeMethodAfterMultipleThreadSuspensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_InvokeMethodWithSuspensionTest_D8() throws IOException {
+    runTest("ClassType.InvokeMethodWithSuspensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_InvokeMethodTest_D8() throws IOException {
+    runTest("ClassType.InvokeMethodTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_NewInstance002Test_D8() throws IOException {
+    runTest("ClassType.NewInstance002Test", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_NewInstanceTagTest_D8() throws IOException {
+    runTest("ClassType.NewInstanceTagTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_NewInstanceAfterMultipleThreadSuspensionTest_D8()
+      throws IOException {
+    runTest("ClassType.NewInstanceAfterMultipleThreadSuspensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_NewInstanceStringTest_D8() throws IOException {
+    runTest("ClassType.NewInstanceStringTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_NewInstanceTest_D8() throws IOException {
+    runTest("ClassType.NewInstanceTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_NewInstanceWithSuspensionTest_D8() throws IOException {
+    runTest("ClassType.NewInstanceWithSuspensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_SetValues002Test_D8() throws IOException {
+    runTest("ClassType.SetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_SetValuesTest_D8() throws IOException {
+    runTest("ClassType.SetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testClassType_SuperClassTest_D8() throws IOException {
+    runTest("ClassType.SuperClassTest", Tool.D8);
+  }
+
+  @Test
+  public void testDeoptimization_DeoptimizationWithExceptionHandlingTest_D8()
+      throws IOException {
+    runTest("Deoptimization.DeoptimizationWithExceptionHandlingTest", Tool.D8);
+  }
+
+  @Test
+  public void testEventModifiers_CountModifierTest_D8() throws IOException {
+    runTest("EventModifiers.CountModifierTest", Tool.D8);
+  }
+
+  @Test
+  public void testEventModifiers_InstanceOnlyModifierTest_D8() throws IOException {
+    runTest("EventModifiers.InstanceOnlyModifierTest", Tool.D8);
+  }
+
+  @Test
+  public void testEventModifiers_ThreadOnlyModifierTest_D8() throws IOException {
+    runTest("EventModifiers.ThreadOnlyModifierTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_Breakpoint002Test_D8() throws IOException {
+    runTest("Events.Breakpoint002Test", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_BreakpointMultipleTest_D8() throws IOException {
+    runTest("Events.BreakpointMultipleTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_BreakpointOnCatchTest_D8() throws IOException {
+    runTest("Events.BreakpointOnCatchTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_BreakpointTest_D8() throws IOException {
+    runTest("Events.BreakpointTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ClassPrepare002Test_D8() throws IOException {
+    runTest("Events.ClassPrepare002Test", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ClassPrepareTest_D8() throws IOException {
+    runTest("Events.ClassPrepareTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_CombinedEvents002Test_D8() throws IOException {
+    runTest("Events.CombinedEvents002Test", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_CombinedEvents003Test_D8() throws IOException {
+    runTest("Events.CombinedEvents003Test", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_CombinedEventsTest_D8() throws IOException {
+    runTest("Events.CombinedEventsTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_CombinedExceptionEventsTest_D8() throws IOException {
+    runTest("Events.CombinedExceptionEventsTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_EventWithExceptionTest_D8() throws IOException {
+    runTest("Events.EventWithExceptionTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ExceptionCaughtTest_D8() throws IOException {
+    runTest("Events.ExceptionCaughtTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ExceptionUncaughtTest_D8() throws IOException {
+    runTest("Events.ExceptionUncaughtTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ExceptionWithLocationTest_D8() throws IOException {
+    runTest("Events.ExceptionWithLocationTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_FieldAccessTest_D8() throws IOException {
+    runTest("Events.FieldAccessTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_FieldModification002Test_D8() throws IOException {
+    runTest("Events.FieldModification002Test", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_FieldModificationTest_D8() throws IOException {
+    runTest("Events.FieldModificationTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_FieldWithLocationTest_D8() throws IOException {
+    runTest("Events.FieldWithLocationTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_MethodEntryTest_D8() throws IOException {
+    runTest("Events.MethodEntryTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_MethodExitTest_D8() throws IOException {
+    runTest("Events.MethodExitTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_MethodExitWithReturnValueTest_D8() throws IOException {
+    runTest("Events.MethodExitWithReturnValueTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_SingleStepTest_D8() throws IOException {
+    runTest("Events.SingleStepTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_SingleStepThroughReflectionTest_D8() throws IOException {
+    runTest("Events.SingleStepThroughReflectionTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_SingleStepWithLocationTest_D8() throws IOException {
+    runTest("Events.SingleStepWithLocationTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_SingleStepWithPendingExceptionTest_D8() throws IOException {
+    runTest("Events.SingleStepWithPendingExceptionTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ThreadEndTest_D8() throws IOException {
+    runTest("Events.ThreadEndTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_ThreadStartTest_D8() throws IOException {
+    runTest("Events.ThreadStartTest", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_VMDeath002Test_D8() throws IOException {
+    runTest("Events.VMDeath002Test", Tool.D8);
+  }
+
+  @Test
+  public void testEvents_VMDeathTest_D8() throws IOException {
+    runTest("Events.VMDeathTest", Tool.D8);
+  }
+
+  @Test
+  public void testInterfaceType_InvokeMethodTest_D8() throws IOException {
+    runTest("InterfaceType.InvokeMethodTest", Tool.D8);
+  }
+
+  @Test
+  public void testMethod_BytecodesTest_D8() throws IOException {
+    runTest("Method.BytecodesTest", Tool.D8);
+  }
+
+  @Test
+  public void testMethod_IsObsoleteTest_D8() throws IOException {
+    runTest("Method.IsObsoleteTest", Tool.D8);
+  }
+
+  @Test
+  public void testMethod_LineTableTest_D8() throws IOException {
+    runTest("Method.LineTableTest", Tool.D8);
+  }
+
+  @Test
+  public void testMethod_VariableTableTest_D8() throws IOException {
+    runTest("Method.VariableTableTest", Tool.JAVAC);
+  }
+
+  @Test
+  public void testMethod_VariableTableWithGenericTest_D8() throws IOException {
+    runTest("Method.VariableTableWithGenericTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_AttachConnectorTest_D8() throws IOException {
+    runTest("MultiSession.AttachConnectorTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_BreakpointTest_D8() throws IOException {
+    runTest("MultiSession.BreakpointTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_ClassObjectIDTest_D8() throws IOException {
+    runTest("MultiSession.ClassObjectIDTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_ClassPrepareTest_D8() throws IOException {
+    runTest("MultiSession.ClassPrepareTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_EnableCollectionTest_D8() throws IOException {
+    runTest("MultiSession.EnableCollectionTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_ExceptionTest_D8() throws IOException {
+    runTest("MultiSession.ExceptionTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_FieldAccessTest_D8() throws IOException {
+    runTest("MultiSession.FieldAccessTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_FieldModificationTest_D8() throws IOException {
+    runTest("MultiSession.FieldModificationTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_ListenConnectorTest_D8() throws IOException {
+    runTest("MultiSession.ListenConnectorTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_MethodEntryExitTest_D8() throws IOException {
+    runTest("MultiSession.MethodEntryExitTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_RefTypeIDTest_D8() throws IOException {
+    runTest("MultiSession.RefTypeIDTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_ResumeTest_D8() throws IOException {
+    runTest("MultiSession.ResumeTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_SingleStepTest_D8() throws IOException {
+    runTest("MultiSession.SingleStepTest", Tool.D8);
+  }
+
+  @Test
+  public void testMultiSession_VMDeathTest_D8() throws IOException {
+    runTest("MultiSession.VMDeathTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_DisableCollectionTest_D8() throws IOException {
+    runTest("ObjectReference.DisableCollectionTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_EnableCollectionTest_D8() throws IOException {
+    runTest("ObjectReference.EnableCollectionTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_GetValues002Test_D8() throws IOException {
+    runTest("ObjectReference.GetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_GetValues003Test_D8() throws IOException {
+    runTest("ObjectReference.GetValues003Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_GetValuesTest_D8() throws IOException {
+    runTest("ObjectReference.GetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethod002Test_D8() throws IOException {
+    runTest("ObjectReference.InvokeMethod002Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethod003Test_D8() throws IOException {
+    runTest("ObjectReference.InvokeMethod003Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethodTest_D8() throws IOException {
+    runTest("ObjectReference.InvokeMethodTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethodAfterMultipleThreadSuspensionTest_D8()
+      throws IOException {
+    runTest("ObjectReference.InvokeMethodAfterMultipleThreadSuspensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethodDefault002Test_D8() throws IOException {
+    runTest("ObjectReference.InvokeMethodDefault002Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethodDefaultTest_D8() throws IOException {
+    runTest("ObjectReference.InvokeMethodDefaultTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_InvokeMethodWithSuspensionTest_D8() throws IOException {
+    runTest("ObjectReference.InvokeMethodWithSuspensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_IsCollectedTest_D8() throws IOException {
+    runTest("ObjectReference.IsCollectedTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_MonitorInfoTest_D8() throws IOException {
+    runTest("ObjectReference.MonitorInfoTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_ReferenceTypeTest_D8() throws IOException {
+    runTest("ObjectReference.ReferenceTypeTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_ReferringObjectsTest_D8() throws IOException {
+    runTest("ObjectReference.ReferringObjectsTest", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_SetValues002Test_D8() throws IOException {
+    runTest("ObjectReference.SetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_SetValues003Test_D8() throws IOException {
+    runTest("ObjectReference.SetValues003Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_SetValues004Test_D8() throws IOException {
+    runTest("ObjectReference.SetValues004Test", Tool.D8);
+  }
+
+  @Test
+  public void testObjectReference_SetValuesTest_D8() throws IOException {
+    runTest("ObjectReference.SetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_ClassLoaderTest_D8() throws IOException {
+    runTest("ReferenceType.ClassLoaderTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_ClassObjectTest_D8() throws IOException {
+    runTest("ReferenceType.ClassObjectTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_ConstantPoolTest_D8() throws IOException {
+    runTest("ReferenceType.ConstantPoolTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_FieldsTest_D8() throws IOException {
+    runTest("ReferenceType.FieldsTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_FieldsWithGenericTest_D8() throws IOException {
+    runTest("ReferenceType.FieldsWithGenericTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValues002Test_D8() throws IOException {
+    runTest("ReferenceType.GetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValues003Test_D8() throws IOException {
+    runTest("ReferenceType.GetValues003Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValues004Test_D8() throws IOException {
+    runTest("ReferenceType.GetValues004Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValues005Test_D8() throws IOException {
+    runTest("ReferenceType.GetValues005Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValues006Test_D8() throws IOException {
+    runTest("ReferenceType.GetValues006Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValues007Test_D8() throws IOException {
+    runTest("ReferenceType.GetValues007Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_GetValuesTest_D8() throws IOException {
+    runTest("ReferenceType.GetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_InstancesTest_D8() throws IOException {
+    runTest("ReferenceType.InstancesTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_InterfacesTest_D8() throws IOException {
+    runTest("ReferenceType.InterfacesTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_MethodsTest_D8() throws IOException {
+    runTest("ReferenceType.MethodsTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_MethodsWithGenericTest_D8() throws IOException {
+    runTest("ReferenceType.MethodsWithGenericTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_ModifiersTest_D8() throws IOException {
+    runTest("ReferenceType.ModifiersTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_Signature002Test_D8() throws IOException {
+    runTest("ReferenceType.Signature002Test", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_SignatureTest_D8() throws IOException {
+    runTest("ReferenceType.SignatureTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_SignatureWithGenericTest_D8() throws IOException {
+    runTest("ReferenceType.SignatureWithGenericTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_SourceDebugExtensionTest_D8() throws IOException {
+    runTest("ReferenceType.SourceDebugExtensionTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_SourceFileTest_D8() throws IOException {
+    runTest("ReferenceType.SourceFileTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_StatusTest_D8() throws IOException {
+    runTest("ReferenceType.StatusTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_SyntheticFieldsTest_D8() throws IOException {
+    runTest("ReferenceType.SyntheticFieldsTest", Tool.D8);
+  }
+
+  @Test
+  public void testReferenceType_SyntheticMethodsTest_D8() throws IOException {
+    runTest("ReferenceType.SyntheticMethodsTest", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_GetValues002Test_D8() throws IOException {
+    runTest("StackFrame.GetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_GetValuesTest_D8() throws IOException {
+    runTest("StackFrame.GetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_PopFrames002Test_D8() throws IOException {
+    runTest("StackFrame.PopFrames002Test", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_PopFramesTest_D8() throws IOException {
+    runTest("StackFrame.PopFramesTest", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_ProxyThisObjectTest_D8() throws IOException {
+    runTest("StackFrame.ProxyThisObjectTest", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_SetValues002Test_D8() throws IOException {
+    runTest("StackFrame.SetValues002Test", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_SetValuesTest_D8() throws IOException {
+    runTest("StackFrame.SetValuesTest", Tool.D8);
+  }
+
+  @Test
+  public void testStackFrame_ThisObjectTest_D8() throws IOException {
+    runTest("StackFrame.ThisObjectTest", Tool.D8);
+  }
+
+  @Test
+  public void testStringReference_ValueTest_D8() throws IOException {
+    runTest("StringReference.ValueTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadGroupReference_ChildrenTest_D8() throws IOException {
+    runTest("ThreadGroupReference.ChildrenTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadGroupReference_NameTest_D8() throws IOException {
+    runTest("ThreadGroupReference.NameTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadGroupReference_ParentTest_D8() throws IOException {
+    runTest("ThreadGroupReference.ParentTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_CurrentContendedMonitorTest_D8() throws IOException {
+    runTest("ThreadReference.CurrentContendedMonitorTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ForceEarlyReturn002Test_D8() throws IOException {
+    runTest("ThreadReference.ForceEarlyReturn002Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ForceEarlyReturn003Test_D8() throws IOException {
+    runTest("ThreadReference.ForceEarlyReturn003Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ForceEarlyReturn004Test_D8() throws IOException {
+    runTest("ThreadReference.ForceEarlyReturn004Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ForceEarlyReturn005Test_D8() throws IOException {
+    runTest("ThreadReference.ForceEarlyReturn005Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ForceEarlyReturn006Test_D8() throws IOException {
+    runTest("ThreadReference.ForceEarlyReturn006Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ForceEarlyReturnTest_D8() throws IOException {
+    runTest("ThreadReference.ForceEarlyReturnTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_FrameCountTest_D8() throws IOException {
+    runTest("ThreadReference.FrameCountTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_FramesTest_D8() throws IOException {
+    runTest("ThreadReference.FramesTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_InterruptTest_D8() throws IOException {
+    runTest("ThreadReference.InterruptTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_NameTest_D8() throws IOException {
+    runTest("ThreadReference.NameTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_OwnedMonitorsStackDepthInfoTest_D8() throws IOException {
+    runTest("ThreadReference.OwnedMonitorsStackDepthInfoTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_OwnedMonitorsTest_D8() throws IOException {
+    runTest("ThreadReference.OwnedMonitorsTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ResumeTest_D8() throws IOException {
+    runTest("ThreadReference.ResumeTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_Status002Test_D8() throws IOException {
+    runTest("ThreadReference.Status002Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_Status003Test_D8() throws IOException {
+    runTest("ThreadReference.Status003Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_Status004Test_D8() throws IOException {
+    runTest("ThreadReference.Status004Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_Status005Test_D8() throws IOException {
+    runTest("ThreadReference.Status005Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_Status006Test_D8() throws IOException {
+    runTest("ThreadReference.Status006Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_StatusTest_D8() throws IOException {
+    runTest("ThreadReference.StatusTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_SuspendCountTest_D8() throws IOException {
+    runTest("ThreadReference.SuspendCountTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_SuspendTest_D8() throws IOException {
+    runTest("ThreadReference.SuspendTest", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ThreadGroup002Test_D8() throws IOException {
+    runTest("ThreadReference.ThreadGroup002Test", Tool.D8);
+  }
+
+  @Test
+  public void testThreadReference_ThreadGroupTest_D8() throws IOException {
+    runTest("ThreadReference.ThreadGroupTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_AllClassesTest_D8() throws IOException {
+    runTest("VirtualMachine.AllClassesTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_AllClassesWithGenericTest_D8() throws IOException {
+    runTest("VirtualMachine.AllClassesWithGenericTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_AllThreadsTest_D8() throws IOException {
+    runTest("VirtualMachine.AllThreadsTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_CapabilitiesNewTest_D8() throws IOException {
+    runTest("VirtualMachine.CapabilitiesNewTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_CapabilitiesTest_D8() throws IOException {
+    runTest("VirtualMachine.CapabilitiesTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_ClassesBySignatureTest_D8() throws IOException {
+    runTest("VirtualMachine.ClassesBySignatureTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_ClassPathsTest_D8() throws IOException {
+    runTest("VirtualMachine.ClassPathsTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_CreateStringTest_D8() throws IOException {
+    runTest("VirtualMachine.CreateStringTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_DisposeDuringInvokeTest_D8() throws IOException {
+    runTest("VirtualMachine.DisposeDuringInvokeTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_DisposeTest_D8() throws IOException {
+    runTest("VirtualMachine.DisposeTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_DisposeObjectsTest_D8() throws IOException {
+    runTest("VirtualMachine.DisposeObjectsTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_ExitTest_D8() throws IOException {
+    runTest("VirtualMachine.ExitTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_IDSizesTest_D8() throws IOException {
+    runTest("VirtualMachine.IDSizesTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_InstanceCountsTest_D8() throws IOException {
+    runTest("VirtualMachine.InstanceCountsTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_RedefineClassesTest_D8() throws IOException {
+    runTest("VirtualMachine.RedefineClassesTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_Resume002Test_D8() throws IOException {
+    runTest("VirtualMachine.Resume002Test", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_ResumeTest_D8() throws IOException {
+    runTest("VirtualMachine.ResumeTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_SetDefaultStratumTest_D8() throws IOException {
+    runTest("VirtualMachine.SetDefaultStratumTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_SuspendTest_D8() throws IOException {
+    runTest("VirtualMachine.SuspendTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_TopLevelThreadGroupsTest_D8() throws IOException {
+    runTest("VirtualMachine.TopLevelThreadGroupsTest", Tool.D8);
+  }
+
+  @Test
+  public void testVirtualMachine_VersionTest_D8() throws IOException {
+    runTest("VirtualMachine.VersionTest", Tool.D8);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jsr45/HelloKt.class b/src/test/java/com/android/tools/r8/jsr45/HelloKt.class
new file mode 100644
index 0000000..936fc80
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/HelloKt.class
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
new file mode 100644
index 0000000..66137df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -0,0 +1,190 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jsr45;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.AnnotationSubject;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.io.Closer;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class JSR45Tests {
+
+  private static final String DEFAULT_MAP_FILENAME = "proguard.map";
+  private static final Path INPUT_PATH =
+      Paths.get("src/test/java/com/android/tools/r8/jsr45/HelloKt.class");
+  private static final Path DONT_SHRINK_DONT_OBFUSCATE_CONFIG =
+      Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt");
+  private static final Path DONT_SHRINK_CONFIG =
+      Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt");
+  private static final Path SHRINK_KEEP_CONFIG =
+      Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt");
+  private static final Path SHRINK_NO_KEEP_CONFIG =
+      Paths.get("src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt");
+
+  @Rule
+  public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
+
+  void compileWithD8(Path intputPath, Path outputPath) throws IOException, CompilationException {
+    D8.run(
+        D8Command.builder()
+            .setMinApiLevel(Constants.ANDROID_O_API)
+            .addProgramFiles(intputPath)
+            .setOutputPath(outputPath)
+            .build());
+  }
+
+  void compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    AndroidApp androidApp =
+        R8.run(
+            R8Command.builder()
+                .addProgramFiles(inputPath)
+                .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+                .setOutputPath(outputPath)
+                .addProguardConfigurationFiles(keepRulesPath)
+                .build());
+    if (androidApp.hasProguardMap()) {
+      try (Closer closer = Closer.create()) {
+        androidApp.writeProguardMap(closer, new FileOutputStream(
+            Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME).toFile()));
+      }
+    }
+  }
+
+  static class ReadSourceDebugExtensionAttribute extends ClassVisitor {
+
+    private ReadSourceDebugExtensionAttribute(int api, ClassVisitor cv) {
+      super(api, cv);
+    }
+
+    private String debugSourceExtension = null;
+
+    @Override
+    public void visitSource(String source, String debug) {
+      debugSourceExtension = debug;
+      super.visitSource(source, debug);
+    }
+  }
+
+  @Test
+  public void testSourceDebugExtensionWithD8()
+      throws IOException, CompilationException, ExecutionException {
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+
+    compileWithD8(INPUT_PATH, outputPath);
+
+    checkAnnotationContent(INPUT_PATH, outputPath);
+  }
+
+  /**
+   * Check that when dontshrink and dontobfuscate is used the annotation is transmitted.
+   */
+  @Test
+  public void testSourceDebugExtensionWithShriking1()
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+    compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_DONT_OBFUSCATE_CONFIG);
+    checkAnnotationContent(INPUT_PATH, outputPath);
+  }
+
+  /**
+   * Check that when dontshrink is used the annotation is not removed due to obfuscation.
+   */
+  @Test
+  public void testSourceDebugExtensionWithShrinking2()
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+    compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_CONFIG);
+    checkAnnotationContent(INPUT_PATH, outputPath);
+  }
+
+  /**
+   * Check that the annotation is transmitted when shrinking is enabled with a keepattribute option.
+   */
+  @Test
+  public void testSourceDebugExtensionWithShrinking3()
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+
+    compileWithR8(INPUT_PATH, outputPath, SHRINK_KEEP_CONFIG);
+
+    checkAnnotationContent(INPUT_PATH, outputPath);
+  }
+
+  /**
+   * Check that the annotation is removed when shriking is enabled and that there is not
+   * keepattributes option.
+   */
+  @Test
+  public void testSourceDebugExtensionWithShrinking4()
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+
+    compileWithR8(INPUT_PATH, outputPath, SHRINK_NO_KEEP_CONFIG);
+
+    DexInspector dexInspector =
+        new DexInspector(outputPath.resolve("classes.dex"), getGeneratedProguardMap());
+    ClassSubject classSubject = dexInspector.clazz("HelloKt");
+    AnnotationSubject annotationSubject =
+        classSubject.annotation("dalvik.annotation.SourceDebugExtension");
+    Assert.assertFalse(annotationSubject.isPresent());
+  }
+
+  private void checkAnnotationContent(Path inputPath, Path outputPath)
+      throws IOException, ExecutionException {
+    ClassReader classReader = new ClassReader(new FileInputStream(inputPath.toFile()));
+    ReadSourceDebugExtensionAttribute sourceDebugExtensionReader =
+        new ReadSourceDebugExtensionAttribute(Opcodes.ASM5, null);
+    classReader.accept(sourceDebugExtensionReader, 0);
+
+    DexInspector dexInspector =
+        new DexInspector(outputPath.resolve("classes.dex"), getGeneratedProguardMap());
+    ClassSubject classSubject = dexInspector.clazz("HelloKt");
+
+    AnnotationSubject annotationSubject =
+        classSubject.annotation("dalvik.annotation.SourceDebugExtension");
+    Assert.assertTrue(annotationSubject.isPresent());
+    DexAnnotationElement[] annotationElement = annotationSubject.getAnnotation().elements;
+    Assert.assertNotNull(annotationElement);
+    Assert.assertTrue(annotationElement.length == 1);
+    Assert.assertEquals("value", annotationElement[0].name.toString());
+    Assert.assertTrue(annotationElement[0].value instanceof DexValueString);
+    Assert.assertEquals(
+        sourceDebugExtensionReader.debugSourceExtension,
+        ((DexValueString) annotationElement[0].value).value.toSourceString());
+  }
+
+  private String getGeneratedProguardMap() throws IOException {
+    Path mapFile = Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME);
+    if (Files.exists(mapFile)) {
+      return mapFile.toAbsolutePath().toString();
+    }
+    return null;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt
new file mode 100644
index 0000000..04e2d30
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-1.txt
@@ -0,0 +1,7 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontshrink
+-dontobfuscate
+
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt
new file mode 100644
index 0000000..916ee1c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-2.txt
@@ -0,0 +1,8 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontshrink
+# allow access modification to enable minification
+-allowaccessmodification
+
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt
new file mode 100644
index 0000000..69395dc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-3.txt
@@ -0,0 +1,10 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-keep,allowobfuscation public class HelloKt {
+  public static void main(...);
+}
+# allow access modification to enable minification
+-allowaccessmodification
+-keepattributes SourceDebugExtension
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt b/src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt
new file mode 100644
index 0000000..1651fda
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jsr45/keep-rules-4.txt
@@ -0,0 +1,9 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-keep,allowobfuscation public class HelloKt {
+  public static void main(...);
+}
+# allow access modification to enable minification
+-allowaccessmodification
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
new file mode 100644
index 0000000..7840b6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -0,0 +1,416 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.maindexlist;
+
+import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Switch.Type;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.ir.desugar.SynthesizedCode;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
+import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.MainDexList;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class MainDexListTests {
+
+  private static final Path BASE =
+      Paths.get("src", "test", "java", "com", "android", "tools", "r8", "maindexlist");
+
+  private static boolean verifyApplications = true;
+  private static boolean regenerateApplications = false;
+
+  private static final int MAX_METHOD_COUNT = Constants.U16BIT_MAX;
+  private static final List<String> TWO_LARGE_CLASSES = ImmutableList.of("A", "B");
+  private static final String TWO_LARGE_CLASSES_APP = "two-large-classes.zip";
+
+  private static final int MANY_CLASSES_COUNT = 1000;
+  private static final List<String> MANY_CLASSES;
+  private static final String MANY_CLASSES_APP = "many-classes.zip";
+  static {
+    ImmutableList.Builder<String> builder = ImmutableList.builder();
+    for (int i = 0; i < MANY_CLASSES_COUNT; ++i) {
+      String pkg = i % 2 == 0 ? "a" : "b";
+      builder.add(pkg + ".Class" + i);
+    }
+    MANY_CLASSES = builder.build();
+  }
+
+  public static Path getTwoLargeClassesAppPath() {
+    return BASE.resolve(TWO_LARGE_CLASSES_APP);
+  }
+
+  public static Path getManyClassesAppPath() {
+    return BASE.resolve(MANY_CLASSES_APP);
+  }
+
+  // Generates an application with two classes, each with the maximum possible number of methods.
+  @Test
+  public void generateTwoLargeClasses() throws IOException, ExecutionException {
+    if (!verifyApplications && !regenerateApplications) {
+      return;
+    }
+    AndroidApp generated = generateApplication(TWO_LARGE_CLASSES, MAX_METHOD_COUNT);
+    if (regenerateApplications) {
+      generated.write(getTwoLargeClassesAppPath(), true);
+    } else {
+      AndroidApp cached = AndroidApp.fromProgramFiles(getTwoLargeClassesAppPath());
+      compareToCachedVersion(cached, generated, TWO_LARGE_CLASSES_APP);
+    }
+  }
+
+  // Generates an application with many classes, every even in one package and every odd in another.
+  @Test
+  public void generateManyClasses() throws IOException, ExecutionException {
+    if (!verifyApplications && !regenerateApplications) {
+      return;
+    }
+    AndroidApp generated = generateApplication(MANY_CLASSES, 1);
+    if (regenerateApplications) {
+      generated.write(getManyClassesAppPath(), true);
+    } else {
+      AndroidApp cached = AndroidApp.fromProgramFiles(getManyClassesAppPath());
+      compareToCachedVersion(cached, generated, MANY_CLASSES_APP);
+    }
+  }
+
+  private static void compareToCachedVersion(AndroidApp cached, AndroidApp generated, String name)
+      throws IOException {
+    assertEquals("On-disk cached app (" + name + ") differs in file count from regeneration"
+            + "Set 'regenerateApplications = true' and rerun this test to update the cache.",
+        cached.getDexProgramResources().size(), generated.getDexProgramResources().size());
+    try (Closer closer = Closer.create()) {
+      for (int i = 0; i < cached.getDexProgramResources().size(); i++) {
+        byte[] cachedBytes =
+            ByteStreams.toByteArray(cached.getDexProgramResources().get(i).getStream(closer));
+        byte[] generatedBytes =
+            ByteStreams.toByteArray(generated.getDexProgramResources().get(i).getStream(closer));
+        assertArrayEquals("On-disk cached app differs in byte content from regeneration"
+                + "Set 'regenerateApplications = true' and rerun this test to update the cache.",
+            cachedBytes, generatedBytes);
+      }
+    }
+  }
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void putFirstClassInMainDexList() throws Throwable {
+    verifyMainDexContains(TWO_LARGE_CLASSES.subList(0, 1), getTwoLargeClassesAppPath());
+  }
+
+  @Test
+  public void putSecondClassInMainDexList() throws Throwable {
+    verifyMainDexContains(TWO_LARGE_CLASSES.subList(1, 2), getTwoLargeClassesAppPath());
+  }
+
+  @Test
+  public void cannotFitBothIntoMainDex() throws Throwable {
+    thrown.expect(CompilationError.class);
+    verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath());
+  }
+
+  @Test
+  public void everySecondClassInMainDex() throws Throwable {
+    ImmutableList.Builder<String> mainDexBuilder = ImmutableList.builder();
+    for (int i = 0; i < MANY_CLASSES.size(); i++) {
+      String clazz = MANY_CLASSES.get(i);
+      if (i % 3 == 0) {
+        mainDexBuilder.add(clazz);
+      }
+    }
+    verifyMainDexContains(mainDexBuilder.build(), getManyClassesAppPath());
+  }
+
+  @Test
+  public void validEntries() throws IOException {
+    List<String> list = ImmutableList.of(
+        "A.class",
+        "a/b/c/D.class",
+        "a/b/c/D$E.class"
+    );
+    DexItemFactory factory = new DexItemFactory();
+    Path mainDexList = temp.getRoot().toPath().resolve("valid.txt");
+    FileUtils.writeTextFile(mainDexList, list);
+    Set<DexType> types = MainDexList.parse(mainDexList, factory);
+    for (String entry : list) {
+      assertTrue(types.contains(factory.createType("L" + entry.replace(".class", "") + ";")));
+    }
+  }
+
+  @Test
+  public void invalidQualifiedEntry() throws IOException {
+    thrown.expect(CompilationError.class);
+    DexItemFactory factory = new DexItemFactory();
+    Path mainDexList = temp.getRoot().toPath().resolve("invalid.txt");
+    FileUtils.writeTextFile(mainDexList, ImmutableList.of("a.b.c.D.class"));
+    MainDexList.parse(mainDexList, factory);
+  }
+
+  private static String typeToEntry(String type) {
+    return type.replace(".", "/") + CLASS_EXTENSION;
+  }
+
+  private void verifyMainDexContains(List<String> mainDex, Path app)
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+    AndroidApp originalApp = AndroidApp.fromProgramFiles(app);
+    DexInspector originalInspector = new DexInspector(originalApp);
+    for (String clazz : mainDex) {
+      assertTrue("Class " + clazz + " does not exist in input",
+          originalInspector.clazz(clazz).isPresent());
+    }
+    Path outDir = temp.newFolder("out").toPath();
+    Path mainDexList = temp.getRoot().toPath().resolve("main-dex-list.txt");
+    FileUtils.writeTextFile(mainDexList, ListUtils.map(mainDex, MainDexListTests::typeToEntry));
+    Path packageDistribution = temp.getRoot().toPath().resolve("package.map");
+    FileUtils.writeTextFile(packageDistribution, ImmutableList.of("a.*:2", "b.*:1"));
+    R8Command command =
+        R8Command.builder()
+            .addProgramFiles(app)
+            .setPackageDistributionFile(packageDistribution)
+            .setMainDexListFile(mainDexList)
+            .setOutputPath(outDir)
+            .setTreeShaking(false)
+            .setMinification(false)
+            .build();
+    ToolHelper.runR8(command);
+    assertTrue("Output run only produced one dex file. Invalid test",
+        1 < Files.list(outDir).filter(FileUtils::isDexFile).count());
+    DexInspector inspector =
+        new DexInspector(AndroidApp.fromProgramFiles(outDir.resolve("classes.dex")));
+    for (String clazz : mainDex) {
+      if (!inspector.clazz(clazz).isPresent()) {
+        Files.list(outDir)
+            .filter(FileUtils::isDexFile)
+            .forEach(
+                p -> {
+                  try {
+                    DexInspector i = new DexInspector(AndroidApp.fromProgramFiles(p));
+                    assertFalse("Found " + clazz + " in file " + p, i.clazz(clazz).isPresent());
+                  } catch (IOException | ExecutionException e) {
+                    e.printStackTrace();
+                  }
+                });
+        fail("Failed to find class " + clazz + "in any file...");
+      }
+    }
+  }
+
+  private static AndroidApp generateApplication(List<String> classes, int methodCount)
+      throws IOException, ExecutionException {
+    Timing timing = new Timing("MainDexListTests");
+    InternalOptions options = new InternalOptions();
+    DexItemFactory factory = options.itemFactory;
+    DexApplication.Builder builder = new DexApplication.Builder(factory, timing);
+    for (String clazz : classes) {
+      DexString desc = factory.createString(DescriptorUtils.javaTypeToDescriptor(clazz));
+      DexType type = factory.createType(desc);
+      DexEncodedMethod[] virtualMethods = new DexEncodedMethod[methodCount];
+      for (int i = 0; i < methodCount; i++) {
+        DexAccessFlags access = new DexAccessFlags();
+        access.setPublic();
+        access.setStatic();
+        Code code = new SynthesizedCode(new ReturnVoidCode());
+        DexEncodedMethod method =
+            new DexEncodedMethod(
+                factory.createMethod(
+                    desc,
+                    factory.createString("method" + i),
+                    factory.voidDescriptor,
+                    DexString.EMPTY_ARRAY),
+                access,
+                DexAnnotationSet.empty(),
+                DexAnnotationSetRefList.empty(),
+                code);
+        IRCode ir = code.buildIR(method, options);
+        RegisterAllocator allocator = new LinearScanRegisterAllocator(ir);
+        method.setCode(ir, allocator);
+        virtualMethods[i] = method;
+      }
+      builder.addClassPromise(
+          new DexProgramClass(
+              type,
+              DexClass.Origin.Synthetic,
+              new DexAccessFlags(),
+              factory.objectType,
+              DexTypeList.empty(),
+              null,
+              DexAnnotationSet.empty(),
+              DexEncodedField.EMPTY_ARRAY,
+              DexEncodedField.EMPTY_ARRAY,
+              DexEncodedMethod.EMPTY_ARRAY,
+              virtualMethods));
+    }
+    DexApplication application = builder.build();
+    AppInfoWithSubtyping appInfo = new AppInfoWithSubtyping(application);
+    ApplicationWriter writer =
+        new ApplicationWriter(application, appInfo, options, NamingLens.getIdentityLens(), null);
+    ExecutorService executor = ThreadUtils.getExecutorService(options);
+    try {
+      return writer.write(null, executor);
+    } finally {
+      executor.shutdown();
+    }
+  }
+
+  // Code stub to generate methods with "return-void" bodies.
+  private static class ReturnVoidCode implements SourceCode {
+
+    @Override
+    public int instructionCount() {
+      return 1;
+    }
+
+    @Override
+    public int instructionIndex(int instructionOffset) {
+      return instructionOffset;
+    }
+
+    @Override
+    public int instructionOffset(int instructionIndex) {
+      return instructionIndex;
+    }
+
+    @Override
+    public boolean needsPrelude() {
+      return false;
+    }
+
+    @Override
+    public DebugLocalInfo getCurrentLocal(int register) {
+      return null;
+    }
+
+    @Override
+    public boolean traceInstruction(int instructionIndex, IRBuilder builder) {
+      return true;
+    }
+
+    @Override
+    public void closedCurrentBlockWithFallthrough(int fallthroughInstructionIndex) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public void closedCurrentBlock() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void setUp() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void clear() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void buildPrelude(IRBuilder builder) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void buildInstruction(IRBuilder builder, int instructionIndex) {
+      assert instructionIndex == 0;
+      builder.addReturn();
+    }
+
+    @Override
+    public void buildPostlude(IRBuilder builder) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void resolveAndBuildSwitch(
+        Type type, int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public void resolveAndBuildNewArrayFilledData(
+        int arrayRef, int payloadOffset, IRBuilder builder) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public CatchHandlers<Integer> getCurrentCatchHandlers() {
+      return null;
+    }
+
+    @Override
+    public boolean verifyRegister(int register) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public boolean verifyCurrentInstructionCanThrow() {
+      throw new Unreachable();
+    }
+
+    @Override
+    public boolean verifyLocalInScope(DebugLocalInfo local) {
+      throw new Unreachable();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/many-classes.zip b/src/test/java/com/android/tools/r8/maindexlist/many-classes.zip
new file mode 100644
index 0000000..3c1530d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/many-classes.zip
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip b/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
new file mode 100644
index 0000000..97b5fde
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
Binary files differ
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
new file mode 100644
index 0000000..e45b639
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -0,0 +1,263 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.memberrebinding;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MemberRebindingTest {
+
+  private static final String ANDROID_JAR = ToolHelper.getDefaultAndroidJar();
+  private static final List<String> JAR_LIBRARIES = ImmutableList
+      .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "memberrebindinglib.jar");
+  private static final List<String> DEX_LIBRARIES = ImmutableList
+      .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "memberrebindinglib/classes.dex");
+
+  private enum Frontend {
+    DEX, JAR;
+
+    @Override
+    public String toString() {
+      return this == DEX ? ".dex" : ".jar";
+    }
+  }
+
+  private final Frontend kind;
+  private final Path originalDex;
+  private final Path programFile;
+  private final Consumer<DexInspector> inspection;
+  private final Consumer<DexInspector> originalInspection;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  public MemberRebindingTest(
+      String test, Frontend kind,
+      Consumer<DexInspector> inspection,
+      Consumer<DexInspector> originalInspection) {
+    this.kind = kind;
+    originalDex = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex");
+    if (kind == Frontend.DEX) {
+      this.programFile = originalDex;
+    } else {
+      this.programFile = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar");
+    }
+    this.inspection = inspection;
+    this.originalInspection = originalInspection;
+  }
+
+  @Before
+  public void runR8()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    // Generate R8 processed version without library option.
+    String out = temp.getRoot().getCanonicalPath();
+    List<String> libs = kind == Frontend.DEX ? DEX_LIBRARIES : JAR_LIBRARIES;
+    // NOTE: It is important to turn off inlining to ensure
+    // dex inspection of invokes is predictable.
+    ToolHelper.runR8(
+        R8Command.builder()
+            .setOutputPath(Paths.get(out))
+            .addProgramFiles(programFile)
+            .addLibraryFiles(ListUtils.map(libs, Paths::get))
+            .build(),
+        options -> options.inlineAccessors = false);
+  }
+
+  private static boolean coolInvokes(InstructionSubject instruction) {
+    if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+      return false;
+    }
+    InvokeInstructionSubject invoke = (InvokeInstructionSubject) instruction;
+    return !invoke.holder().is("java.io.PrintStream");
+  }
+
+  private static void inspectOriginalMain(DexInspector inspector) {
+    MethodSubject main = inspector.clazz("memberrebinding.Test").method(DexInspector.MAIN);
+    Iterator<InvokeInstructionSubject> iterator =
+        main.iterateInstructions(MemberRebindingTest::coolInvokes);
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PublicClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsOtherLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebindinglib.AnIndependentInterface"));
+    assertTrue(
+        iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+    assertTrue(
+        iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+    assertFalse(iterator.hasNext());
+  }
+
+  private static void inspectMain(DexInspector inspector) {
+    MethodSubject main = inspector.clazz("memberrebinding.Test").method(DexInspector.MAIN);
+    Iterator<InvokeInstructionSubject> iterator =
+        main.iterateInstructions(MemberRebindingTest::coolInvokes);
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassInMiddleOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding.SuperClassOfAll"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    assertTrue(iterator.next().holder().is("memberrebinding.ClassExtendsLibraryClass"));
+    // For the next three - test that we re-bind to library methods (holder is java.util.ArrayList).
+    assertTrue(iterator.next().holder().is("java.util.ArrayList"));
+    assertTrue(iterator.next().holder().is("java.util.ArrayList"));
+    assertTrue(iterator.next().holder().is("java.util.ArrayList"));
+    assertTrue(iterator.next().holder().is("memberrebinding.subpackage.PackagePrivateClass"));
+    // For the next three - test that we re-bind to the lowest library class.
+    assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+    assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+    assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+    // The next one is already precise.
+    assertTrue(
+        iterator.next().holder().is("memberrebinding.SuperClassOfClassExtendsOtherLibraryClass"));
+    // Some dispatches on interfaces.
+    assertTrue(iterator.next().holder().is("memberrebindinglib.AnIndependentInterface"));
+    assertTrue(iterator.next().holder().is("memberrebindinglib.SubClass"));
+    assertTrue(iterator.next().holder().is("memberrebindinglib.ImplementedInProgramClass"));
+    assertFalse(iterator.hasNext());
+  }
+
+  private static void inspectOriginalMain2(DexInspector inspector) {
+    MethodSubject main = inspector.clazz("memberrebinding2.Test").method(DexInspector.MAIN);
+    Iterator<FieldAccessInstructionSubject> iterator =
+        main.iterateInstructions(InstructionSubject::isFieldAccess);
+    // Run through instance put, static put, instance get and instance get.
+    for (int i = 0; i < 4; i++) {
+      assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PublicClass"));
+    }
+  }
+
+  private static void inspectMain2(DexInspector inspector) {
+    MethodSubject main = inspector.clazz("memberrebinding2.Test").method(DexInspector.MAIN);
+    Iterator<FieldAccessInstructionSubject> iterator =
+        main.iterateInstructions(InstructionSubject::isFieldAccess);
+    // Run through instance put, static put, instance get and instance get.
+    for (int i = 0; i < 4; i++) {
+      assertTrue(iterator.next().holder().is("memberrebinding2.ClassAtBottomOfChain"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.ClassInMiddleOfChain"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.SuperClassOfAll"));
+      assertTrue(iterator.next().holder().is("memberrebinding2.subpackage.PackagePrivateClass"));
+    }
+  }
+
+  public static MethodSignature TEST =
+      new MethodSignature("test", "void", new String[]{});
+
+  private static void inspectOriginal3(DexInspector inspector) {
+    MethodSubject main = inspector.clazz("memberrebinding3.Test").method(TEST);
+    Iterator<InvokeInstructionSubject> iterator =
+        main.iterateInstructions(InstructionSubject::isInvoke);
+    assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+  }
+
+  private static void inspect3(DexInspector inspector) {
+    MethodSubject main = inspector.clazz("memberrebinding3.Test").method(TEST);
+    Iterator<InvokeInstructionSubject> iterator =
+        main.iterateInstructions(InstructionSubject::isInvoke);
+    assertTrue(iterator.next().holder().is("memberrebinding3.ClassAtBottomOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding3.ClassInMiddleOfChain"));
+    assertTrue(iterator.next().holder().is("memberrebinding3.SuperClassOfAll"));
+  }
+
+  @Parameters(name = "{0}{1}")
+  public static Collection<Object[]> data() {
+    List<String> tests =
+        ImmutableList.of("memberrebinding", "memberrebinding2", "memberrebinding3");
+
+    Map<String, List<Consumer<DexInspector>>> inspections = new HashMap<>();
+    inspections.put("memberrebinding", ImmutableList.of(
+        MemberRebindingTest::inspectMain,
+        MemberRebindingTest::inspectOriginalMain));
+    inspections.put("memberrebinding2", ImmutableList.of(
+        MemberRebindingTest::inspectMain2,
+        MemberRebindingTest::inspectOriginalMain2));
+    inspections.put("memberrebinding3", ImmutableList.of(
+        MemberRebindingTest::inspect3,
+        MemberRebindingTest::inspectOriginal3));
+
+    List<Object[]> testCases = new ArrayList<>();
+    Set<String> usedInspections = new HashSet<>();
+
+    for (String test : tests) {
+      List<Consumer<DexInspector>> inspection = inspections.get(test);
+      assert inspection != null;
+      usedInspections.add(test);
+      testCases.add(new Object[]{test, Frontend.JAR, inspection.get(0), inspection.get(1)});
+      testCases.add(new Object[]{test, Frontend.DEX, inspection.get(0), inspection.get(1)});
+    }
+
+    assert usedInspections.size() == inspections.size();
+
+    return testCases;
+  }
+
+  @Test
+  public void memberRebindingTest() throws IOException, InterruptedException, ExecutionException {
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+    String out = temp.getRoot().getCanonicalPath();
+    Path processed = Paths.get(out, "classes.dex");
+
+    if (kind == Frontend.DEX) {
+      DexInspector inspector = new DexInspector(originalDex);
+      originalInspection.accept(inspector);
+    }
+
+    DexInspector inspector = new DexInspector(processed);
+    inspection.accept(inspector);
+
+    // We don't run Art, as the test R8RunExamplesTest already does that.
+    // ToolHelper.checkArtOutputIdentical(originalDex, processed, mainClass, null);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
new file mode 100644
index 0000000..dccfd8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.ToolHelper;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ProguardMapReaderTest {
+
+  public static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
+  public static final String EXAMPLE_MAP = "throwing/throwing.map";
+
+  public static final String EXAMPLE_MAP_WITH_PACKAGE_INFO =
+      "dagger.android.package-info -> dagger.android.package-info\n";
+
+  @Test
+  public void parseThrowingMap() throws IOException {
+    ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+  }
+
+  @Test
+  public void roundTripTest() throws IOException {
+    ClassNameMapper firstMapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+    ClassNameMapper secondMapper = ProguardMapReader.mapperFromString(firstMapper.toString());
+    Assert.assertEquals(firstMapper, secondMapper);
+  }
+
+  @Test
+  public void parseMapWithPackageInfo() throws IOException {
+    ClassNameMapper mapper = ProguardMapReader.mapperFromString(EXAMPLE_MAP_WITH_PACKAGE_INFO);
+    Assert.assertTrue(mapper.getObfuscatedToOriginalMapping().isEmpty());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java b/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
new file mode 100644
index 0000000..72b452f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/R8DebugStrippingTest.java
@@ -0,0 +1,234 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.optimize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.InlineInformation;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.io.Closer;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8DebugStrippingTest {
+
+  private static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
+  private static final String EXAMPLE_DEX = "throwing/classes.dex";
+  private static final String EXAMPLE_MAP = "throwing/throwing.map";
+  private static final String EXAMPLE_CLASS = "throwing.Throwing";
+  private static final String EXAMPLE_JAVA = "Throwing.java";
+
+  private static final String MAIN_NAME = "main";
+  private static final String[] MAIN_PARAMETERS = new String[]{"java.lang.String[]"};
+  private static final String VOID_RETURN = "void";
+
+  private static final String OTHER_NAME = "throwInAFunctionThatIsNotInlinedAndCalledTwice";
+  private static final String[] NO_PARAMETERS = new String[0];
+  private static final String INT_RETURN = "int";
+
+  private static final String THIRD_NAME = "aFunctionThatCallsAnInlinedMethodThatThrows";
+  private static final String[] LIST_PARAMETER = new String[]{"java.util.List"};
+
+  private static final String FORTH_NAME = "anotherFunctionThatCallsAnInlinedMethodThatThrows";
+  private static final String[] STRING_PARAMETER = new String[]{"java.lang.String"};
+
+  private static final Map<String, Signature> SIGNATURE_MAP = ImmutableMap.of(
+      MAIN_NAME, new MethodSignature(MAIN_NAME, VOID_RETURN, MAIN_PARAMETERS),
+      OTHER_NAME, new MethodSignature(OTHER_NAME, INT_RETURN, NO_PARAMETERS),
+      THIRD_NAME, new MethodSignature(THIRD_NAME, INT_RETURN, LIST_PARAMETER),
+      FORTH_NAME, new MethodSignature(FORTH_NAME, INT_RETURN, STRING_PARAMETER)
+  );
+
+  private ClassNameMapper mapper;
+
+  @Parameter(0)
+  public boolean compressRanges;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Before
+  public void loadRangeInformation() throws IOException {
+    mapper = ProguardMapReader.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+  }
+
+  @Parameters(name = "compressLineNumers={0}")
+  public static Object[] parameters() {
+    return new Object[]{true, false};
+  }
+
+  @Test
+  public void testStackTraces()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+
+    // Temporary directory for R8 output.
+    Path out = temp.getRoot().toPath();
+
+    R8Command command =
+        R8Command.builder()
+            .addProgramFiles(Paths.get(ROOT, EXAMPLE_DEX))
+            .setOutputPath(out)
+            .setProguardMapFile(Paths.get(ROOT, EXAMPLE_MAP))
+            .build();
+
+    // Generate R8 processed version.
+    AndroidApp result =
+        ToolHelper.runR8(command, (options) -> options.skipDebugLineNumberOpt = !compressRanges);
+
+    ClassNameMapper classNameMapper;
+    try (Closer closer = Closer.create()) {
+      classNameMapper = ProguardMapReader.mapperFromInputStream(result.getProguardMap(closer));
+    }
+    if (compressRanges) {
+      classNameMapper.forAllClassNamings(this::ensureRangesAreUniquePerClass);
+    }
+
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+    // Run art on original.
+    String originalOutput =
+        ToolHelper.runArtNoVerificationErrors(ROOT + EXAMPLE_DEX, EXAMPLE_CLASS);
+    // Run art on R8 processed version.
+    String otherOutput =
+        ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", EXAMPLE_CLASS);
+    // Check that exceptions are in same range
+    assertStacktracesMatchRanges(originalOutput, otherOutput, classNameMapper);
+
+    // Check that we have debug information in all the places required.
+    DexInspector inspector = new DexInspector(out.resolve("classes.dex"));
+    BiMap<String, String> obfuscationMap
+        = classNameMapper.getObfuscatedToOriginalMapping().inverse();
+    ClassSubject overloaded = inspector.clazz(obfuscationMap.get("throwing.Overloaded"));
+    assertTrue(overloaded.isPresent());
+    ensureDebugInfosExist(overloaded);
+  }
+
+  private void ensureDebugInfosExist(ClassSubject overloaded) {
+    final Map<DexString, Boolean> hasDebugInfo = Maps.newIdentityHashMap();
+    overloaded.forAllMethods(method -> {
+          if (!method.isAbstract()) {
+            DexCode code = method.getMethod().getCode().asDexCode();
+            DexString name = method.getMethod().method.name;
+            Boolean previous = hasDebugInfo.get(name);
+            boolean current = code.getDebugInfo() != null;
+            // If we have seen one before, it should be the same as now.
+            assertTrue(previous == null || (previous == current));
+            hasDebugInfo.put(name, current);
+          }
+        }
+    );
+  }
+
+  private void ensureRangesAreUniquePerClass(ClassNaming naming) {
+    final Map<String, Set<Integer>> rangeMap = new HashMap<>();
+    naming.forAllMemberNaming(memberNaming -> {
+      if (memberNaming.isMethodNaming()) {
+        if (memberNaming.topLevelRange != MemberNaming.fakeZeroRange) {
+          int startLine = memberNaming.topLevelRange.from;
+          Set<Integer> used = rangeMap
+              .computeIfAbsent(memberNaming.getRenamedName(), any -> new HashSet<>());
+          assertFalse(used.contains(startLine));
+          used.add(startLine);
+        }
+      }
+    });
+  }
+
+  private String extractRangeIndex(String line, ClassNameMapper mapper) {
+    int position = line.lastIndexOf(EXAMPLE_JAVA);
+    assertNotSame("Malformed stackframe: " + line, -1, position);
+    String numberPart = line.substring(position + EXAMPLE_JAVA.length() + 1, line.lastIndexOf(')'));
+    int number = Integer.parseInt(numberPart);
+    // Search the signature map for all signatures that actually match. We do this by first looking
+    // up the renamed signature and then checking whether it is contained in the line. We prepend
+    // the class name to make sure we do not match random characters in the line.
+    for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
+      MemberNaming naming = mapper.getClassNaming(EXAMPLE_CLASS)
+          .lookupByOriginalSignature(entry.getValue());
+      if (!line.contains(EXAMPLE_CLASS + "." + naming.getRenamedName())) {
+        continue;
+      }
+      if (naming.topLevelRange.contains(number)) {
+        return entry.getKey() + ":" + 0;
+      }
+      int rangeNo = 1;
+      for (InlineInformation inlineInformation : naming.inlineInformation) {
+        if (inlineInformation.inlinedRange.contains(number)) {
+          return entry.getKey() + ":" + rangeNo;
+        }
+        rangeNo++;
+      }
+    }
+    fail("Number not in any range " + number);
+    return null;
+  }
+
+  private MemberNaming selectRanges(String line, ClassNameMapper mapper) {
+    Signature signature;
+    for (Entry<String, Signature> entry : SIGNATURE_MAP.entrySet()) {
+      if (line.contains(entry.getKey())) {
+        return mapper.getClassNaming(EXAMPLE_CLASS).lookup(entry.getValue());
+      }
+    }
+    Assert.fail("unknown method in line " + line);
+    return null;
+  }
+
+  private void assertStacktracesMatchRanges(String before, String after,
+      ClassNameMapper newMapper) {
+    String[] beforeLines = before.split("\n");
+    String[] afterLines = after.split("\n");
+    assertEquals("Output length differs", beforeLines.length,
+        afterLines.length);
+    for (int i = 0; i < beforeLines.length; i++) {
+      if (!beforeLines[i].startsWith("FRAME:")) {
+        continue;
+      }
+      String beforeLine = beforeLines[i];
+      String expected = extractRangeIndex(beforeLine, mapper);
+      String afterLine = afterLines[i];
+      String generated = extractRangeIndex(afterLine, newMapper);
+      assertEquals("Ranges match", expected, generated);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
new file mode 100644
index 0000000..5258b31
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -0,0 +1,146 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.regress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.smali.SmaliTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import java.util.Base64;
+import java.util.List;
+import org.junit.Test;
+
+public class Regress37740372 extends SmaliTestBase {
+
+  /*
+    Binary class file data for a java.lang.Object stub implementation. Runnong javap -c on the
+    bytecode produces the following output:
+
+    Compiled from "Object.java"
+    public class java.lang.Object {
+      public java.lang.Object();
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      protected java.lang.Object clone() throws java.lang.CloneNotSupportedException;
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      public boolean equals(java.lang.Object);
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      protected void finalize() throws java.lang.Throwable;
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      public final native java.lang.Class<?> getClass();
+
+      public native int hashCode();
+
+      public final native void notify();
+
+      public final native void notifyAll();
+
+      public java.lang.String toString();
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      public final void wait() throws java.lang.InterruptedException;
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      public final void wait(long) throws java.lang.InterruptedException;
+        Code:
+           0: new           #1                  // class java/lang/RuntimeException
+           3: dup
+           4: ldc           #2                  // String Stub!
+           6: invokespecial #3                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+           9: athrow
+
+      public final native void wait(long, int) throws java.lang.InterruptedException;
+    }
+  */
+  String javaLangObjectClassFile =
+      "yv66vgAAADEALwcAJwgAKAoAAQApBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
+          "ZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABJMamF2YS9sYW5nL09iamVjdDsB" +
+          "AAVjbG9uZQEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAKRXhjZXB0aW9ucwcAKwEABmVxdWFscwEA" +
+          "FShMamF2YS9sYW5nL09iamVjdDspWgEAAW8BAAhmaW5hbGl6ZQcALAEACGdldENsYXNzAQATKClM" +
+          "amF2YS9sYW5nL0NsYXNzOwEACVNpZ25hdHVyZQEAFigpTGphdmEvbGFuZy9DbGFzczwqPjsBAAho" +
+          "YXNoQ29kZQEAAygpSQEABm5vdGlmeQEACW5vdGlmeUFsbAEACHRvU3RyaW5nAQAUKClMamF2YS9s" +
+          "YW5nL1N0cmluZzsBAAR3YWl0BwAtAQAEKEopVgEABm1pbGxpcwEAAUoBAAUoSkkpVgEAClNvdXJj" +
+          "ZUZpbGUBAAtPYmplY3QuamF2YQEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uAQAFU3R1YiEM" +
+          "AAUALgEAEGphdmEvbGFuZy9PYmplY3QBACRqYXZhL2xhbmcvQ2xvbmVOb3RTdXBwb3J0ZWRFeGNl" +
+          "cHRpb24BABNqYXZhL2xhbmcvVGhyb3dhYmxlAQAeamF2YS9sYW5nL0ludGVycnVwdGVkRXhjZXB0" +
+          "aW9uAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABAAAAAAAAAAMAAEABQAGAAEABwAAADQAAwAB" +
+          "AAAACrsAAVkSArcAA78AAAACAAgAAAAGAAEAAAAEAAkAAAAMAAEAAAAKAAoACwAAAAQADAANAAIA" +
+          "BwAAADQAAwABAAAACrsAAVkSArcAA78AAAACAAgAAAAGAAEAAAAFAAkAAAAMAAEAAAAKAAoACwAA" +
+          "AA4AAAAEAAEADwABABAAEQABAAcAAAA+AAMAAgAAAAq7AAFZEgK3AAO/AAAAAgAIAAAABgABAAAA" +
+          "BgAJAAAAFgACAAAACgAKAAsAAAAAAAoAEgALAAEABAATAAYAAgAHAAAANAADAAEAAAAKuwABWRIC" +
+          "twADvwAAAAIACAAAAAYAAQAAAAcACQAAAAwAAQAAAAoACgALAAAADgAAAAQAAQAUAREAFQAWAAEA" +
+          "FwAAAAIAGAEBABkAGgAAAREAGwAGAAABEQAcAAYAAAABAB0AHgABAAcAAAA0AAMAAQAAAAq7AAFZ" +
+          "EgK3AAO/AAAAAgAIAAAABgABAAAADAAJAAAADAABAAAACgAKAAsAAAARAB8ABgACAAcAAAA0AAMA" +
+          "AQAAAAq7AAFZEgK3AAO/AAAAAgAIAAAABgABAAAADQAJAAAADAABAAAACgAKAAsAAAAOAAAABAAB" +
+          "ACAAEQAfACEAAgAHAAAAPgADAAMAAAAKuwABWRICtwADvwAAAAIACAAAAAYAAQAAAA4ACQAAABYA" +
+          "AgAAAAoACgALAAAAAAAKACIAIwABAA4AAAAEAAEAIAERAB8AJAABAA4AAAAEAAEAIAABACUAAAAC" +
+          "ACY=";
+
+  private void assertIsJavaLangObjet(ClassSubject clazz) {
+    assertTrue(clazz.getOriginalDescriptor().equals("Ljava/lang/Object;"));
+    assertNull(clazz.getDexClass().superType);
+  }
+
+  private void checkApplicationOnlyHasJavaLangObject(AndroidApp app) throws Throwable {
+    DexInspector inspector = new DexInspector(app);
+    inspector.forAllClasses(this::assertIsJavaLangObjet);
+  }
+
+  @Test
+  public void test() throws Throwable {
+    // Build an application with the java.lang.Object stub from a class file.
+    AndroidApp output =
+        ToolHelper.runD8(
+            D8Command.builder()
+                .addClassProgramData(Base64.getDecoder().decode(javaLangObjectClassFile))
+                .build());
+    checkApplicationOnlyHasJavaLangObject(output);
+
+    // Build an application with the java.lang.Object stub from a dex file.
+    List<byte[]> dex = output.writeToMemory();
+    assertEquals(1, dex.size());
+    checkApplicationOnlyHasJavaLangObject(
+        ToolHelper.runD8(D8Command.builder().addDexProgramData(dex).build()));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
new file mode 100644
index 0000000..5fb3007
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.rewrite.longcompare;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+/**
+ * Tests checking that Long.compare() is always rewritten into long compare instruction.
+ */
+public class LongCompare {
+
+  @Rule
+  public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
+
+  void compileWithD8(Path intputPath, Path outputPath)
+      throws IOException, CompilationException {
+    D8.run(D8Command.builder().addProgramFiles(intputPath).setOutputPath(outputPath).build());
+  }
+
+  void runTest(Path dexFile) {
+    ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
+    builder.appendClasspath(dexFile.toString());
+    builder.setMainClass("rewrite.LongCompare");
+    try {
+      String output = ToolHelper.runArt(builder);
+      Assert.assertEquals("-1\n1\n-1\n0\ntrue\nfalse\nfalse\nfalse\n", output);
+    } catch (IOException e) {
+      Assert.fail();
+    }
+  }
+
+  @Test
+  public void testLongCompareIsRewritten()
+      throws IOException, CompilationException, ExecutionException {
+    final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+
+    compileWithD8(inputPath, outputPath);
+
+    Path dexPath = outputPath.resolve("classes.dex");
+
+    DexInspector dexInspector = new DexInspector(dexPath);
+    ClassSubject classSubject = dexInspector.clazz("rewrite.LongCompare");
+    MethodSubject methodSubject = classSubject
+        .method("int", "simpleCompare", Arrays.asList("long", "long"));
+    // Check that exception handler is removed since it is no longer needed.
+    Assert.assertFalse(methodSubject.getMethod().getCode().asDexCode().usesExceptionHandling());
+    Iterator<InvokeInstructionSubject> iterator =
+        methodSubject.iterateInstructions(InstructionSubject::isInvoke);
+    Assert.assertFalse(iterator.hasNext());
+
+    runTest(dexPath);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
new file mode 100644
index 0000000..4cbae16
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
@@ -0,0 +1,94 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.rewrite.longcompare;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class RequireNonNullRewriteTest {
+
+  @Rule
+  public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
+
+  void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
+      throws IOException, CompilationException {
+    D8.run(
+        D8Command.builder()
+            .setMode(mode)
+            .addProgramFiles(intputPath)
+            .setOutputPath(outputPath)
+            .build());
+  }
+
+  void runTest(Path jarFile, Path dexFile) {
+    String mainClass = "rewrite.RequireNonNull";
+    ArtCommandBuilder builder = new ArtCommandBuilder(ToolHelper.getDexVm());
+    builder.appendClasspath(dexFile.toString());
+    builder.setMainClass(mainClass);
+    try {
+      String output = ToolHelper.runArt(builder);
+      ProcessResult javaResult = ToolHelper
+          .runJava(ImmutableList.of(jarFile.toString()), mainClass);
+      Assert.assertEquals(javaResult.stdout, output);
+    } catch (IOException e) {
+      Assert.fail();
+    }
+  }
+
+  @Test
+  public void testDebugRequireNonNullIsRewritten()
+      throws IOException, CompilationException, ExecutionException {
+    runTest(CompilationMode.DEBUG);
+  }
+
+  @Test
+  public void testReleaseRequireNonNullIsRewritten()
+      throws IOException, CompilationException, ExecutionException {
+    runTest(CompilationMode.RELEASE);
+  }
+
+  private void runTest(CompilationMode mode)
+      throws IOException, CompilationException, ExecutionException {
+    final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
+    Path outputPath = tmpOutputDir.newFolder().toPath();
+
+    compileWithD8(inputPath, outputPath, mode);
+
+    Path dexPath = outputPath.resolve("classes.dex");
+
+    DexInspector dexInspector = new DexInspector(dexPath);
+    ClassSubject classSubject = dexInspector.clazz("rewrite.RequireNonNull");
+    MethodSubject methodSubject = classSubject
+        .method("java.lang.Object", "nonnullRemove", Collections.emptyList());
+
+    Iterator<InvokeInstructionSubject> iterator =
+        methodSubject.iterateInstructions(InstructionSubject::isInvoke);
+    Assert.assertTrue(iterator.hasNext());
+    Assert.assertEquals("getClass", iterator.next().invokedMethod().name.toString());
+    Assert.assertFalse(iterator.hasNext());
+
+    runTest(inputPath, dexPath);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
new file mode 100644
index 0000000..2362028
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -0,0 +1,334 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexItemFactory;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class ProguardConfigurationParserTest {
+
+  private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/";
+  private static final String INVALID_PROGUARD_DIR = "src/test/proguard/invalid/";
+  private static final String PROGUARD_SPEC_FILE = VALID_PROGUARD_DIR + "proguard.flags";
+  private static final String MULTIPLE_NAME_PATTERNS_FILE =
+      VALID_PROGUARD_DIR + "multiple-name-patterns.flags";
+  private static final String ACCESS_FLAGS_FILE = VALID_PROGUARD_DIR + "access-flags.flags";
+  private static final String WHY_ARE_YOU_KEEPING_FILE =
+      VALID_PROGUARD_DIR + "why-are-you-keeping.flags";
+  private static final String ASSUME_NO_SIDE_EFFECTS =
+      VALID_PROGUARD_DIR + "assume-no-side-effects.flags";
+  private static final String ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE =
+      VALID_PROGUARD_DIR + "assume-no-side-effects-with-return-value.flags";
+  private static final String ASSUME_VALUES_WITH_RETURN_VALUE =
+      VALID_PROGUARD_DIR + "assume-values-with-return-value.flags";
+  private static final String INCLUDING =
+      VALID_PROGUARD_DIR + "including.flags";
+  private static final String INVALID_INCLUDING_1 =
+      INVALID_PROGUARD_DIR + "including-1.flags";
+  private static final String INVALID_INCLUDING_2 =
+      INVALID_PROGUARD_DIR + "including-2.flags";
+  private static final String LIBRARY_JARS =
+      VALID_PROGUARD_DIR + "library-jars.flags";
+  private static final String SEEDS =
+      VALID_PROGUARD_DIR + "seeds.flags";
+  private static final String SEEDS_2 =
+      VALID_PROGUARD_DIR + "seeds-2.flags";
+  private static final String VERBOSE =
+      VALID_PROGUARD_DIR + "verbose.flags";
+  private static final String DONT_OBFUSCATE =
+      VALID_PROGUARD_DIR + "dontobfuscate.flags";
+  private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES =
+      VALID_PROGUARD_DIR + "dontskipnonpubliclibraryclasses.flags";
+  private static final String DONT_OPTIMIZE =
+      VALID_PROGUARD_DIR + "dontoptimize.flags";
+  private static final String SKIP_NON_PUBLIC_LIBRARY_CLASSES =
+      VALID_PROGUARD_DIR + "skipnonpubliclibraryclasses.flags";
+  private static final String PARSE_AND_SKIP_SINGLE_ARGUMENT =
+      VALID_PROGUARD_DIR + "parse-and-skip-single-argument.flags";
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void parse() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(PROGUARD_SPEC_FILE));
+    List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+    assertEquals(24, rules.size());
+    assertEquals(1, rules.get(0).getMemberRules().size());
+  }
+
+  @Test
+  public void parseMultipleNamePatterns() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(MULTIPLE_NAME_PATTERNS_FILE));
+    List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+    assertEquals(1, rules.size());
+    ProguardConfigurationRule rule = rules.get(0);
+    assertEquals(1, rule.getMemberRules().size());
+    assertEquals("com.company.hello.**", rule.getClassNames().get(0).toString());
+    assertEquals("com.company.world.**", rule.getClassNames().get(1).toString());
+    assertEquals(ProguardKeepRuleType.KEEP, ((ProguardKeepRule) rule).getType());
+    assertTrue(rule.getInheritanceIsExtends());
+    assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
+    ProguardMemberRule memberRule = rule.getMemberRules().iterator().next();
+    assertTrue(memberRule.getAccessFlags().isProtected());
+    assertEquals(ProguardNameMatcher.create("getContents"), memberRule.getName());
+    assertEquals("java.lang.Object[][]", memberRule.getType().toString());
+    assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
+    assertEquals(0, memberRule.getArguments().size());
+  }
+
+  @Test
+  public void parseAccessFlags() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(ACCESS_FLAGS_FILE));
+    List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+    assertEquals(1, rules.size());
+    ProguardConfigurationRule rule = rules.get(0);
+    DexAccessFlags publicAndFinalFlags = new DexAccessFlags(0);
+    publicAndFinalFlags.setPublic();
+    publicAndFinalFlags.setFinal();
+    assertTrue(rule.getClassAccessFlags().containsNoneOf(publicAndFinalFlags));
+    assertTrue(rule.getNegatedClassAccessFlags().containsAllOf(publicAndFinalFlags));
+    DexAccessFlags abstractFlags = new DexAccessFlags(0);
+    abstractFlags.setAbstract();
+    assertTrue(rule.getClassAccessFlags().containsAllOf(abstractFlags));
+    assertTrue(rule.getNegatedClassAccessFlags().containsNoneOf(abstractFlags));
+    for (ProguardMemberRule member : rule.getMemberRules()) {
+      if (member.getRuleType() == ProguardMemberType.ALL_FIELDS) {
+        DexAccessFlags publicFlags = new DexAccessFlags(0);
+        publicAndFinalFlags.setPublic();
+        assertTrue(member.getAccessFlags().containsAllOf(publicFlags));
+        assertTrue(member.getNegatedAccessFlags().containsNoneOf(publicFlags));
+        DexAccessFlags staticFlags = new DexAccessFlags(0);
+        staticFlags.setStatic();
+        assertTrue(member.getAccessFlags().containsNoneOf(staticFlags));
+        assertTrue(member.getNegatedAccessFlags().containsAllOf(staticFlags));
+      } else {
+        assertTrue(member.getRuleType() == ProguardMemberType.ALL_METHODS);
+        DexAccessFlags publicProtectedVolatileFlags = new DexAccessFlags(0);
+        publicProtectedVolatileFlags.setPublic();
+        publicProtectedVolatileFlags.setProtected();
+        publicProtectedVolatileFlags.setVolatile();
+        assertTrue(member.getAccessFlags().containsNoneOf(publicProtectedVolatileFlags));
+        assertTrue(member.getNegatedAccessFlags().containsAllOf(publicProtectedVolatileFlags));
+      }
+    }
+  }
+
+  @Test
+  public void parseWhyAreYouKeeping() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(WHY_ARE_YOU_KEEPING_FILE));
+    List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+    assertEquals(1, rules.size());
+    ProguardConfigurationRule rule = rules.get(0);
+    assertEquals(1, rule.getClassNames().size());
+    assertEquals("*", rule.getClassNames().get(0).toString());
+    assertTrue(rule.getInheritanceIsExtends());
+    assertEquals("foo.bar", rule.getInheritanceClassName().toString());
+  }
+
+  @Test
+  public void parseAssumeNoSideEffects() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS));
+    List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules();
+    assertEquals(1, assumeNoSideEffects.size());
+    assumeNoSideEffects.get(0).getMemberRules().forEach(rule -> {
+      assertFalse(rule.hasReturnValue());
+    });
+  }
+
+  @Test
+  public void parseAssumeNoSideEffectsWithReturnValue()
+      throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(ASSUME_NO_SIDE_EFFECTS_WITH_RETURN_VALUE));
+    List<ProguardConfigurationRule> assumeNoSideEffects = parser.getConfig().getRules();
+    assertEquals(1, assumeNoSideEffects.size());
+    assumeNoSideEffects.get(0).getMemberRules().forEach(rule -> {
+      assertTrue(rule.hasReturnValue());
+      if (rule.getName().matches("returnsTrue") || rule.getName().matches("returnsFalse")) {
+        assertTrue(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean());
+      } else if (rule.getName().matches("returns1")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertTrue(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertTrue(rule.getReturnValue().isSingleValue());
+        assertEquals(1, rule.getReturnValue().getValueRange().getMin());
+        assertEquals(1, rule.getReturnValue().getValueRange().getMax());
+        assertEquals(1, rule.getReturnValue().getSingleValue());
+      } else if (rule.getName().matches("returns2To4")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertTrue(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isSingleValue());
+        assertEquals(2, rule.getReturnValue().getValueRange().getMin());
+        assertEquals(4, rule.getReturnValue().getValueRange().getMax());
+      } else if (rule.getName().matches("returnsField")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertTrue(rule.getReturnValue().isField());
+        assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
+        assertEquals("int", rule.getReturnValue().getField().type.toString());
+        assertEquals("X", rule.getReturnValue().getField().name.toString());
+      }
+    });
+  }
+
+  @Test
+  public void parseAssumeValuesWithReturnValue()
+      throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(ASSUME_VALUES_WITH_RETURN_VALUE));
+    List<ProguardConfigurationRule> assumeValues = parser.getConfig().getRules();
+    assertEquals(1, assumeValues.size());
+    assumeValues.get(0).getMemberRules().forEach(rule -> {
+      assertTrue(rule.hasReturnValue());
+      if (rule.getName().matches("isTrue") || rule.getName().matches("isFalse")) {
+        assertTrue(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean());
+      } else if (rule.getName().matches("is1")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertTrue(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertTrue(rule.getReturnValue().isSingleValue());
+        assertEquals(1, rule.getReturnValue().getValueRange().getMin());
+        assertEquals(1, rule.getReturnValue().getValueRange().getMax());
+        assertEquals(1, rule.getReturnValue().getSingleValue());
+      } else if (rule.getName().matches("is2To4")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertTrue(rule.getReturnValue().isValueRange());
+        assertFalse(rule.getReturnValue().isField());
+        assertFalse(rule.getReturnValue().isSingleValue());
+        assertEquals(2, rule.getReturnValue().getValueRange().getMin());
+        assertEquals(4, rule.getReturnValue().getValueRange().getMax());
+      } else if (rule.getName().matches("isField")) {
+        assertFalse(rule.getReturnValue().isBoolean());
+        assertFalse(rule.getReturnValue().isValueRange());
+        assertTrue(rule.getReturnValue().isField());
+        assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
+        assertEquals("int", rule.getReturnValue().getField().type.toString());
+        assertEquals("X", rule.getReturnValue().getField().name.toString());
+      }
+    });
+  }
+
+  @Test
+  public void parseDontobfuscate() throws IOException, ProguardRuleParserException {
+    new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(DONT_OBFUSCATE));
+  }
+
+  @Test
+  public void parseIncluding() throws IOException, ProguardRuleParserException {
+    new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INCLUDING));
+  }
+
+  @Test
+  public void parseInvalidIncluding1() throws IOException {
+    try {
+      new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INVALID_INCLUDING_1));
+      fail();
+    } catch (ProguardRuleParserException e) {
+      assertTrue(e.getMessage().contains("6")); // line
+      assertTrue(e.getMessage().contains("including-1.flags")); // file in error
+      assertTrue(e.getMessage().contains("does-not-exist.flags")); // missing file
+    }
+  }
+
+  @Test
+  public void parseInvalidIncluding2() throws IOException {
+    try {
+      new ProguardConfigurationParser(new DexItemFactory()).parse(Paths.get(INVALID_INCLUDING_2));
+      fail();
+    } catch (ProguardRuleParserException e) {
+      String message = e.getMessage();
+      assertTrue(message, message.contains("6")); // line
+      assertTrue(message, message.contains("including-2.flags")); // file in error
+      assertTrue(message, message.contains("does-not-exist.flags")); // missing file
+    }
+  }
+
+  @Test
+  public void parseLibraryJars() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(LIBRARY_JARS));
+    assertEquals(4, parser.getConfig().getLibraryjars().size());
+  }
+
+  @Test
+  public void parseSeeds() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(SEEDS));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.getPrintSeeds());
+    assertNull(config.getSeedFile());
+  }
+
+  @Test
+  public void parseSeeds2() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(SEEDS_2));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.getPrintSeeds());
+    assertNotNull(config.getSeedFile());
+  }
+
+  @Test
+  public void parseVerbose() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(VERBOSE));
+    ProguardConfiguration config = parser.getConfig();
+    assertTrue(config.isVerbose());
+  }
+
+  @Test
+  public void parseDontSkipNonPublicLibraryClasses()
+      throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+  }
+
+  @Test
+  public void parseDontOptimize()
+      throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(DONT_OPTIMIZE));
+  }
+
+  @Test
+  public void parseSkipNonPublicLibraryClasses()
+      throws IOException, ProguardRuleParserException {
+    thrown.expect(ProguardRuleParserException.class);
+    thrown.expectMessage("Unsupported option: -skipnonpubliclibraryclasses");
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+  }
+
+  @Test
+  public void parseAndskipSingleArgument() throws IOException, ProguardRuleParserException {
+    ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+    parser.parse(Paths.get(PARSE_AND_SKIP_SINGLE_ARGUMENT));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
new file mode 100644
index 0000000..c38c379
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
+import com.android.tools.r8.utils.DescriptorUtils;
+import org.junit.Test;
+
+public class ProguardNameMatchingTest {
+  private static final String[] BASIC_TYPES = {
+      "char", "byte", "short", "int", "long", "float", "double", "boolean"
+  };
+
+  private static final DexItemFactory dexItemFactory = new DexItemFactory();
+
+  private static boolean matchTypeName(String pattern, String typeName,
+      DexItemFactory dexItemFactory) {
+    return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
+        .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
+  }
+
+  private static boolean matchClassName(String pattern, String className,
+      DexItemFactory dexItemFactory) {
+    return ProguardTypeMatcher.create(pattern, ClassOrType.CLASS, dexItemFactory)
+        .matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(className)));
+  }
+
+  @Test
+  public void matchClassNames() {
+    assertTrue(matchClassName("**", "", dexItemFactory));
+    assertTrue(matchClassName("**", "a", dexItemFactory));
+    assertTrue(matchClassName("*", "", dexItemFactory));
+    assertTrue(matchClassName("*", "a", dexItemFactory));
+    assertFalse(matchClassName("?", "", dexItemFactory));
+    assertTrue(matchClassName("?", "a", dexItemFactory));
+
+    assertTrue(matchClassName("**", "java.lang.Object", dexItemFactory));
+    assertFalse(
+        matchClassName("ja*", "java.lang.Object", dexItemFactory));
+    assertTrue(
+        matchClassName("ja**", "java.lang.Object", dexItemFactory));
+    assertTrue(matchClassName("ja**ject", "java.lang.Object",
+        dexItemFactory));
+    // Oddly, the proguard specification for this makes a lonely * synonymous with **.
+    assertTrue(matchClassName("*", "java.lang.Object", dexItemFactory));
+    assertFalse(matchClassName("ja*ject", "java.lang.Object",
+        dexItemFactory));
+
+    assertTrue(matchClassName("java.*g.O*", "java.lang.Object",
+        dexItemFactory));
+    assertTrue(matchClassName("java.*g.O?je?t", "java.lang.Object",
+        dexItemFactory));
+    assertFalse(matchClassName("java.*g.O?je?t?", "java.lang.Object",
+        dexItemFactory));
+    assertFalse(matchClassName("java?lang.Object", "java.lang.Object",
+        dexItemFactory));
+    assertTrue(matchClassName("*a*.*a**", "java.lang.Object",
+        dexItemFactory));
+    assertTrue(matchClassName("*a**a**", "java.lang.Object",
+        dexItemFactory));
+  }
+
+  private void assertMatchesBasicTypes(String pattern) {
+    for (String type : BASIC_TYPES) {
+      assertTrue(matchTypeName(pattern, type, dexItemFactory));
+    }
+  }
+
+  private void assertDoesNotMatchBasicTypes(String pattern) {
+    for (String type : BASIC_TYPES) {
+      assertFalse(matchTypeName(pattern, type, dexItemFactory));
+    }
+  }
+
+  @Test
+  public void matchTypeNames() {
+    assertTrue(matchTypeName("**", "java.lang.Object", dexItemFactory));
+    assertDoesNotMatchBasicTypes("**");
+    assertDoesNotMatchBasicTypes("*");
+    assertFalse(
+        matchTypeName("**", "java.lang.Object[]", dexItemFactory));
+    assertFalse(
+        matchTypeName("**z", "java.lang.Object", dexItemFactory));
+    assertFalse(matchTypeName("java.**", "java.lang.Object[]",
+        dexItemFactory));
+    assertTrue(
+        matchTypeName("***", "java.lang.Object[]", dexItemFactory));
+    assertTrue(matchTypeName("***", "java.lang.Object[][]",
+        dexItemFactory));
+    assertFalse(matchTypeName("%", "java.lang.Object", dexItemFactory));
+    assertTrue(matchTypeName("**", "a", dexItemFactory));
+    assertTrue(matchTypeName("**[]", "java.lang.Object[]",
+        dexItemFactory));
+    assertFalse(matchTypeName("**[]", "java.lang.Object[][]",
+        dexItemFactory));
+    assertTrue(matchTypeName("*", "abc", dexItemFactory));
+    assertMatchesBasicTypes("***");
+    assertMatchesBasicTypes("%");
+  }
+
+  @Test
+  public void matchFieldOrMethodNames() {
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("*", ""));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("*", "get"));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("get*", "get"));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("get*", "getObject"));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("*t", "get"));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("g*t*", "getObject"));
+    assertTrue(
+        ProguardNameMatcher.matchFieldOrMethodName("g*t***************", "getObject"));
+    assertFalse(ProguardNameMatcher.matchFieldOrMethodName("get*y", "getObject"));
+    assertFalse(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject"));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject1"));
+    assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject5"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java b/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
new file mode 100644
index 0000000..be28b38
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/R8Shaking2LookupTest.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class R8Shaking2LookupTest {
+
+  static final String APP_FILE_NAME = ToolHelper.EXAMPLES_BUILD_DIR + "shaking2/classes.dex";
+  private DexApplication program;
+  private DexItemFactory dexItemFactory;
+  private AppInfoWithSubtyping appInfo;
+
+  @Before
+  public void readApp() throws IOException, ExecutionException {
+    program = ToolHelper.buildApplication(ImmutableList.of(APP_FILE_NAME));
+    dexItemFactory = program.dexItemFactory;
+    appInfo = new AppInfoWithSubtyping(program);
+  }
+
+  private void validateSubtype(DexType super_type, DexType sub_type) {
+    assertFalse(super_type.equals(sub_type));
+    assertTrue(appInfo.subtypes(super_type).contains(sub_type));
+    assertTrue(sub_type.isSubtypeOf(super_type, appInfo));
+    assertFalse(appInfo.subtypes(sub_type).contains(super_type));
+    assertFalse(super_type.isSubtypeOf(sub_type, appInfo));
+  }
+
+  private void validateSubtypeSize(DexType type, int size) {
+    assertEquals(size, appInfo.subtypes(type).size());
+  }
+
+  @Test
+  public void testLookup() {
+    DexType object_type = dexItemFactory.createType("Ljava/lang/Object;");
+
+    DexType interface_type = dexItemFactory.createType("Lshaking2/Interface;");
+    DexType superInterface1_type = dexItemFactory.createType("Lshaking2/SuperInterface1;");
+    DexType superInterface2_type = dexItemFactory.createType("Lshaking2/SuperInterface2;");
+
+    DexType superclass_type = dexItemFactory.createType("Lshaking2/SuperClass;");
+    DexType subClass1_type = dexItemFactory.createType("Lshaking2/SubClass1;");
+    DexType subClass2_type = dexItemFactory.createType("Lshaking2/SubClass2;");
+
+    validateSubtype(object_type, interface_type);
+    validateSubtypeSize(interface_type, 4);
+    validateSubtype(interface_type, superclass_type);
+    validateSubtype(superInterface1_type, subClass1_type);
+    validateSubtype(superInterface2_type, subClass2_type);
+    validateSubtype(superclass_type, subClass2_type);
+    validateSubtype(superInterface1_type, interface_type);
+    validateSubtype(superInterface2_type, interface_type);
+    validateSubtypeSize(subClass2_type, 0);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
new file mode 100644
index 0000000..f5b2b1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
+import static com.android.tools.r8.ToolHelper.EXAMPLES_DIR;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.CompilationError;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class TreeShakingSpecificTest {
+  private static final String VALID_PROGUARD_DIR = "src/test/proguard/valid/";
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void testIgnoreWarnings()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    // Generate R8 processed version without library option.
+    Path out = temp.getRoot().toPath();
+    String test = "shaking2";
+    Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
+    Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
+    Path ignoreWarnings = Paths.get(VALID_PROGUARD_DIR, "ignorewarnings.flags");
+    R8.run(
+        R8Command.builder()
+            .addProgramFiles(originalDex)
+            .setOutputPath(out)
+            .addProguardConfigurationFiles(keepRules, ignoreWarnings)
+            .build());
+  }
+
+  @Test
+  public void testMissingLibrary()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    // Generate R8 processed version without library option.
+    Path out = temp.getRoot().toPath();
+    String test = "shaking2";
+    Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
+    Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
+    thrown.expect(CompilationError.class);
+    thrown.expectMessage("Shrinking can't be performed because some library classes are missing");
+    R8.run(
+        R8Command.builder()
+            .addProgramFiles(originalDex)
+            .setOutputPath(out)
+            .addProguardConfigurationFiles(keepRules)
+            .build());
+  }
+
+  @Test
+  public void testPrintMapping()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    // Generate R8 processed version without library option.
+    String test = "shaking1";
+    Path out = temp.getRoot().toPath();
+    Path originalDex = Paths.get(EXAMPLES_BUILD_DIR, test, "classes.dex");
+    Path keepRules = Paths.get(EXAMPLES_DIR, test, "keep-rules.txt");
+
+    // Create a flags file in temp dir requesting dump of the mapping.
+    // The mapping file will be created alongside the flags file in temp dir.
+    Path printMapping = out.resolve("printmapping.flags");
+    try (PrintStream mapping = new PrintStream(printMapping.toFile())) {
+      mapping.println("-printmapping mapping.txt");
+    }
+
+    ToolHelper.runR8(
+        R8Command.builder()
+            .addProgramFiles(originalDex)
+            .setOutputPath(out)
+            .addProguardConfigurationFiles(keepRules, printMapping)
+            .build());
+    Path outputmapping = out.resolve("mapping.txt");
+    String actualMapping;
+    actualMapping = new String(Files.readAllBytes(outputmapping), StandardCharsets.UTF_8);
+    String refMapping = new String(Files.readAllBytes(
+        Paths.get(EXAMPLES_DIR, "shaking1", "print-mapping.ref")), StandardCharsets.UTF_8);
+    Assert.assertEquals(refMapping, actualMapping);
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
new file mode 100644
index 0000000..6e03c0e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -0,0 +1,783 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
+import com.android.tools.r8.utils.DexInspector.FieldSubject;
+import com.android.tools.r8.utils.DexInspector.FoundFieldSubject;
+import com.android.tools.r8.utils.DexInspector.FoundMethodSubject;
+import com.android.tools.r8.utils.DexInspector.InstructionSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingTest {
+
+  private static final String ANDROID_JAR = ToolHelper.getDefaultAndroidJar();
+  private static final List<String> JAR_LIBRARIES = ImmutableList
+      .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib.jar");
+  private static final List<String> DEX_LIBRARIES = ImmutableList
+      .of(ANDROID_JAR, ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib/classes.dex");
+  private static final String EMPTY_FLAGS = "src/test/proguard/valid/empty.flags";
+  private static Set<String> IGNORED = ImmutableSet.of(
+      // there's no point in running those without obfuscation
+      "shaking1:keep-rules-repackaging.txt:DEX:false",
+      "shaking1:keep-rules-repackaging.txt:JAR:false",
+      "shaking16:keep-rules-1.txt:DEX:false",
+      "shaking16:keep-rules-1.txt:JAR:false",
+      "shaking16:keep-rules-2.txt:DEX:false",
+      "shaking16:keep-rules-2.txt:JAR:false",
+      "shaking15:keep-rules.txt:DEX:false",
+      "shaking15:keep-rules.txt:JAR:false"
+  );
+  private final boolean minify;
+
+
+  private enum Frontend {
+    DEX, JAR
+  }
+
+  private final Frontend kind;
+  private final String originalDex;
+  private final String programFile;
+  private final String mainClass;
+  private final List<String> keepRulesFiles;
+  private final Consumer<DexInspector> inspection;
+  private final BiConsumer<String, String> outputComparator;
+  private BiConsumer<DexInspector, DexInspector> dexComparator;
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  public TreeShakingTest(String test, Frontend kind, String mainClass, List<String> keepRulesFiles,
+      boolean minify, Consumer<DexInspector> inspection,
+      BiConsumer<String, String> outputComparator,
+      BiConsumer<DexInspector, DexInspector> dexComparator) {
+    this.kind = kind;
+    originalDex = ToolHelper.EXAMPLES_BUILD_DIR + test + "/classes.dex";
+    if (kind == Frontend.DEX) {
+      this.programFile = originalDex;
+    } else {
+      this.programFile = ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar";
+    }
+    this.mainClass = mainClass;
+    this.keepRulesFiles = keepRulesFiles;
+    this.inspection = inspection;
+    this.minify = minify;
+    this.outputComparator = outputComparator;
+    this.dexComparator = dexComparator;
+  }
+
+  @Before
+  public void generateTreeShakedVersion()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    // Generate R8 processed version without library option.
+    Path out = temp.getRoot().toPath();
+    List<String> libs = kind == Frontend.DEX ? DEX_LIBRARIES : JAR_LIBRARIES;
+    boolean inline = programFile.contains("inlining");
+
+    R8Command command =
+        R8Command.builder()
+            .setOutputPath(out)
+            .addProgramFiles(Paths.get(programFile))
+            .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get))
+            .addLibraryFiles(ListUtils.map(libs, Paths::get))
+            .setMinification(minify)
+            .build();
+    ToolHelper.runR8(command, options -> options.inlineAccessors = inline);
+  }
+
+  public static void shaking1HasNoClassUnused(DexInspector inspector) {
+    Assert.assertFalse(inspector.clazz("shaking1.Unused").isPresent());
+    ClassSubject used = inspector.clazz("shaking1.Used");
+    Assert.assertTrue(used.isPresent());
+    Assert.assertTrue(
+        used.method("java.lang.String", "aMethodThatIsNotUsedButKept", Collections.emptyList())
+            .isPresent());
+    Assert.assertTrue(used.field("int", "aStaticFieldThatIsNotUsedButKept").isPresent());
+  }
+
+  public static void shaking1IsCorrectlyRepackaged(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> {
+      String descriptor = clazz.getFinalDescriptor();
+      Assert.assertTrue(descriptor,
+          DescriptorUtils.getSimpleClassNameFromDescriptor(descriptor).equals("Shaking")
+              || DescriptorUtils.getPackageNameFromDescriptor(descriptor).equals("repackaged"));
+    });
+  }
+
+  private static void shaking2SuperClassIsAbstract(DexInspector inspector) {
+    ClassSubject clazz = inspector.clazz("shaking2.SuperClass");
+    Assert.assertTrue(clazz.isAbstract());
+    Assert.assertTrue(clazz.method("void", "virtualMethod", Collections.emptyList()).isAbstract());
+    Assert.assertTrue(clazz.method("void", "virtualMethod2", ImmutableList
+        .of("int", "int", "int", "int", "int", "int", "int", "int")).isAbstract());
+  }
+
+  public static void shaking3HasNoClassB(DexInspector inspector) {
+    Assert.assertFalse(inspector.clazz("shaking3.B").isPresent());
+    ClassSubject classA = inspector.clazz("shaking3.A");
+    Assert.assertTrue(classA.isPresent());
+    Assert.assertFalse(classA.method("void", "unused", ImmutableList.of()).isPresent());
+  }
+
+  public static void shaking3HasNoPrivateClass(DexInspector inspector) {
+    Assert.assertTrue(inspector.clazz("shaking3.B").isPresent());
+    Assert.assertFalse(inspector.clazz("shaking3.AnAbstractClass").isPresent());
+  }
+
+  private static void shaking5Inspection(DexInspector inspector) {
+    Assert.assertFalse(inspector.clazz("shaking5.Superclass")
+        .method("void", "virtualMethod", Collections.emptyList()).isPresent());
+  }
+
+  private static void hasNoPrivateMethods(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> clazz.forAllMethods(
+        method -> Assert.assertTrue(method.hasNone(new DexAccessFlags(Constants.ACC_PRIVATE)))
+    ));
+  }
+
+  private static void hasNoPublicMethodsButPrivate(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> clazz.forAllMethods(method -> {
+      if (!method.isStatic() && !method.isFinal()) {
+        Assert.assertTrue(method.hasNone(new DexAccessFlags(Constants.ACC_PUBLIC)));
+      }
+    }));
+    Assert.assertTrue(inspector.clazz("shaking6.Superclass")
+        .method("void", "justAMethod", Collections.emptyList()).isPresent());
+  }
+
+  private static void hasNoPrivateJustAMethod(DexInspector inspector) {
+    Assert.assertFalse(
+        inspector.clazz("shaking6.Superclass")
+            .method("void", "justAMethod", Collections.emptyList())
+            .isPresent());
+    ClassSubject subclass = inspector.clazz("shaking6.Subclass");
+    Assert.assertTrue(subclass.isPresent());
+    Assert.assertTrue(
+        subclass.method("void", "justAMethod", Collections.emptyList())
+            .isPresent());
+    Assert.assertTrue(
+        subclass.method("void", "justAMethod", Collections.singletonList("int"))
+            .isPresent());
+    Assert.assertTrue(
+        subclass.method("void", "justAMethod", Collections.singletonList("boolean"))
+            .isPresent());
+    Assert.assertFalse(
+        subclass.method("int", "justAMethod", Collections.singletonList("double"))
+            .isPresent());
+  }
+
+  private static void hasOnlyIntJustAMethod(DexInspector inspector) {
+    Assert.assertFalse(
+        inspector.clazz("shaking6.Superclass")
+            .method("void", "justAMethod", Collections.emptyList())
+            .isPresent());
+    ClassSubject subclass = inspector.clazz("shaking6.Subclass");
+    Assert.assertTrue(subclass.isPresent());
+    Assert.assertFalse(
+        subclass.method("void", "justAMethod", Collections.emptyList())
+            .isPresent());
+    Assert.assertTrue(
+        subclass.method("void", "justAMethod", Collections.singletonList("int"))
+            .isPresent());
+    Assert.assertFalse(
+        subclass.method("void", "justAMethod", Collections.singletonList("boolean"))
+            .isPresent());
+    Assert.assertFalse(
+        subclass.method("int", "justAMethod", Collections.singletonList("double"))
+            .isPresent());
+  }
+
+  private static void shaking7HasOnlyPublicFields(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> {
+      clazz.forAllFields(field -> {
+        Assert.assertTrue(field.hasAll(new DexAccessFlags(Constants.ACC_PUBLIC)));
+      });
+    });
+    ClassSubject subclass = inspector.clazz("shaking7.Subclass");
+    Assert.assertTrue(subclass.field("int", "theIntField").isPresent());
+    Assert.assertTrue(subclass.field("double", "theDoubleField").isPresent());
+    Assert.assertTrue(inspector.clazz("shaking7.Superclass")
+        .field("double", "theDoubleField").isPresent());
+    Assert.assertTrue(inspector.clazz("shaking7.Liar").field("int", "theDoubleField").isPresent());
+  }
+
+  private static void shaking7HasOnlyDoubleFields(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> {
+      clazz.forAllFields(field -> {
+        Assert.assertTrue(field.type().is("double"));
+      });
+    });
+    Assert.assertTrue(
+        inspector.clazz("shaking7.Subclass").field("double", "theDoubleField").isPresent());
+    Assert.assertTrue(
+        inspector.clazz("shaking7.Superclass").field("double", "theDoubleField").isPresent());
+    Assert.assertFalse(
+        inspector.clazz("shaking7.Liar").field("int", "theDoubleField").isPresent());
+  }
+
+  private static void shaking7HasOnlyPublicFieldsNamedTheDoubleField(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> {
+      clazz.forAllFields(field -> {
+        Assert.assertTrue(field.hasAll(new DexAccessFlags(Constants.ACC_PUBLIC)));
+      });
+    });
+    ClassSubject subclass = inspector.clazz("shaking7.Subclass");
+    Assert.assertFalse(subclass.field("int", "theIntField").isPresent());
+    Assert.assertTrue(subclass.field("double", "theDoubleField").isPresent());
+    Assert.assertTrue(inspector.clazz("shaking7.Superclass")
+        .field("double", "theDoubleField").isPresent());
+    Assert.assertTrue(inspector.clazz("shaking7.Liar").field("int", "theDoubleField").isPresent());
+  }
+
+  private static void shaking7HasOnlyPublicFieldsNamedTheIntField(DexInspector inspector) {
+    inspector.forAllClasses(clazz -> {
+      clazz.forAllFields(field -> {
+        Assert.assertTrue(field.hasAll(new DexAccessFlags(Constants.ACC_PUBLIC)));
+      });
+    });
+    ClassSubject subclass = inspector.clazz("shaking7.Subclass");
+    Assert.assertTrue(subclass.field("int", "theIntField").isPresent());
+    Assert.assertFalse(subclass.field("double", "theDoubleField").isPresent());
+    Assert.assertFalse(inspector.clazz("shaking7.Superclass")
+        .field("double", "theDoubleField").isPresent());
+    ClassSubject liar = inspector.clazz("shaking7.Liar");
+    Assert.assertFalse(liar.field("int", "theDoubleField").isPresent());
+    Assert.assertTrue(liar.field("double", "theIntField").isPresent());
+  }
+
+  private static void shaking8ThingClassIsAbstractAndEmpty(DexInspector inspector) {
+    ClassSubject clazz = inspector.clazz("shaking8.Thing");
+    Assert.assertTrue(clazz.isAbstract());
+    clazz.forAllMethods((method) -> Assert.fail());
+    clazz = inspector.clazz("shaking8.YetAnotherThing");
+    Assert.assertTrue(clazz.isAbstract());
+    clazz.forAllMethods((method) -> Assert.fail());
+  }
+
+  private static void shaking9OnlySuperMethodsKept(DexInspector inspector) {
+    ClassSubject superclass = inspector.clazz("shaking9.Superclass");
+    Assert.assertTrue(superclass.isAbstract());
+    Assert.assertTrue(superclass.method("void", "aMethod", ImmutableList.of()).isPresent());
+    ClassSubject subclass = inspector.clazz("shaking9.Subclass");
+    Assert.assertFalse(subclass.method("void", "aMethod", ImmutableList.of()).isPresent());
+  }
+
+  private static void shaking11OnlyOneClassKept(DexInspector dexInspector) {
+    Assert.assertFalse(dexInspector.clazz("shaking11.Subclass").isPresent());
+    Assert.assertTrue(dexInspector.clazz("shaking11.SubclassWithMethod").isPresent());
+  }
+
+  private static void shaking11BothMethodsKept(DexInspector dexInspector) {
+    Assert.assertFalse(
+        dexInspector.clazz("shaking11.Subclass").method("void", "aMethod", Collections.emptyList())
+            .isPresent());
+    Assert.assertTrue(
+        dexInspector.clazz("shaking11.SuperClass")
+            .method("void", "aMethod", Collections.emptyList())
+            .isPresent());
+    Assert.assertTrue(dexInspector.clazz("shaking11.SubclassWithMethod")
+        .method("void", "aMethod", Collections.emptyList()).isPresent());
+  }
+
+  private static void shaking12OnlyInstantiatedClassesHaveConstructors(DexInspector inspector) {
+    ClassSubject animalClass = inspector.clazz("shaking12.AnimalClass");
+    Assert.assertTrue(animalClass.isPresent());
+    Assert.assertFalse(
+        animalClass.method("void", "<init>", Collections.emptyList()).isPresent());
+    Assert.assertTrue(inspector.clazz("shaking12.MetaphorClass").isAbstract());
+    ClassSubject peopleClass = inspector.clazz("shaking12.PeopleClass");
+    Assert.assertTrue((peopleClass.isPresent() && !peopleClass.isAbstract()));
+    Assert.assertTrue(
+        peopleClass.method("void", "<init>", Collections.emptyList()).isPresent());
+    ClassSubject thingClass = inspector.clazz("shaking12.ThingClass");
+    Assert.assertTrue((thingClass.isPresent() && !thingClass.isAbstract()));
+    Assert.assertTrue(
+        thingClass.method("void", "<init>", Collections.emptyList()).isPresent());
+  }
+
+  private static void shaking13EnsureFieldWritesCorrect(DexInspector inspector) {
+    ClassSubject mainClass = inspector.clazz("shaking13.Shaking");
+    MethodSubject testMethod = mainClass.method("void", "fieldTest", Collections.emptyList());
+    Assert.assertTrue(testMethod.isPresent());
+    Iterator<FieldAccessInstructionSubject> iterator =
+        testMethod.iterateInstructions(InstructionSubject::isFieldAccess);
+    Assert.assertTrue(iterator.hasNext() && iterator.next().holder().is("shakinglib.LibraryClass"));
+    Assert.assertTrue(iterator.hasNext() && iterator.next().holder().is("shakinglib.LibraryClass"));
+    Assert.assertFalse(iterator.hasNext());
+  }
+
+  private static void shaking14EnsureRightStaticMethodsLive(DexInspector inspector) {
+    ClassSubject superclass = inspector.clazz("shaking14.Superclass");
+    Assert.assertFalse(superclass.method("int", "aMethod", ImmutableList.of("int")).isPresent());
+    Assert.assertFalse(
+        superclass.method("double", "anotherMethod", ImmutableList.of("double")).isPresent());
+    ClassSubject subclass = inspector.clazz("shaking14.Subclass");
+    Assert.assertTrue(subclass.method("int", "aMethod", ImmutableList.of("int")).isPresent());
+    Assert.assertTrue(
+        subclass.method("double", "anotherMethod", ImmutableList.of("double")).isPresent());
+  }
+
+  private static List<String> names =
+      ImmutableList.of("pqr", "vw$", "abc", "def", "stu", "ghi", "jkl", "ea", "xyz_", "mno");
+
+  private static void checkFieldInDictionary(FieldSubject field) {
+    if (!names.contains(field.getField().field.name.toSourceString())) {
+      throw new AssertionError();
+    }
+  }
+
+  private static void checkMethodInDictionary(MethodSubject method) {
+    String name = method.getMethod().method.name.toSourceString();
+    if (!names.contains(name) && !name.equals("<init>") && !name.equals("main")) {
+      throw new AssertionError();
+    }
+  }
+
+  private static void checkClassAndMemberInDictionary(ClassSubject clazz) {
+    String name = clazz.getDexClass().type.getName();
+    if (!names.contains(name) && !name.equals("Shaking")) {
+      throw new AssertionError();
+    }
+
+    clazz.forAllMethods(method -> checkMethodInDictionary(method));
+    clazz.forAllFields(field -> checkFieldInDictionary(field));
+  }
+
+  private static void shaking15testDictionary(DexInspector inspector) {
+    inspector.forAllClasses((clazz) -> checkClassAndMemberInDictionary(clazz));
+  }
+
+
+  private static void assumenosideeffects1CheckOutput(String output1, String output2) {
+    Assert.assertEquals("noSideEffectVoid\nnoSideEffectInt\n", output1);
+    Assert.assertEquals("", output2);
+  }
+
+  private static void assumenosideeffects2CheckOutput(String output1, String output2) {
+    Assert.assertEquals("Hello, world!\n", output1);
+    Assert.assertEquals("", output2);
+  }
+
+  private static void assumenosideeffects3CheckOutput(String output1, String output2) {
+    Assert.assertEquals("0\n1\n0L\n1L\n", output1);
+    Assert.assertEquals("1\n0\n1L\n0L\n", output2);
+  }
+
+  private static void assumenosideeffects4CheckOutput(String output1, String output2) {
+    Assert.assertEquals("method0\n0\nmethod1\n1\nmethod0L\n0L\nmethod1L\n1L\n", output1);
+    Assert.assertEquals("1\n0\n1L\n0L\n", output2);
+  }
+
+  private static void assumenosideeffects5CheckOutput(String output1, String output2) {
+    Assert.assertEquals("methodTrue\ntrue\nmethodFalse\nfalse\n", output1);
+    Assert.assertEquals("false\ntrue\n", output2);
+  }
+
+  private static void assumevalues1CheckOutput(String output1, String output2) {
+    Assert.assertEquals("3\n3L\n", output1);
+    Assert.assertEquals("1\n1L\n", output2);
+  }
+
+  private static void assumevalues2CheckOutput(String output1, String output2) {
+    Assert.assertEquals("1\n2\n3\n4\n1L\n2L\n3L\n4L\n", output1);
+    Assert.assertEquals("2\n3\n2L\n3L\n", output2);
+  }
+
+  private static void assumevalues3CheckOutput(String output1, String output2) {
+    Assert.assertEquals("3\n3L\n", output1);
+    Assert.assertEquals("1\n1L\n", output2);
+  }
+
+  private static void assumevalues4CheckOutput(String output1, String output2) {
+    Assert.assertEquals("method0\n0\nmethod1\n1\nmethod0L\n0L\nmethod1L\n1L\n", output1);
+    Assert.assertEquals("method0\n1\nmethod1\n0\nmethod0L\n1L\nmethod1L\n0L\n", output2);
+  }
+
+  private static void assumevalues5CheckOutput(String output1, String output2) {
+    Assert.assertEquals("methodTrue\ntrue\nmethodFalse\nfalse\n", output1);
+    Assert.assertEquals("methodTrue\nfalse\nmethodFalse\ntrue\n", output2);
+  }
+
+  private static void annotationRemovalHasAllInnerClassAnnotations(DexInspector inspector) {
+    ClassSubject outer = inspector.clazz("annotationremoval.OuterClass");
+    Assert.assertTrue(outer.isPresent());
+    Assert.assertTrue(outer.annotation("dalvik.annotation.MemberClasses").isPresent());
+    ClassSubject inner = inspector.clazz("annotationremoval.OuterClass$InnerClass");
+    Assert.assertTrue(inner.isPresent());
+    Assert.assertFalse(inner.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+    Assert.assertTrue(inner.annotation("dalvik.annotation.EnclosingClass").isPresent());
+    Assert.assertTrue(inner.annotation("dalvik.annotation.InnerClass").isPresent());
+    ClassSubject anonymous = inspector.clazz("annotationremoval.OuterClass$1");
+    Assert.assertTrue(anonymous.isPresent());
+    Assert.assertFalse(anonymous.annotation("dalvik.annotation.EnclosingClass").isPresent());
+    Assert.assertTrue(anonymous.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+    Assert.assertTrue(anonymous.annotation("dalvik.annotation.InnerClass").isPresent());
+    ClassSubject local = inspector.clazz("annotationremoval.OuterClass$1LocalMagic");
+    Assert.assertTrue(local.isPresent());
+    Assert.assertFalse(local.annotation("dalvik.annotation.EnclosingClass").isPresent());
+    Assert.assertTrue(local.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+    Assert.assertTrue(local.annotation("dalvik.annotation.InnerClass").isPresent());
+  }
+
+  private static void annotationRemovalHasNoInnerClassAnnotations(DexInspector inspector) {
+    ClassSubject outer = inspector.clazz("annotationremoval.OuterClass");
+    Assert.assertTrue(outer.isPresent());
+    Assert.assertFalse(outer.annotation("dalvik.annotation.MemberClasses").isPresent());
+    ClassSubject inner = inspector.clazz("annotationremoval.OuterClass$InnerClass");
+    Assert.assertTrue(inner.isPresent());
+    Assert.assertFalse(inner.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+    Assert.assertFalse(inner.annotation("dalvik.annotation.EnclosingClass").isPresent());
+    Assert.assertFalse(inner.annotation("dalvik.annotation.InnerClass").isPresent());
+    ClassSubject anonymous = inspector.clazz("annotationremoval.OuterClass$1");
+    Assert.assertTrue(anonymous.isPresent());
+    Assert.assertFalse(anonymous.annotation("dalvik.annotation.EnclosingClass").isPresent());
+    Assert.assertFalse(anonymous.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+    Assert.assertFalse(anonymous.annotation("dalvik.annotation.InnerClass").isPresent());
+    ClassSubject local = inspector.clazz("annotationremoval.OuterClass$1LocalMagic");
+    Assert.assertTrue(local.isPresent());
+    Assert.assertFalse(local.annotation("dalvik.annotation.EnclosingClass").isPresent());
+    Assert.assertFalse(local.annotation("dalvik.annotation.EnclosingMethod").isPresent());
+    Assert.assertFalse(local.annotation("dalvik.annotation.InnerClass").isPresent());
+  }
+
+  private static void checkSameStructure(DexInspector ref, DexInspector inspector) {
+    ref.forAllClasses(refClazz -> checkSameStructure(refClazz,
+        inspector.clazz(refClazz.getDexClass().toSourceString())));
+  }
+
+  private static void checkSameStructure(ClassSubject refClazz, ClassSubject clazz) {
+    Assert.assertTrue(clazz.isPresent());
+    refClazz.forAllFields(refField -> checkSameStructure(refField, clazz));
+    refClazz.forAllMethods(refMethod -> checkSameStructure(refMethod, clazz));
+  }
+
+  private static void checkSameStructure(FoundMethodSubject refMethod, ClassSubject clazz) {
+    MethodSignature signature = refMethod.getOriginalSignature();
+    Assert.assertTrue("Missing Method: " + clazz.getDexClass().toSourceString() + "."
+            + signature.toString(),
+        clazz.method(signature).isPresent());
+  }
+
+  private static void checkSameStructure(FoundFieldSubject refField, ClassSubject clazz) {
+    FieldSignature signature = refField.getOriginalSignature();
+    Assert.assertTrue(
+        "Missing field: " + signature.type + " " + clazz.getOriginalDescriptor()
+            + "." + signature.name,
+        clazz.field(signature.type, signature.name).isPresent());
+  }
+
+  @Parameters(name = "dex: {0} frontend: {1} keep: {3} minify: {4}")
+  public static Collection<Object[]> data() {
+    List<String> tests = Arrays
+        .asList(
+            "shaking1",
+            "shaking2",
+            "shaking3",
+            "shaking4",
+            "shaking5",
+            "shaking6",
+            "shaking7",
+            "shaking8",
+            "shaking9",
+            "shaking10",
+            "shaking11",
+            "shaking12",
+            "shaking13",
+            "shaking14",
+            "shaking15",
+            "shaking16",
+            "inlining",
+            "minification",
+            "assumenosideeffects1",
+            "assumenosideeffects2",
+            "assumenosideeffects3",
+            "assumenosideeffects4",
+            "assumenosideeffects5",
+            "assumevalues1",
+            "assumevalues2",
+            "assumevalues3",
+            "assumevalues4",
+            "assumevalues5",
+            "annotationremoval");
+
+    // Keys can be the name of the test or the name of the test followed by a colon and the name
+    // of the keep file.
+    Map<String, Consumer<DexInspector>> inspections = new HashMap<>();
+    inspections.put("shaking1:keep-rules.txt", TreeShakingTest::shaking1HasNoClassUnused);
+    inspections
+        .put("shaking1:keep-rules-repackaging.txt", TreeShakingTest::shaking1IsCorrectlyRepackaged);
+    inspections.put("shaking2:keep-rules.txt", TreeShakingTest::shaking2SuperClassIsAbstract);
+    inspections.put("shaking3:keep-by-tag.txt", TreeShakingTest::shaking3HasNoClassB);
+    inspections.put("shaking3:keep-by-tag-default.txt", TreeShakingTest::shaking3HasNoClassB);
+    inspections.put("shaking3:keep-by-tag-with-pattern.txt", TreeShakingTest::shaking3HasNoClassB);
+    inspections.put("shaking3:keep-by-tag-via-interface.txt", TreeShakingTest::shaking3HasNoClassB);
+    inspections.put("shaking3:keep-by-tag-on-method.txt", TreeShakingTest::shaking3HasNoClassB);
+    inspections
+        .put("shaking3:keep-no-abstract-classes.txt", TreeShakingTest::shaking3HasNoPrivateClass);
+    inspections.put("shaking5", TreeShakingTest::shaking5Inspection);
+    inspections.put("shaking6:keep-public.txt", TreeShakingTest::hasNoPrivateMethods);
+    inspections.put("shaking6:keep-non-public.txt", TreeShakingTest::hasNoPublicMethodsButPrivate);
+    inspections
+        .put("shaking6:keep-justAMethod-public.txt", TreeShakingTest::hasNoPrivateJustAMethod);
+    inspections.put("shaking6:keep-justAMethod-OnInt.txt", TreeShakingTest::hasOnlyIntJustAMethod);
+    inspections
+        .put("shaking7:keep-public-fields.txt", TreeShakingTest::shaking7HasOnlyPublicFields);
+    inspections
+        .put("shaking7:keep-double-fields.txt", TreeShakingTest::shaking7HasOnlyDoubleFields);
+    inspections
+        .put("shaking7:keep-public-theDoubleField-fields.txt",
+            TreeShakingTest::shaking7HasOnlyPublicFieldsNamedTheDoubleField);
+    inspections
+        .put("shaking7:keep-public-theIntField-fields.txt",
+            TreeShakingTest::shaking7HasOnlyPublicFieldsNamedTheIntField);
+    inspections
+        .put("shaking8:keep-rules.txt", TreeShakingTest::shaking8ThingClassIsAbstractAndEmpty);
+    inspections
+        .put("shaking9:keep-rules.txt", TreeShakingTest::shaking9OnlySuperMethodsKept);
+    inspections
+        .put("shaking11:keep-rules.txt", TreeShakingTest::shaking11OnlyOneClassKept);
+    inspections
+        .put("shaking11:keep-rules-keep-method.txt", TreeShakingTest::shaking11BothMethodsKept);
+    inspections
+        .put("shaking12:keep-rules.txt",
+            TreeShakingTest::shaking12OnlyInstantiatedClassesHaveConstructors);
+    inspections
+        .put("shaking13:keep-rules.txt",
+            TreeShakingTest::shaking13EnsureFieldWritesCorrect);
+    inspections
+        .put("shaking14:keep-rules.txt",
+            TreeShakingTest::shaking14EnsureRightStaticMethodsLive);
+    inspections.put("shaking15:keep-rules.txt",
+        TreeShakingTest::shaking15testDictionary);
+    inspections
+        .put("annotationremoval:keep-rules.txt",
+            TreeShakingTest::annotationRemovalHasNoInnerClassAnnotations);
+    inspections
+        .put("annotationremoval:keep-rules-keep-innerannotation.txt",
+            TreeShakingTest::annotationRemovalHasAllInnerClassAnnotations);
+
+    // Keys can be the name of the test or the name of the test followed by a colon and the name
+    // of the keep file.
+    Map<String, Collection<List<String>>> optionalRules = new HashMap<>();
+    optionalRules.put("shaking1", ImmutableList.of(
+        Collections.singletonList(EMPTY_FLAGS),
+        Lists.newArrayList(EMPTY_FLAGS, EMPTY_FLAGS)));
+    List<Object[]> testCases = new ArrayList<>();
+
+    Map<String, BiConsumer<String, String>> outputComparators = new HashMap<>();
+    outputComparators
+        .put("assumenosideeffects1",
+            TreeShakingTest::assumenosideeffects1CheckOutput);
+    outputComparators
+        .put("assumenosideeffects2",
+            TreeShakingTest::assumenosideeffects2CheckOutput);
+    outputComparators
+        .put("assumenosideeffects3",
+            TreeShakingTest::assumenosideeffects3CheckOutput);
+    outputComparators
+        .put("assumenosideeffects4",
+            TreeShakingTest::assumenosideeffects4CheckOutput);
+    outputComparators
+        .put("assumenosideeffects5",
+            TreeShakingTest::assumenosideeffects5CheckOutput);
+    outputComparators
+        .put("assumevalues1",
+            TreeShakingTest::assumevalues1CheckOutput);
+    outputComparators
+        .put("assumevalues2",
+            TreeShakingTest::assumevalues2CheckOutput);
+    outputComparators
+        .put("assumevalues3",
+            TreeShakingTest::assumevalues3CheckOutput);
+    outputComparators
+        .put("assumevalues4",
+            TreeShakingTest::assumevalues4CheckOutput);
+    outputComparators
+        .put("assumevalues5",
+            TreeShakingTest::assumevalues5CheckOutput);
+
+    Map<String, BiConsumer<DexInspector, DexInspector>> dexComparators = new HashMap<>();
+    dexComparators
+        .put("shaking1:keep-rules-dont-shrink.txt", TreeShakingTest::checkSameStructure);
+    dexComparators
+        .put("shaking2:keep-rules-dont-shrink.txt", TreeShakingTest::checkSameStructure);
+    dexComparators
+        .put("shaking4:keep-rules-dont-shrink.txt", TreeShakingTest::checkSameStructure);
+
+    Set<String> usedInspections = new HashSet<>();
+    Set<String> usedOptionalRules = new HashSet<>();
+    Set<String> usedOutputComparators = new HashSet<>();
+    Set<String> usedDexComparators = new HashSet<>();
+
+    for (String test : tests) {
+      String mainClass = deriveMainClass(test);
+      File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + "/" + test)
+          .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+      for (File keepFile : keepFiles) {
+        String keepName = keepFile.getName();
+        Consumer<DexInspector> inspection =
+            getTestOptionalParameter(inspections, usedInspections, test, keepName);
+        Collection<List<String>> additionalRules =
+            getTestOptionalParameter(optionalRules, usedOptionalRules, test, keepName);
+
+        BiConsumer<String, String> outputComparator =
+            getTestOptionalParameter(outputComparators, usedOutputComparators, test, keepName);
+        BiConsumer<DexInspector, DexInspector> dexComparator =
+            getTestOptionalParameter(dexComparators, usedDexComparators, test, keepName);
+
+        addTestCases(testCases, test, mainClass, keepName,
+            Collections.singletonList(keepFile.getPath()), inspection,
+            outputComparator, dexComparator);
+
+        if (additionalRules != null) {
+          for (List<String> list : additionalRules) {
+            List<String> keepList = new ArrayList<>(list.size());
+            keepList.add(keepFile.getPath());
+            keepList.addAll(list);
+            addTestCases(testCases, test, mainClass, keepName, keepList, inspection,
+                outputComparator, dexComparator);
+          }
+        }
+      }
+    }
+
+    assert usedInspections.size() == inspections.size();
+    assert usedOptionalRules.size() == optionalRules.size();
+    assert usedOutputComparators.size() == outputComparators.size();
+
+    return testCases;
+  }
+
+  private static void addTestCases(List<Object[]> testCases, String test, String mainClass,
+      String keepName, List<String> keepList, Consumer<DexInspector> inspection,
+      BiConsumer<String, String> outputComparator,
+      BiConsumer<DexInspector, DexInspector> dexComparator) {
+    addTestCase(testCases, test, Frontend.JAR, mainClass, keepName, keepList, false, inspection,
+        outputComparator, dexComparator);
+    addTestCase(testCases, test, Frontend.DEX, mainClass, keepName, keepList, false, inspection,
+        outputComparator, dexComparator);
+    addTestCase(testCases, test, Frontend.JAR, mainClass, keepName, keepList, true, inspection,
+        outputComparator, dexComparator);
+    addTestCase(testCases, test, Frontend.DEX, mainClass, keepName, keepList, true, inspection,
+        outputComparator, dexComparator);
+  }
+
+  private static void addTestCase(List<Object[]> testCases, String test, Frontend kind,
+      String mainClass, String keepName, List<String> keepList, boolean minify,
+      Consumer<DexInspector> inspection, BiConsumer<String, String> outputComparator,
+      BiConsumer<DexInspector, DexInspector> dexComparator) {
+    if (!IGNORED.contains(test + ":" + keepName + ":" + kind + ":" + minify)) {
+      testCases.add(new Object[]{
+          test, kind, mainClass, keepList, minify, inspection, outputComparator, dexComparator});
+    }
+  }
+
+  private static <T> T getTestOptionalParameter(
+      Map<String, T> specifications, Set<String> usedSpecifications, String test,
+      String keepName) {
+    T parameter = specifications.get(test);
+    if (parameter == null) {
+      parameter = specifications.get(test + ":" + keepName);
+      if (parameter != null) {
+        usedSpecifications.add(test + ":" + keepName);
+      }
+    } else {
+      usedSpecifications.add(test);
+    }
+    return parameter;
+  }
+
+  private static String deriveMainClass(String testName) {
+    StringBuilder mainClass = new StringBuilder(testName.length() * 2 + 1);
+    mainClass.append(testName);
+    mainClass.append('.');
+    mainClass.append(Character.toUpperCase(testName.charAt(0)));
+    for (int i = 1; i < testName.length(); i++) {
+      char next = testName.charAt(i);
+      if (!Character.isAlphabetic(next)) {
+        break;
+      }
+      mainClass.append(next);
+    }
+    return mainClass.toString();
+  }
+
+  @Test
+  public void treeShakingTest() throws IOException, InterruptedException, ExecutionException {
+    if (!ToolHelper.artSupported()) {
+      return;
+    }
+    String out = temp.getRoot().getCanonicalPath();
+    Path generated = Paths.get(out, "classes.dex");
+    Consumer<ArtCommandBuilder> extraArtArgs = builder -> {
+      builder.appendClasspath(ToolHelper.EXAMPLES_BUILD_DIR + "shakinglib/classes.dex");
+    };
+
+    if (outputComparator != null) {
+      String output1 = ToolHelper.runArtNoVerificationErrors(
+          Collections.singletonList(originalDex), mainClass, extraArtArgs, null);
+      String output2 = ToolHelper.runArtNoVerificationErrors(
+          Collections.singletonList(generated.toString()), mainClass, extraArtArgs, null);
+      outputComparator.accept(output1, output2);
+    } else {
+      ToolHelper.checkArtOutputIdentical(Collections.singletonList(originalDex),
+          Collections.singletonList(generated.toString()), mainClass,
+          extraArtArgs, null);
+    }
+
+    if (dexComparator != null) {
+      DexInspector ref = new DexInspector(Paths.get(originalDex));
+      DexInspector inspector = new DexInspector(generated,
+          minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+      dexComparator.accept(ref, inspector);
+    }
+
+    if (inspection != null) {
+      DexInspector inspector = new DexInspector(generated,
+          minify ? temp.getRoot().toPath().resolve("proguard.map").toString() : null);
+      inspection.accept(inspector);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/BinopLiteralTest.java b/src/test/java/com/android/tools/r8/smali/BinopLiteralTest.java
new file mode 100644
index 0000000..7429b43
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/BinopLiteralTest.java
@@ -0,0 +1,120 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const16;
+import com.android.tools.r8.code.Format22b;
+import com.android.tools.r8.code.Format22s;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class BinopLiteralTest extends SmaliTestBase {
+
+  int[] lit8Values = new int[]{
+      Constants.S8BIT_MIN,
+      Constants.S8BIT_MIN + 1,
+      Constants.S4BIT_MIN - 1,
+      Constants.S4BIT_MIN,
+      Constants.S4BIT_MIN + 1,
+      1,
+      0,
+      1,
+      Constants.S4BIT_MAX - 1,
+      Constants.S4BIT_MAX,
+      Constants.S4BIT_MAX + 1,
+      Constants.S8BIT_MAX - 1,
+      Constants.S8BIT_MAX,
+  };
+
+  int[] lit16Values = new int[]{
+      Short.MIN_VALUE,
+      Short.MIN_VALUE + 1,
+      Constants.S8BIT_MIN - 1,
+      Constants.S8BIT_MAX + 1,
+      Short.MAX_VALUE - 1,
+      Short.MAX_VALUE,
+  };
+
+  @Test
+  public void lit8PassthroughTest() {
+    List<String> lit8Binops = Arrays.asList(
+        "add", "rsub", "mul", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr"
+    );
+
+    for (String binop : lit8Binops) {
+      for (int lit8Value : lit8Values) {
+        DexEncodedMethod method = oneMethodApplication(
+            "int", Collections.singletonList("int"),
+            0,
+            // E.g. add-int/lit8 p0, p0, -128
+            "    " + binop + "-int/lit8 p0, p0, " + lit8Value,
+            "    return p0"
+        );
+        DexCode code = method.getCode().asDexCode();
+        assertEquals(2, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof Format22b);
+        assertEquals(lit8Value, ((Format22b) code.instructions[0]).CC);
+        assertTrue(code.instructions[1] instanceof Return);
+      }
+    }
+  }
+
+  @Test
+  public void lit16PassthroughTest() {
+    List<String> lit16Binops = Arrays.asList(
+        "add", "rsub", "mul", "div", "rem", "and", "or", "xor"
+    );
+
+    for (String binop : lit16Binops) {
+      for (int lit16Value : lit16Values) {
+        String lit16Postfix = !binop.equals("rsub") ? "/lit16" : "";
+        DexEncodedMethod method = oneMethodApplication(
+            "int", Collections.singletonList("int"),
+            0,
+            // E.g. add-int/lit16 p0, p0, -32768
+            "    " + binop + "-int" + lit16Postfix + " p0, p0, " + lit16Value,
+            "    return p0"
+        );
+        DexCode code = method.getCode().asDexCode();
+        assertEquals(2, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof Format22s);
+        assertEquals(lit16Value, ((Format22s) code.instructions[0]).CCCC);
+        assertTrue(code.instructions[1] instanceof Return);
+      }
+    }
+  }
+
+  @Test
+  public void lit16NotSupported() {
+    String[] lit8OnlyBinops = new String[]{
+        "shl", "shr", "ushr",
+    };
+    for (String binop : lit8OnlyBinops) {
+      for (int lit16Value : lit16Values) {
+        DexEncodedMethod method = oneMethodApplication(
+            "int", Collections.singletonList("int"),
+            1,
+            "    const/16 v0, " + lit16Value,
+            "    " + binop + "-int/2addr p0, v0    ",
+            "    return p0"
+        );
+        DexCode code = method.getCode().asDexCode();
+        assertEquals(3, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof Const16);
+        assertEquals(lit16Value, ((Const16) code.instructions[0]).BBBB);
+        assertTrue(code.instructions[2] instanceof Return);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
new file mode 100644
index 0000000..5e67ad1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/CatchSuccessorFallthroughTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+
+/**
+ * Regression test to ensure that we do not ignore the exceptional / on-throw value of a
+ * throwing instruction in the special case where the exceptional edge and the normal edge target
+ * the same block.
+ */
+public class CatchSuccessorFallthroughTest extends SmaliTestBase {
+
+  @Test
+  public void catchSuccessorFallthroughTest() {
+
+    SmaliBuilder builder = new SmaliBuilder("Test");
+
+    builder.addStaticMethod("int", "maybeThrow", Arrays.asList("int"), 0,
+        "  if-eqz v0, :throw",
+        "  const v0, 42",
+        "  return v0",
+        ":throw",
+        "  div-int/2addr v0, v0",
+        "  return v0");
+
+    MethodSignature methodSig = builder.addStaticMethod(
+        "int", "method", Collections.singletonList("int"), 0,
+        ":try_start",
+        "  invoke-static {v0}, LTest;->maybeThrow(I)I",
+        "  move-result v0",
+        ":try_end",
+        "  .catch Ljava/lang/Throwable; {:try_start .. :try_end} :return",
+        ":return",
+        "  return v0"
+    );
+
+    builder.addStaticMethod(
+        "void", "main", Arrays.asList("java.lang.String[]"), 2,
+        "  sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "  const v0, 1",
+        "  invoke-static {v0}, LTest;->method(I)I",
+        "  move-result v0",
+        "  invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+        "  const v0, 0",
+        "  invoke-static {v0}, LTest;->method(I)I",
+        "  move-result v0",
+        "  invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+        "  return-void");
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+
+
+    DexEncodedMethod method = getMethod(originalApplication, methodSig);
+    // Get the IR pre-optimization.
+    IRCode code = method.buildIR(options);
+
+    // Find the exit block and assert that the value is a phi merging the exceptional edge
+    // with the normal edge.
+    boolean hasReturn = false;
+    for (BasicBlock block : code.blocks) {
+      if (block.exit() instanceof Return) {
+        // Find the return block.
+        // Check it has one phi with two operands / two predecessors.
+        Return ret = block.exit().asReturn();
+        assertEquals(2, block.getPredecessors().size());
+        assertEquals(1, block.getPhis().size());
+        assertEquals(ret.returnValue(), block.getPhis().get(0));
+        // Next we find and check that the phi values come from the expected predecessor.
+        boolean hasNormalPredecessor = false;
+        boolean hasExceptionalPredecessor = false;
+        Phi phi = block.getPhis().get(0);
+        for (Value operand : phi.getOperands()) {
+          BasicBlock defBlock = operand.definition.getBlock();
+          if (defBlock.canThrow()) {
+            // Found the invoke instruction / block.
+            assertEquals(2, defBlock.getSuccessors().size());
+            assertTrue(
+                defBlock.getInstructions().get(defBlock.getInstructions().size() - 2).isInvoke());
+            for (BasicBlock returnPredecessor : block.getPredecessors()) {
+              if (defBlock.isCatchSuccessor(returnPredecessor)) {
+                hasExceptionalPredecessor = true;
+              } else if (defBlock == returnPredecessor) {
+                // Normal flow goes to return.
+                hasNormalPredecessor = true;
+              } else if (defBlock.getSuccessors().contains(returnPredecessor)) {
+                // Normal flow goes to return after an edge split.
+                assertTrue(returnPredecessor.isTrivialGoto());
+                hasNormalPredecessor = true;
+              }
+            }
+          }
+        }
+        assertTrue(hasNormalPredecessor);
+        assertTrue(hasExceptionalPredecessor);
+        hasReturn = true;
+      }
+    }
+    assertTrue(hasReturn);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
new file mode 100644
index 0000000..64aa946
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+
+public class ComputeBlockTryRangeTest extends SmaliTestBase {
+
+  @Test
+  public void jumpIntoTryRange() {
+
+    SmaliBuilder builder = new SmaliBuilder("Test");
+
+    builder.addStaticMethod(
+        "void", "main", Arrays.asList("java.lang.String[]"), 2,
+        "  sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "  const v0, 1",
+        "  invoke-static {v0}, LTest;->method(I)I",
+        "  move-result v0",
+        "  invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+        "  const v0, 0",
+        "  invoke-static {v0}, LTest;->method(I)I",
+        "  move-result v0",
+        "  invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V",
+        "  return-void");
+
+    MethodSignature methodSig = builder.addStaticMethod(
+        "int", "method", Collections.singletonList("int"), 1,
+        "  const v0, 42",
+        "  goto :in_try",
+        ":try_start",
+        ":dead_code",
+        "  const v0, 0",
+        ":in_try",
+        "  div-int/2addr v0, v1",
+        "  return v0",
+        ":try_end",
+        "  .catch Ljava/io/IOException; {:try_start .. :try_end} :dead_code",
+        "  .catch Ljava/lang/Throwable; {:try_start .. :try_end} :return_half",
+        ":return_half",
+        "  const v1, 2",
+        "  goto :in_try"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+
+    DexEncodedMethod method = getMethod(processedApplication, methodSig);
+    assert method.getCode().asDexCode().tries.length > 0;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
new file mode 100644
index 0000000..2a2fe32
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/ConstantFoldingTest.java
@@ -0,0 +1,739 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.DivIntLit8;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.RemIntLit8;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.Cmp.Bias;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.SingleConstant;
+import com.android.tools.r8.ir.code.WideConstant;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class ConstantFoldingTest extends SmaliTestBase {
+
+  public void generateBinopTest(String type, String op, List<Long> values, Long result) {
+    boolean wide = type.equals("long") || type.equals("double");
+    StringBuilder source = new StringBuilder();
+    int factor = wide ? 2 : 1;
+    for (int i = 0; i < values.size(); i++) {
+      source.append("    ");
+      source.append(wide ? "const-wide " : "const ");
+      source.append("v" + (i * factor));
+      source.append(", ");
+      source.append("0x" + Long.toHexString(values.get(i)));
+      source.append(wide ? "L" : "");
+      source.append("\n");
+    }
+
+    for (int i = 0; i < values.size() - 1; i++) {
+      source.append("    ");
+      source.append(op + "-" + type + "/2addr ");
+      source.append("v" + ((i + 1) * factor));
+      source.append(", ");
+      source.append("v" + (i * factor));
+      source.append("\n");
+    }
+
+    source.append("    ");
+    source.append(wide ? "return-wide " : "return ");
+    source.append("v" + ((values.size() - 1) * factor));
+
+    DexEncodedMethod method = oneMethodApplication(
+        type, Collections.singletonList(type),
+        values.size() * factor,
+        source.toString());
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    if (wide) {
+      assertTrue(code.instructions[0] instanceof WideConstant);
+      assertEquals(result.longValue(), ((WideConstant) code.instructions[0]).decodedValue());
+      assertTrue(code.instructions[1] instanceof ReturnWide);
+    } else {
+      assertTrue(code.instructions[0] instanceof SingleConstant);
+      assertEquals(
+          result.longValue(), (long) ((SingleConstant) code.instructions[0]).decodedValue());
+      assertTrue(code.instructions[1] instanceof Return);
+    }
+  }
+
+  private long floatBits(float f) {
+    return Float.floatToIntBits(f);
+  }
+
+  private long doubleBits(double d) {
+    return Double.doubleToLongBits(d);
+  }
+
+  ImmutableList<Long> arguments = ImmutableList.of(1L, 2L, 3L, 4L);
+  ImmutableList<Long> floatArguments = ImmutableList.of(
+      floatBits(1.0f), floatBits(2.0f), floatBits(3.0f), floatBits(4.0f));
+  ImmutableList<Long> doubleArguments = ImmutableList.of(
+      doubleBits(1.0), doubleBits(2.0), doubleBits(3.0), doubleBits(4.0));
+
+  @Test
+  public void addFold() {
+    generateBinopTest("int", "add", arguments, 10L);
+    generateBinopTest("long", "add", arguments, 10L);
+    generateBinopTest("float", "add", floatArguments, floatBits(10.0f));
+    generateBinopTest("double", "add", doubleArguments, doubleBits(10.0));
+  }
+
+  @Test
+  public void mulFold() {
+    generateBinopTest("int", "mul", arguments, 24L);
+    generateBinopTest("long", "mul", arguments, 24L);
+    generateBinopTest("float", "mul", floatArguments, floatBits(24.0f));
+    generateBinopTest("double", "mul", doubleArguments, doubleBits(24.0));
+  }
+
+  @Test
+  public void subFold() {
+    generateBinopTest("int", "sub", arguments.reverse(), -2L);
+    generateBinopTest("long", "sub", arguments.reverse(), -2L);
+    generateBinopTest("float", "sub", floatArguments.reverse(), floatBits(-2.0f));
+    generateBinopTest("double", "sub", doubleArguments.reverse(), doubleBits(-2.0));
+  }
+
+  @Test
+  public void divFold() {
+    ImmutableList<Long> arguments = ImmutableList.of(2L, 24L, 48L, 4L);
+    ImmutableList<Long> floatArguments = ImmutableList.of(
+        floatBits(2.0f), floatBits(24.0f), floatBits(48.0f), floatBits(4.0f));
+    ImmutableList<Long> doubleArguments = ImmutableList.of(
+        doubleBits(2.0), doubleBits(24.0), doubleBits(48.0), doubleBits(4.0));
+
+    generateBinopTest("int", "div", arguments, 1L);
+    generateBinopTest("long", "div", arguments, 1L);
+    generateBinopTest("float", "div", floatArguments, floatBits(1.0f));
+    generateBinopTest("double", "div", doubleArguments, doubleBits(1.0));
+  }
+
+
+  @Test
+  public void remFold() {
+    ImmutableList<Long> arguments = ImmutableList.of(10L, 6L, 3L, 2L);
+    ImmutableList<Long> floatArguments = ImmutableList.of(
+        floatBits(10.0f), floatBits(6.0f), floatBits(3.0f), floatBits(2.0f));
+    ImmutableList<Long> doubleArguments = ImmutableList.of(
+        doubleBits(10.0), doubleBits(6.0), doubleBits(3.0), doubleBits(2.0));
+
+    generateBinopTest("int", "rem", arguments, 2L);
+    generateBinopTest("long", "rem", arguments, 2L);
+    generateBinopTest("float", "rem", floatArguments, floatBits(2.0f));
+    generateBinopTest("double", "rem", doubleArguments, doubleBits(2.0));
+  }
+
+  @Test
+  public void divIntFoldDivByZero() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int", Collections.singletonList("int"),
+        2,
+        "    const/4 v0, 1           ",
+        "    const/4 v1, 0           ",
+        "    div-int/2addr v0, v1    ",
+        "    return v0\n             "
+    );
+    DexCode code = method.getCode().asDexCode();
+    // Division by zero is not folded, but div-int/lit8 is used.
+    assertEquals(3, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertTrue(code.instructions[1] instanceof DivIntLit8);
+    assertEquals(0, ((DivIntLit8) code.instructions[1]).CC);
+    assertTrue(code.instructions[2] instanceof Return);
+  }
+
+  @Test
+  public void divIntFoldRemByZero() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int", Collections.singletonList("int"),
+        2,
+        "    const/4 v0, 1           ",
+        "    const/4 v1, 0           ",
+        "    rem-int/2addr v0, v1    ",
+        "    return v0\n             "
+    );
+    DexCode code = method.getCode().asDexCode();
+    // Division by zero is not folded, but rem-int/lit8 is used.
+    assertEquals(3, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertTrue(code.instructions[1] instanceof RemIntLit8);
+    assertEquals(0, ((RemIntLit8) code.instructions[1]).CC);
+    assertTrue(code.instructions[2] instanceof Return);
+  }
+
+  public void generateUnopTest(String type, String op, Long value, Long result) {
+    boolean wide = type.equals("long") || type.equals("double");
+    StringBuilder source = new StringBuilder();
+    source.append("    ");
+    source.append(wide ? "const-wide " : "const ");
+    source.append("v0 , ");
+    source.append("0x" + Long.toHexString(value));
+    source.append(wide ? "L" : "");
+    source.append("\n");
+
+    source.append("    ");
+    source.append(op + "-" + type + " v0, v0\n");
+
+    source.append("    ");
+    source.append(wide ? "return-wide v0" : "return v0");
+
+    DexEncodedMethod method = oneMethodApplication(
+        type, Collections.singletonList(type),
+        wide ? 2 : 1,
+        source.toString());
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    if (wide) {
+      assertTrue(code.instructions[0] instanceof WideConstant);
+      assertEquals(result.longValue(), ((WideConstant) code.instructions[0]).decodedValue());
+      assertTrue(code.instructions[1] instanceof ReturnWide);
+    } else {
+      assertTrue(code.instructions[0] instanceof SingleConstant);
+      assertEquals(
+          result.longValue(), (long) ((SingleConstant) code.instructions[0]).decodedValue());
+      assertTrue(code.instructions[1] instanceof Return);
+    }
+  }
+
+  @Test
+  public void negFold() {
+    generateUnopTest("int", "neg", 2L, -2L);
+    generateUnopTest("int", "neg", -2L, 2L);
+    generateUnopTest("long", "neg", 2L, -2L);
+    generateUnopTest("long", "neg", -2L, 2L);
+    generateUnopTest("float", "neg", floatBits(2.0f), floatBits(-2.0f));
+    generateUnopTest("float", "neg", floatBits(-2.0f), floatBits(2.0f));
+    generateUnopTest("float", "neg", floatBits(0.0f), floatBits(-0.0f));
+    generateUnopTest("float", "neg", floatBits(-0.0f), floatBits(0.0f));
+    generateUnopTest("double", "neg", doubleBits(2.0), doubleBits(-2.0));
+    generateUnopTest("double", "neg", doubleBits(-2.0), doubleBits(2.0));
+    generateUnopTest("double", "neg", doubleBits(0.0), doubleBits(-0.0));
+    generateUnopTest("double", "neg", doubleBits(-0.0), doubleBits(0.0));
+  }
+
+  private void assertConstValue(int expected, Instruction insn) {
+    assertTrue(insn instanceof SingleConstant);
+    assertEquals(expected, ((SingleConstant) insn).decodedValue());
+  }
+
+  private void assertConstValue(long expected, Instruction insn) {
+    assertTrue(insn instanceof WideConstant);
+    assertEquals(expected, ((WideConstant) insn).decodedValue());
+  }
+
+  public void testLogicalOperatorsFolding(String op, int[] v) {
+    int v0 = v[0];
+    int v1 = v[1];
+    int v2 = v[2];
+    int v3 = v[3];
+
+    int expected = 0;
+    switch (op) {
+      case "and":
+        expected = v0 & v1 & v2 & v3;
+        break;
+      case "or":
+        expected = v0 | v1 | v2 | v3;
+        break;
+      case "xor":
+        expected = v0 ^ v1 ^ v2 ^ v3;
+        break;
+      default:
+        fail("Unsupported logical binop " + op);
+    }
+
+    DexEncodedMethod method = oneMethodApplication(
+        "int", Collections.singletonList("int"),
+        4,
+        "    const v0, " + v0,
+        "    const v1, " + v1,
+        "    const v2, " + v2,
+        "    const v3, " + v3,
+        // E.g. and-int//2addr v1, v0
+        "    " + op + "-int/2addr v1, v0    ",
+        "    " + op + "-int/2addr v2, v1    ",
+        "    " + op + "-int/2addr v3, v2    ",
+        "    return v3\n                    "
+    );
+    DexCode code = method.getCode().asDexCode();
+    // Test that this just returns a constant.
+    assertEquals(2, code.instructions.length);
+    assertConstValue(expected, code.instructions[0]);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void logicalOperatorsFolding() {
+    int[][] testValues = new int[][]{
+        new int[]{0x00, 0x00, 0x00, 0x00},
+        new int[]{0x0b, 0x06, 0x03, 0x00},
+        new int[]{0x0f, 0x07, 0x03, 0x01},
+        new int[]{0x08, 0x04, 0x02, 0x01},
+    };
+
+    for (int[] values : testValues) {
+      testLogicalOperatorsFolding("and", values);
+      testLogicalOperatorsFolding("or", values);
+      testLogicalOperatorsFolding("xor", values);
+    }
+  }
+
+  private void testShiftOperatorsFolding(String op, int[] v) {
+    int v0 = v[0];
+    int v1 = v[1];
+    int v2 = v[2];
+    int v3 = v[3];
+
+    int expected = 0;
+    switch (op) {
+      case "shl":
+        v0 = v0 << v1;
+        v0 = v0 << v2;
+        v0 = v0 << v3;
+        break;
+      case "shr":
+        v0 = v0 >> v1;
+        v0 = v0 >> v2;
+        v0 = v0 >> v3;
+        break;
+      case "ushr":
+        v0 = v0 >>> v1;
+        v0 = v0 >>> v2;
+        v0 = v0 >>> v3;
+        break;
+      default:
+        fail("Unsupported shift " + op);
+    }
+    expected = v0;
+
+    DexEncodedMethod method = oneMethodApplication(
+        "int", Collections.singletonList("int"),
+        4,
+        "    const v0, " + v[0],
+        "    const v1, " + v[1],
+        "    const v2, " + v[2],
+        "    const v3, " + v[3],
+        // E.g. and-int//2addr v1, v0
+        "    " + op + "-int/2addr v0, v1    ",
+        "    " + op + "-int/2addr v0, v2    ",
+        "    " + op + "-int/2addr v0, v3    ",
+        "    return v0\n                    "
+    );
+    DexCode code = method.getCode().asDexCode();
+    // Test that this just returns a constant.
+    assertEquals(2, code.instructions.length);
+    assertConstValue(expected, code.instructions[0]);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void shiftOperatorsFolding() {
+    int[][] testValues = new int[][]{
+        new int[]{0x01, 0x01, 0x01, 0x01},
+        new int[]{0x01, 0x02, 0x03, 0x04},
+        new int[]{0x7f000000, 0x01, 0x2, 0x03},
+        new int[]{0x80000000, 0x01, 0x2, 0x03},
+        new int[]{0xffffffff, 0x01, 0x2, 0x03},
+    };
+
+    for (int[] values : testValues) {
+      testShiftOperatorsFolding("shl", values);
+      testShiftOperatorsFolding("shr", values);
+      testShiftOperatorsFolding("ushr", values);
+    }
+  }
+
+  private void testShiftOperatorsFoldingWide(String op, long[] v) {
+    long v0 = v[0];
+    int v2 = (int) v[1];
+    int v4 = (int) v[2];
+    int v6 = (int) v[3];
+
+    long expected = 0;
+    switch (op) {
+      case "shl":
+        v0 = v0 << v2;
+        v0 = v0 << v4;
+        v0 = v0 << v6;
+        break;
+      case "shr":
+        v0 = v0 >> v2;
+        v0 = v0 >> v4;
+        v0 = v0 >> v6;
+        break;
+      case "ushr":
+        v0 = v0 >>> v2;
+        v0 = v0 >>> v4;
+        v0 = v0 >>> v6;
+        break;
+      default:
+        fail("Unsupported shift " + op);
+    }
+    expected = v0;
+
+    DexEncodedMethod method = oneMethodApplication(
+        "long", Collections.singletonList("long"),
+        5,
+        "    const-wide v0, 0x" + Long.toHexString(v[0]) + "L",
+        "    const v2, " + v[1],
+        "    const v3, " + v[2],
+        "    const v4, " + v[3],
+        // E.g. and-long//2addr v1, v0
+        "    " + op + "-long/2addr v0, v2    ",
+        "    " + op + "-long/2addr v0, v3    ",
+        "    " + op + "-long/2addr v0, v4    ",
+        "    return-wide v0\n                    "
+    );
+    DexCode code = method.getCode().asDexCode();
+    // Test that this just returns a constant.
+    assertEquals(2, code.instructions.length);
+    assertConstValue(expected, code.instructions[0]);
+    assertTrue(code.instructions[1] instanceof ReturnWide);
+  }
+
+  @Test
+  public void shiftOperatorsFoldingWide() {
+    long[][] testValues = new long[][]{
+        new long[]{0x01, 0x01, 0x01, 0x01},
+        new long[]{0x01, 0x02, 0x03, 0x04},
+        new long[]{0x7f0000000000L, 0x01, 0x2, 0x03},
+        new long[]{0x800000000000L, 0x01, 0x2, 0x03},
+        new long[]{0x7f00000000000000L, 0x01, 0x2, 0x03},
+        new long[]{0x8000000000000000L, 0x01, 0x2, 0x03},
+        new long[]{0xffffffffffffffffL, 0x01, 0x2, 0x03},
+    };
+
+    for (long[] values : testValues) {
+      testShiftOperatorsFoldingWide("shl", values);
+      testShiftOperatorsFoldingWide("shr", values);
+      testShiftOperatorsFoldingWide("ushr", values);
+    }
+  }
+
+  @Test
+  public void notIntFold() {
+    int[] testValues = new int[]{0, 1, 0xff, 0xffffffff, 0xff000000, 0x80000000};
+    for (int value : testValues) {
+      DexEncodedMethod method = oneMethodApplication(
+          "int", Collections.emptyList(),
+          1,
+          "    const v0, " + value,
+          "    not-int v0, v0",
+          "    return v0"
+      );
+      DexCode code = method.getCode().asDexCode();
+      assertEquals(2, code.instructions.length);
+      assertConstValue(~value, code.instructions[0]);
+      assertTrue(code.instructions[1] instanceof Return);
+    }
+  }
+
+  @Test
+  public void notLongFold() {
+    long[] testValues = new long[]{
+        0L,
+        1L,
+        0xffL,
+        0xffffffffffffffffL,
+        0x00ffffffffffffffL,
+        0xff00000000000000L,
+        0x8000000000000000L
+    };
+    for (long value : testValues) {
+      DexEncodedMethod method = oneMethodApplication(
+          "long", Collections.emptyList(),
+          2,
+          "    const-wide v0, 0x" + Long.toHexString(value) + "L",
+          "    not-long v0, v0",
+          "    return-wide v0"
+      );
+      DexCode code = method.getCode().asDexCode();
+      assertEquals(2, code.instructions.length);
+      assertConstValue(~value, code.instructions[0]);
+      assertTrue(code.instructions[1] instanceof ReturnWide);
+    }
+  }
+
+  @Test
+  public void negIntFold() {
+    int[] testValues = new int[]{0, 1, 0xff, 0xffffffff, 0xff000000, 0x80000000};
+    for (int value : testValues) {
+      DexEncodedMethod method = oneMethodApplication(
+          "int", Collections.emptyList(),
+          1,
+          "    const v0, " + value,
+          "    neg-int v0, v0",
+          "    return v0"
+      );
+      DexCode code = method.getCode().asDexCode();
+      assertEquals(2, code.instructions.length);
+      assertConstValue(-value, code.instructions[0]);
+      assertTrue(code.instructions[1] instanceof Return);
+    }
+  }
+
+  @Test
+  public void negLongFold() {
+    long[] testValues = new long[]{
+        0L,
+        1L,
+        0xffL,
+        0xffffffffffffffffL,
+        0x00ffffffffffffffL,
+        0xff00000000000000L,
+        0x8000000000000000L
+    };
+    for (long value : testValues) {
+      DexEncodedMethod method = oneMethodApplication(
+          "long", Collections.emptyList(),
+          2,
+          "    const-wide v0, 0x" + Long.toHexString(value) + "L",
+          "    neg-long v0, v0",
+          "    return-wide v0"
+      );
+      DexCode code = method.getCode().asDexCode();
+      assertEquals(2, code.instructions.length);
+      long expected = -value;
+      assertConstValue(-value, code.instructions[0]);
+      assertTrue(code.instructions[1] instanceof ReturnWide);
+    }
+  }
+
+  @Test
+  public void cmpFloatFold() {
+    String[] ifOpcode = new String[6];
+    ifOpcode[Type.EQ.ordinal()] = "if-eqz";
+    ifOpcode[Type.NE.ordinal()] = "if-nez";
+    ifOpcode[Type.LE.ordinal()] = "if-lez";
+    ifOpcode[Type.GE.ordinal()] = "if-gez";
+    ifOpcode[Type.LT.ordinal()] = "if-ltz";
+    ifOpcode[Type.GT.ordinal()] = "if-gtz";
+
+    class FloatTestData {
+
+      final float a;
+      final float b;
+      final boolean results[];
+
+      FloatTestData(float a, float b) {
+        this.a = a;
+        this.b = b;
+        results = new boolean[6];
+        results[Type.EQ.ordinal()] = a == b;
+        results[Type.NE.ordinal()] = a != b;
+        results[Type.LE.ordinal()] = a <= b;
+        results[Type.GE.ordinal()] = a >= b;
+        results[Type.LT.ordinal()] = a < b;
+        results[Type.GT.ordinal()] = a > b;
+      }
+    }
+
+    float[] testValues = new float[]{
+        Float.NEGATIVE_INFINITY,
+        -100.0f,
+        -0.0f,
+        0.0f,
+        100.0f,
+        Float.POSITIVE_INFINITY,
+        Float.NaN
+    };
+
+    List<FloatTestData> tests = new ArrayList<>();
+    for (int i = 0; i < testValues.length; i++) {
+      for (int j = 0; j < testValues.length; j++) {
+        tests.add(new FloatTestData(testValues[i], testValues[j]));
+      }
+    }
+
+    for (FloatTestData test : tests) {
+      for (Type type : Type.values()) {
+        for (Bias bias : Bias.values()) {
+          if (bias == Bias.NONE) {
+            // Bias NONE is only for long comparison.
+            continue;
+          }
+          // If no NaNs are involved either bias produce the same result.
+          if (Float.isNaN(test.a) || Float.isNaN(test.b)) {
+            // For NaN comparison only test with the bias that provide Java semantics.
+            // The Java Language Specification 4.2.3. Floating-Point Types, Formats, and Values
+            // says:
+            //
+            // The numerical comparison operators <, <=, >, and >= return false if either or both
+            // operands are NaN
+            if ((type == Type.GE || type == Type.GT) && bias == Bias.GT) {
+              continue;
+            }
+            if ((type == Type.LE || type == Type.LT) && bias == Bias.LT) {
+              continue;
+            }
+          }
+          String cmpInstruction;
+          if (bias == Bias.LT) {
+            cmpInstruction = "    cmpl-float v0, v0, v1";
+          } else {
+            cmpInstruction = "    cmpg-float v0, v0, v1";
+          }
+          DexEncodedMethod method = oneMethodApplication(
+              "int", Collections.emptyList(),
+              2,
+              "    const v0, 0x" + Integer.toHexString(Float.floatToRawIntBits(test.a)),
+              "    const v1, 0x" + Integer.toHexString(Float.floatToRawIntBits(test.b)),
+              cmpInstruction,
+              "    " + ifOpcode[type.ordinal()] + " v0, :label_2",
+              "    const v0, 0",
+              ":label_1",
+              "    return v0",
+              ":label_2",
+              "  const v0, 1",
+              "  goto :label_1"
+          );
+          DexCode code = method.getCode().asDexCode();
+          assertEquals(2, code.instructions.length);
+          int expected = test.results[type.ordinal()] ? 1 : 0;
+          assertConstValue(expected, code.instructions[0]);
+          assertTrue(code.instructions[1] instanceof Return);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void cmpDoubleFold() {
+    String[] ifOpcode = new String[6];
+    ifOpcode[Type.EQ.ordinal()] = "if-eqz";
+    ifOpcode[Type.NE.ordinal()] = "if-nez";
+    ifOpcode[Type.LE.ordinal()] = "if-lez";
+    ifOpcode[Type.GE.ordinal()] = "if-gez";
+    ifOpcode[Type.LT.ordinal()] = "if-ltz";
+    ifOpcode[Type.GT.ordinal()] = "if-gtz";
+
+    class DoubleTestData {
+
+      final double a;
+      final double b;
+      final boolean results[];
+
+      DoubleTestData(double a, double b) {
+        this.a = a;
+        this.b = b;
+        results = new boolean[6];
+        results[Type.EQ.ordinal()] = a == b;
+        results[Type.NE.ordinal()] = a != b;
+        results[Type.LE.ordinal()] = a <= b;
+        results[Type.GE.ordinal()] = a >= b;
+        results[Type.LT.ordinal()] = a < b;
+        results[Type.GT.ordinal()] = a > b;
+      }
+    }
+
+    double[] testValues = new double[]{
+        Double.NEGATIVE_INFINITY,
+        -100.0f,
+        -0.0f,
+        0.0f,
+        100.0f,
+        Double.POSITIVE_INFINITY,
+        Double.NaN
+    };
+
+    List<DoubleTestData> tests = new ArrayList<>();
+    for (int i = 0; i < testValues.length; i++) {
+      for (int j = 0; j < testValues.length; j++) {
+        tests.add(new DoubleTestData(testValues[i], testValues[j]));
+      }
+    }
+
+    for (DoubleTestData test : tests) {
+      for (Type type : Type.values()) {
+        for (Bias bias : Bias.values()) {
+          if (bias == Bias.NONE) {
+            // Bias NONE is only for long comparison.
+            continue;
+          }
+          if (Double.isNaN(test.a) || Double.isNaN(test.b)) {
+            // For NaN comparison only test with the bias that provide Java semantics.
+            // The Java Language Specification 4.2.3. Doubleing-Point Types, Formats, and Values
+            // says:
+            //
+            // The numerical comparison operators <, <=, >, and >= return false if either or both
+            // operands are NaN
+            if ((type == Type.GE || type == Type.GT) && bias == Bias.GT) {
+              continue;
+            }
+            if ((type == Type.LE || type == Type.LT) && bias == Bias.LT) {
+              continue;
+            }
+          }
+          String cmpInstruction;
+          if (bias == Bias.LT) {
+            cmpInstruction = "    cmpl-double v0, v0, v2";
+          } else {
+            cmpInstruction = "    cmpg-double v0, v0, v2";
+          }
+          DexEncodedMethod method = oneMethodApplication(
+              "int", Collections.emptyList(),
+              4,
+              "    const-wide v0, 0x" + Long.toHexString(Double.doubleToRawLongBits(test.a)) + "L",
+              "    const-wide v2, 0x" + Long.toHexString(Double.doubleToRawLongBits(test.b)) + "L",
+              cmpInstruction,
+              "    " + ifOpcode[type.ordinal()] + " v0, :label_2",
+              "    const v0, 0",
+              ":label_1",
+              "    return v0",
+              ":label_2",
+              "  const v0, 1",
+              "  goto :label_1"
+          );
+          DexCode code = method.getCode().asDexCode();
+          assertEquals(2, code.instructions.length);
+          int expected = test.results[type.ordinal()] ? 1 : 0;
+          assertConstValue(expected, code.instructions[0]);
+          assertTrue(code.instructions[1] instanceof Return);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void cmpLongFold() {
+    long[][] longValues = new long[][]{
+        {Long.MIN_VALUE, 1L},
+        {Long.MAX_VALUE, 1L},
+        {Long.MIN_VALUE, 0L},
+        {Long.MAX_VALUE, 0L},
+        {Long.MIN_VALUE, -1L},
+        {Long.MAX_VALUE, -1L},
+    };
+
+    for (long[] values : longValues) {
+      DexEncodedMethod method = oneMethodApplication(
+          "int", Collections.emptyList(),
+          4,
+          "    const-wide v0, 0x" + Long.toHexString(values[0]) + "L",
+          "    const-wide v2, 0x" + Long.toHexString(values[1]) + "L",
+          "    cmp-long v0, v0, v2",
+          "    return v0"
+      );
+      DexCode code = method.getCode().asDexCode();
+      assertEquals(2, code.instructions.length);
+      assertConstValue(Long.compare(values[0], values[1]), code.instructions[0]);
+      assertTrue(code.instructions[1] instanceof Return);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
new file mode 100644
index 0000000..c3f6d67
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -0,0 +1,396 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.If.Type;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class IfSimplificationTest extends SmaliTestBase {
+
+  @Test
+  public void ifZeroNeqZero() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        Collections.emptyList(),
+        1,
+        "  const v0, 0",
+        "  if-nez v0, :label_2",
+        ":label_1",
+        "  return v0",
+        ":label_2",
+        "  const v0, 1",
+        "  goto :label_1");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertEquals(0, ((Const4) code.instructions[0]).B);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void ifTwoEqZero() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        Collections.emptyList(),
+        1,
+        "  const v0, 2",
+        "  if-eqz v0, :label_2",
+        ":label_1",
+        "  return v0",
+        ":label_2",
+        "  const v0, 1",
+        "  goto :label_1");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertEquals(2, ((Const4) code.instructions[0]).B);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void b() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        Collections.singletonList("int"),
+        1,
+        "  const v0, 0",
+        "  if-nez v0, :label_2",
+        ":label_1",
+        "  return v0",
+        ":label_2",
+        "  if-nez p0, :label_3",
+        "  const v0, 1",
+        "  goto :label_1",
+        ":label_3",
+        "  const v0, 2",
+        "  goto :label_1");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertEquals(0, ((Const4) code.instructions[0]).B);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void c() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        Collections.singletonList("int"),
+        1,
+        "  const v0, 0",
+        "  if-nez v0, :label_2",
+        ":label_1",
+        "  return v0",
+        ":label_2",
+        "  if-nez p0, :label_3",
+        "  const v0, 1",
+        "  goto :label_1",
+        ":label_3",
+        "  const p0, 0",
+        "  goto :label_2");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertEquals(0, ((Const4) code.instructions[0]).B);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void d() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        Collections.singletonList("int"),
+        1,
+        "  const v0, 0",
+        "  if-nez v0, :label_2",
+        ":label_1",
+        "  return v0",
+        ":label_2",
+        "  if-nez p0, :label_3",
+        "  const v0, 1",
+        "  goto :label_4",
+        ":label_3",
+        "  const p0, 0",
+        "  goto :label_2",
+        ":label_4",
+        "  if-nez p0, :label_5",
+        "  const v0, 1",
+        "  goto :label_4",
+        ":label_5",
+        "  const p0, 0",
+        "  goto :label_2");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertEquals(0, ((Const4) code.instructions[0]).B);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void e() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        ImmutableList.of("int", "int", "int"),
+        1,
+        "  const v0, 0",
+        "  if-nez v0, :x",
+        "  const v0, 1",
+        "  if-nez p0, :x",
+        "  const v0, 2",
+        "  if-nez p1, :x",
+        "  const v0, 3",
+        "  if-nez p2, :return",
+        "  const v0, 4",
+        "  goto :return",
+        ":x",
+        "  add-int v0, v0, p0",
+        ":return",
+        "  return v0");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(12, code.instructions.length);
+    assertTrue(code.instructions[11] instanceof Return);
+  }
+
+  @Test
+  public void f() {
+    DexEncodedMethod method = oneMethodApplication(
+        "int",
+        Collections.singletonList("int"),
+        1,
+        "  const v0, 0",
+        "  if-nez v0, :label_2",
+        ":label_1",
+        "  return v0",
+        ":label_2",
+        "  const v0, 1",
+        "  goto :label_2");
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof Const4);
+    assertEquals(0, ((Const4) code.instructions[0]).B);
+    assertTrue(code.instructions[1] instanceof Return);
+  }
+
+  @Test
+  public void simplifyNonZeroTests() {
+    String[] ifOpcode = new String[6];
+    ifOpcode[Type.EQ.ordinal()] = "if-eq";
+    ifOpcode[Type.NE.ordinal()] = "if-ne";
+    ifOpcode[Type.LE.ordinal()] = "if-le";
+    ifOpcode[Type.GE.ordinal()] = "if-ge";
+    ifOpcode[Type.LT.ordinal()] = "if-lt";
+    ifOpcode[Type.GT.ordinal()] = "if-gt";
+
+    class TestData {
+
+      final int a;
+      final int b;
+      final boolean results[];
+
+      TestData(int a, int b) {
+        this.a = a;
+        this.b = b;
+        results = new boolean[6];
+        results[Type.EQ.ordinal()] = a == b;
+        results[Type.NE.ordinal()] = a != b;
+        results[Type.LE.ordinal()] = a <= b;
+        results[Type.GE.ordinal()] = a >= b;
+        results[Type.LT.ordinal()] = a < b;
+        results[Type.GT.ordinal()] = a > b;
+      }
+    }
+
+    int[] testValues = new int[]{
+        100,
+        1,
+        0,
+        -1,
+        100
+    };
+
+    List<TestData> tests = new ArrayList<>();
+    for (int i = 0; i < testValues.length; i++) {
+      for (int j = 0; j < testValues.length; j++) {
+        tests.add(new TestData(testValues[i], testValues[j]));
+      }
+    }
+
+    for (TestData test : tests) {
+      for (Type type : Type.values()) {
+        DexEncodedMethod method = oneMethodApplication(
+            "int",
+            Collections.singletonList("int"),
+            2,
+            "  const v0, 0x" + Integer.toHexString(test.a),
+            "  const v1, 0x" + Integer.toHexString(test.b),
+            "  " + ifOpcode[type.ordinal()] + " v0, v1, :label_2",
+            "  const v0, 0",
+            ":label_1",
+            "  return v0",
+            ":label_2",
+            "  const v0, 1",
+            "  goto :label_1");
+        DexCode code = method.getCode().asDexCode();
+        assertEquals(2, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof Const4);
+        int expected = test.results[type.ordinal()] ? 1 : 0;
+        assertEquals(expected, ((Const4) code.instructions[0]).B);
+        assertTrue(code.instructions[1] instanceof Return);
+      }
+    }
+  }
+
+  @Test
+  public void x() {
+    DexEncodedMethod method = oneMethodApplication(
+        "Test",
+        Lists.newArrayList("Test", "java.lang.String[]", "java.lang.String",
+            "java.lang.String[]", "java.lang.String"),
+        10,
+        "          const/4             v4, 0x00  # 0",
+        "          invoke-virtual      { v10 }, LTest;->a()LTest;",
+        "          if-nez              v4, :label_8",
+        "          move-object         v0, v4",
+        "      :label_7",
+        "          return-object       v0",
+        "      :label_8",
+        "          invoke-static       { v14 }, LTest;->a([Ljava/lang/String;)LTest;",
+        "          move-result-object  v2",
+        "          invoke-virtual      { v2 }, LTest;->a()Z",
+        "          move-result         v0",
+        "          if-nez              v0, :label_20",
+        "          move-object         v0, v4",
+        "          goto                :label_7",
+        "      :label_20",
+        "          iget-wide           v0, v2, LTest;->a:J",
+        "          iget-wide           v6, v2, LTest;->b:J",
+        "          invoke-virtual      { v2 }, LTest;->c()Z",
+        "          move-result         v2",
+        "          if-eqz              v2, :label_33",
+        "          invoke-virtual      { v4 }, LTest;->a()V",
+        "      :label_33",
+        "          new-instance        v5, LTest;",
+        "          sget-object         v2, LTest;->a:[Ljava/lang/String;",
+        "          invoke-direct       { v5, v2 }, LTest;-><init>([Ljava/lang/String;)V",
+        "          invoke-virtual      { v10 }, LTest;->a()LTest;",
+        "          invoke-virtual      { v4, v0, v1, v6, v7 }, LTest;->a(JJ)Ljava/util/List;",
+        "          move-result-object  v2",
+        "          invoke-interface    { v2 }, Ljava/util/List;->iterator()Ljava/util/Iterator;",
+        "          move-result-object  v6",
+        "          move-wide           v2, v0",
+        "      :label_52",
+        "          invoke-interface    { v6 }, Ljava/util/Iterator;->hasNext()Z",
+        "          move-result         v0",
+        "          if-eqz              v0, :label_107",
+        "          invoke-interface    { v6 }, Ljava/util/Iterator;->next()Ljava/lang/Object;",
+        "          move-result-object  v0",
+        "          check-cast          v0, LTest;",
+        "          const-wide/16       v8, 0x0000000000000001  # 1",
+        "          add-long/2addr      v2, v8",
+        "          invoke-virtual      { v5 }, LTest;->newRow()LTest;",
+        "          move-result-object  v1",
+        "          invoke-static       { v2, v3 }, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;",
+        "          move-result-object  v7",
+        "          invoke-virtual      { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
+        "          move-result-object  v1",
+        "          const-string        v7, \"add\"",
+        "          invoke-virtual      { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
+        "          move-result-object  v1",
+        "          iget-object         v7, v0, LTest;->a:Ljava/lang/String;",
+        "          invoke-virtual      { v1, v7 }, LTest;->a(Ljava/lang/Object;)LTest;",
+        "          move-result-object  v1",
+        "          iget                v7, v0, LTest;->b:I",
+        "          invoke-static       { v7 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+        "          move-result-object  v7",
+        "          invoke-virtual      { v1, v7 }, LTest;->add(Ljava/lang/Object;)LTest;",
+        "          move-result-object  v1",
+        "          iget-object         v0, v0, LTest;->a:Ljava/lang/String;",
+        "          invoke-virtual      { v1, v0 }, LTest;->add(Ljava/lang/Object;)LTest;",
+        "          goto                :label_52",
+        "      :label_107",
+        "          iget-object         v0, v4, LTest;->a:LTest;",
+        "          const-string        v1, \"text 1\"",
+        "          const/4             v2, 0x00  # 0",
+        "          invoke-virtual      { v0, v1, v2 }, LTest;->a(Ljava/lang/String;I)LTest;",
+        "          move-result-object  v0",
+        "          const-string        v1, \"text 2\"",
+        "          const-string        v2, \"\"",
+        "          invoke-interface    { v0, v1, v2 }, LTest;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+        "          move-result-object  v0",
+        "          invoke-static       { v5, v0 }, LTest;->a(LTest;Ljava/lang/String;)LTest;",
+        "          move-result-object  v0",
+        "          goto                :label_7"
+    );
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(3, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof InvokeVirtual);
+    assertTrue(code.instructions[1] instanceof Const4);
+    assertEquals(0, ((Const4) code.instructions[1]).B);
+    assertTrue(code.instructions[2] instanceof ReturnObject);
+  }
+
+  @Test
+  public void y() {
+    DexEncodedMethod method = oneMethodApplication(
+        "boolean",
+        Lists.newArrayList("Test", "java.lang.Object"),
+        6,
+        "      const-wide/16       v4, 0x0000000000000000L  # 0",
+        "      const/4             v0, 0x01  # 1",
+        "      const/4             v3, 0x00  # 0",
+        "      const/4             v1, 0x00  # 0",
+        "      if-ne               v6, v7, :label_8",
+        "    :label_7",
+        "      return              v0",
+        "    :label_8",
+        "      if-nez              v7, :label_12",
+        "      move                v0, v1",
+        "      goto                :label_7",
+        "    :label_12",
+        "      instance-of         v2, v7, LTest;",
+        "      if-nez              v2, :label_18",
+        "      move                v0, v1",
+        "      goto                :label_7",
+        "    :label_18",
+        "      check-cast          v7, LTest;",
+        "      cmp-long            v2, v4, v4",
+        "      if-nez              v2, :label_50",
+        "      invoke-static       { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
+        "      move-result         v2",
+        "      if-eqz              v2, :label_50",
+        "      invoke-static       { v3, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
+        "      move-result         v2",
+        "      if-eqz              v2, :label_50",
+        "      invoke-static       { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+        "      move-result-object  v2",
+        "      invoke-static       { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+        "      move-result-object  v3",
+        "      invoke-static       { v2, v3 }, LTest;->a(Ljava/lang/Object;Ljava/lang/Object;)Z",
+        "      move-result         v2",
+        "      if-nez              v2, :label_7",
+        "    :label_50",
+        "      move                v0, v1",
+        "      goto                :label_7"
+    );
+    DexCode code = method.getCode().asDexCode();
+    // TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
+    // expectation might need changing with other optimizations.
+    assertEquals(29, code.instructions.length);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/JumboStringTest.java b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
new file mode 100644
index 0000000..79ef0b9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/JumboStringTest.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class JumboStringTest extends SmaliTestBase {
+
+  @Test
+  public void test() {
+    StringBuilder builder = new StringBuilder();
+    StringBuilder expectedBuilder = new StringBuilder();
+    builder.append("    new-instance         v0, Ljava/lang/StringBuilder;\n");
+    builder.append("    invoke-direct        { v0 }, Ljava/lang/StringBuilder;-><init>()V\n");
+    for (int i = 0; i <= 0xffff + 2; i++) {
+      String prefixed = StringUtils.zeroPrefix(i, 5);
+      expectedBuilder.append(prefixed);
+      expectedBuilder.append("\n");
+      builder.append("  const-string         v1, \"" + prefixed + "\\n\"\n");
+      builder.append(
+          "  invoke-virtual       { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n");
+    }
+    builder.append(
+        "    invoke-virtual       { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n");
+    builder.append("    move-result-object   v0\n");
+    builder.append("    return-object               v0\n");
+
+    SmaliBuilder smaliBuilder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = smaliBuilder.addStaticMethod(
+        "java.lang.String",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of(),
+        2,
+        builder.toString()
+    );
+
+    smaliBuilder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    invoke-static       {}, LTest;->method()Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(smaliBuilder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    String result = runArt(processedApplication, options);
+
+    assertEquals(expectedBuilder.toString(), result);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
new file mode 100644
index 0000000..4808f33
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -0,0 +1,1468 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.ConstWide;
+import com.android.tools.r8.code.ConstWideHigh16;
+import com.android.tools.r8.code.DivInt;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.MoveResult;
+import com.android.tools.r8.code.MoveResultWide;
+import com.android.tools.r8.code.Return;
+import com.android.tools.r8.code.ReturnObject;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.ReturnWide;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Test;
+
+public class OutlineTest extends SmaliTestBase {
+
+  DexEncodedMethod getInvokedMethod(DexApplication application, InvokeStatic invoke) {
+    DexInspector inspector = new DexInspector(application);
+    ClassSubject clazz = inspector.clazz(invoke.getMethod().holder.toSourceString());
+    assertTrue(clazz.isPresent());
+    DexMethod invokedMethod = invoke.getMethod();
+    invokedMethod.proto.returnType.toSourceString();
+    MethodSubject method = clazz.method(
+        invokedMethod.proto.returnType.toSourceString(),
+        invokedMethod.name.toString(),
+        Arrays.stream(invokedMethod.proto.parameters.values)
+            .map(p -> p.toSourceString())
+            .collect(Collectors.toList()));
+    assertTrue(method.isPresent());
+    return method.getMethod();
+  }
+
+  String firstOutlineMethodName(InternalOptions options) {
+    StringBuilder builder = new StringBuilder(options.outline.className);
+    builder.append('.');
+    builder.append(options.outline.methodPrefix);
+    builder.append("0");
+    return builder.toString();
+  }
+
+  MethodSignature firstOutlineMethodSignature(
+      String returnType, List<String> parameterTypes, InternalOptions options) {
+    return new MethodSignature(
+        options.outline.className, options.outline.methodPrefix + "0", returnType, parameterTypes);
+  }
+
+  boolean isOutlineMethodName(InternalOptions options, String qualifiedName) {
+    StringBuilder builder = new StringBuilder(options.outline.className);
+    builder.append('.');
+    builder.append(options.outline.methodPrefix);
+    return qualifiedName.indexOf(builder.toString()) == 0;
+  }
+
+  @Test
+  public void a() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "java.lang.String";
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        2,
+        "    move                v0, p0",
+        "    const-string        v1, \"Test\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    for (int i = 2; i < 6; i++) {
+      InternalOptions options = new InternalOptions();
+      options.outline.threshold = 1;
+      options.outline.minSize = i;
+      options.outline.maxSize = i;
+
+      DexApplication originalApplication = buildApplication(builder, options);
+      DexApplication processedApplication = processApplication(originalApplication, options);
+      assertEquals(2, Iterables.size(processedApplication.classes()));
+
+      // Return the processed method for inspection.
+      DexEncodedMethod method = getMethod(processedApplication, signature);
+
+      DexCode code = method.getCode().asDexCode();
+      assertTrue(code.instructions[0] instanceof ConstString);
+      assertTrue(code.instructions[1] instanceof InvokeStatic);
+      InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+      assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+      // Run code and check result.
+      String result = runArt(processedApplication, options);
+      assertEquals("TestTestTestTest", result);
+    }
+  }
+
+  @Test
+  public void b() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "java.lang.String";
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        2,
+        "    move                v0, p0",
+        "    const-string        v1, \"Test1\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    const-string        v1, \"Test2\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    const-string        v1, \"Test3\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    const-string        v1, \"Test4\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    for (int i = 2; i < 6; i++) {
+      InternalOptions options = new InternalOptions();
+      options.outline.threshold = 1;
+      options.outline.minSize = i;
+      options.outline.maxSize = i;
+
+      DexApplication originalApplication = buildApplication(builder, options);
+      DexApplication processedApplication = processApplication(originalApplication, options);
+      assertEquals(2, Iterables.size(processedApplication.classes()));
+
+      // Return the processed method for inspection.
+      DexEncodedMethod method = getMethod(processedApplication, signature);
+
+      DexCode code = method.getCode().asDexCode();
+
+      // Up to 4 const instructions before the invoke of the outline.
+      int firstOutlineInvoke = Math.min(i, 4);
+      for (int j = 0; j < firstOutlineInvoke; j++) {
+        assertTrue(code.instructions[j] instanceof ConstString);
+      }
+      assertTrue(code.instructions[firstOutlineInvoke] instanceof InvokeStatic);
+      InvokeStatic invoke = (InvokeStatic) code.instructions[firstOutlineInvoke];
+      assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+      // Run code and check result.
+      String result = runArt(processedApplication, options);
+      assertEquals("Test1Test2Test3Test4", result);
+    }
+  }
+
+  @Test
+  public void c() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    // Method with const instructions after the outline.
+    String returnType = "int";
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        2,
+        "    move                v0, p0",
+        "    const-string        v1, \"Test\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    const               v0, 0",
+        "    const               v1, 1",
+        "    add-int             v1, v1, v0",
+        "    return              v1"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+
+    DexCode code = method.getCode().asDexCode();
+    assertTrue(code.instructions[0] instanceof ConstString);
+    assertTrue(code.instructions[1] instanceof InvokeStatic);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+    assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("1", result);
+  }
+
+  @Test
+  public void d() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    // Method with mixed use of arguments and locals.
+    String returnType = "java.lang.String";
+    List<String> parameters = ImmutableList.of(
+        "java.lang.StringBuilder", "java.lang.String", "java.lang.String");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        2,
+        "    invoke-virtual      { p0, p1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    const-string        v0, \"Test1\"",
+        "    invoke-virtual      { p0, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    invoke-virtual      { p0, p2 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    const-string        v1, \"Test2\"",
+        "    invoke-virtual      { p0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    const-string        v1, \"Test3\"",
+        "    invoke-virtual      { p0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    invoke-virtual      { p0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    return-object  v1"
+    );
+
+    builder.addMainMethod(
+        4,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    const-string        v2, \"TestX\"",
+        "    const-string        v3, \"TestY\"",
+        "    invoke-static       { v1, v2, v3 }, LTest;->method(Ljava/lang/StringBuilder;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+
+    DexCode code = method.getCode().asDexCode();
+    assertTrue(code.instructions[0] instanceof ConstString);
+    assertTrue(code.instructions[1] instanceof ConstString);
+    assertTrue(code.instructions[2] instanceof InvokeStatic);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[2];
+    assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("TestXTest1TestYTest2Test3", result);
+  }
+
+  @Test
+  public void longArguments() throws Throwable {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "java.lang.String";
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        3,
+        "    move                v0, p0",
+        "    const-wide          v1, 0x7fffffff00000000L",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(J)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    for (int i = 2; i < 4; i++) {
+      InternalOptions options = new InternalOptions();
+      options.outline.threshold = 1;
+      options.outline.minSize = i;
+      options.outline.maxSize = i;
+
+      DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+      DexApplication processedApplication = processApplication(originalApplication, options);
+      assertEquals(2, Iterables.size(processedApplication.classes()));
+
+      // Return the processed method for inspection.
+      DexEncodedMethod method = getMethod(processedApplication, signature);
+
+      DexCode code = method.getCode().asDexCode();
+      assertTrue(code.instructions[0] instanceof ConstWide);
+      if (i < 3) {
+        assertTrue(code.instructions[1] instanceof InvokeStatic);
+        InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      } else {
+        assertTrue(code.instructions[1] instanceof InvokeVirtual);
+        assertTrue(code.instructions[2] instanceof InvokeVirtual);
+        assertTrue(code.instructions[3] instanceof InvokeStatic);
+        InvokeStatic invoke = (InvokeStatic) code.instructions[3];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      }
+
+      // Run code and check result.
+      String result = runArt(processedApplication, options);
+      StringBuilder resultBuilder = new StringBuilder();
+      for (int j = 0; j < 4; j++) {
+        resultBuilder.append(0x7fffffff00000000L);
+      }
+      assertEquals(resultBuilder.toString(), result);
+    }
+  }
+
+  @Test
+  public void doubleArguments() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "java.lang.String";
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        3,
+        "    move                v0, p0",
+        "    const-wide          v1, 0x3ff0000000000000L",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/lang/StringBuilder;->append(D)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    for (int i = 2; i < 4; i++) {
+      InternalOptions options = new InternalOptions();
+      options.outline.threshold = 1;
+      options.outline.minSize = i;
+      options.outline.maxSize = i;
+
+      DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+      DexApplication processedApplication = processApplication(originalApplication, options);
+      assertEquals(2, Iterables.size(processedApplication.classes()));
+
+      // Return the processed method for inspection.
+      DexEncodedMethod method = getMethod(processedApplication, signature);
+
+      DexCode code = method.getCode().asDexCode();
+      assertTrue(code.instructions[0] instanceof ConstWideHigh16);
+      if (i < 3) {
+        assertTrue(code.instructions[1] instanceof InvokeStatic);
+        InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      } else {
+        assertTrue(code.instructions[1] instanceof InvokeVirtual);
+        assertTrue(code.instructions[2] instanceof InvokeVirtual);
+        assertTrue(code.instructions[3] instanceof InvokeStatic);
+        InvokeStatic invoke = (InvokeStatic) code.instructions[3];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      }
+
+      // Run code and check result.
+      String result = runArt(processedApplication, options);
+      StringBuilder resultBuilder = new StringBuilder();
+      for (int j = 0; j < 4; j++) {
+        resultBuilder.append(1.0d);
+      }
+      assertEquals(resultBuilder.toString(), result);
+    }
+  }
+
+  @Test
+  public void invokeStatic() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "void";
+    List<String> parameters = ImmutableList.of("java.lang.StringBuilder", "int");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        1,
+        "    invoke-virtual      { p0, p1 }, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { p0, p1 }, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    return-void"
+    );
+
+    MethodSignature mainSignature = builder.addMainMethod(
+        2,
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    const/4             v1, 0x1",
+        "    invoke-static       { v0, v1 }, LTest;->method(Ljava/lang/StringBuilder;I)V",
+        "    const/4             v1, 0x2",
+        "    invoke-static       { v0, v1 }, LTest;->method(Ljava/lang/StringBuilder;I)V",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    for (int i = 2; i < 6; i++) {
+      InternalOptions options = new InternalOptions();
+      options.outline.threshold = 1;
+      options.outline.minSize = i;
+      options.outline.maxSize = i;
+
+      DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+      DexApplication processedApplication = processApplication(originalApplication, options);
+      assertEquals(2, Iterables.size(processedApplication.classes()));
+
+      // Return the processed main method for inspection.
+      DexEncodedMethod mainMethod = getMethod(processedApplication, mainSignature);
+      DexCode mainCode = mainMethod.getCode().asDexCode();
+
+      if (i == 2 || i == 3) {
+        assert mainCode.instructions.length == 10;
+      } else if (i == 4) {
+        assert mainCode.instructions.length == 9;
+      } else {
+        assert i == 5;
+        assert mainCode.instructions.length == 7;
+      }
+      if (i == 2) {
+        InvokeStatic invoke = (InvokeStatic) mainCode.instructions[4];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      } else if (i == 3) {
+        InvokeStatic invoke = (InvokeStatic) mainCode.instructions[1];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      } else {
+        assert i == 4 || i == 5;
+        InvokeStatic invoke = (InvokeStatic) mainCode.instructions[2];
+        assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+      }
+
+      // Run code and check result.
+      String result = runArt(processedApplication, options);
+      assertEquals("1122", result);
+    }
+  }
+
+  @Test
+  public void constructor() throws IOException, RecognitionException {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature1 = builder.addStaticMethod(
+        "java.lang.String",
+        "method1",
+        Collections.emptyList(),
+        3,
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    const-string        v1, \"Test1\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    MethodSignature signature2 = builder.addStaticMethod(
+        "java.lang.String",
+        "method2",
+        Collections.emptyList(),
+        3,
+        "    const/4             v1, 7",
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v0, v1 }, Ljava/lang/StringBuilder;-><init>(I)V",
+        "    const-string        v1, \"Test2\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    MethodSignature mainSignature = builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    invoke-static       {}, LTest;->method1()Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    invoke-static       {}, LTest;->method2()Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 7;
+    options.outline.maxSize = 7;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    DexCode code1 = getMethod(processedApplication, signature1).getCode().asDexCode();
+    assertEquals(4, code1.instructions.length);
+    assertTrue(code1.instructions[1] instanceof InvokeStatic);
+    InvokeStatic invoke1 = (InvokeStatic) code1.instructions[1];
+    assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+
+    DexCode code2 = getMethod(processedApplication, signature2).getCode().asDexCode();
+    assertEquals(5, code2.instructions.length);
+    assertTrue(code2.instructions[2] instanceof InvokeStatic);
+    InvokeStatic invoke2 = (InvokeStatic) code2.instructions[2];
+    assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("Test1Test1Test1Test1Test2Test2Test2Test2", result);
+  }
+
+  @Test
+  public void constructorDontSplitNewInstanceAndInit() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = builder.addStaticMethod(
+        "java.lang.String",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("java.lang.StringBuilder"),
+        2,
+        "    const-string        v0, \"Test1\"",
+        "    invoke-virtual      { p0, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    invoke-virtual      { p0, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  p0",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    const-string        v0, \"Test2\"",
+        "    invoke-virtual      { v1, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v1, v0 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v1 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    MethodSignature mainSignature = builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    for (int i = 2; i < 8; i++) {
+      InternalOptions options = new InternalOptions();
+      options.outline.threshold = 1;
+      options.outline.minSize = i;
+      options.outline.maxSize = i;
+
+      DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+      DexApplication processedApplication = processApplication(originalApplication, options);
+      assertEquals(2, Iterables.size(processedApplication.classes()));
+
+      DexCode code = getMethod(processedApplication, signature).getCode().asDexCode();
+      InvokeStatic invoke;
+      int outlineInstructionIndex;
+      switch (i) {
+        case 2:
+        case 4:
+          outlineInstructionIndex = 1;
+          break;
+        case 3:
+          outlineInstructionIndex = 4;
+          break;
+        default:
+          outlineInstructionIndex = 2;
+      }
+      invoke = (InvokeStatic) code.instructions[outlineInstructionIndex];
+      assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+      // Run code and check result.
+      String result = runArt(processedApplication, options);
+      assertEquals("Test2Test2", result);
+    }
+  }
+
+  @Test
+  public void outlineWithoutArguments() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature1 = builder.addStaticMethod(
+        "java.lang.String",
+        DEFAULT_METHOD_NAME,
+        Collections.emptyList(),
+        1,
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    MethodSignature mainSignature = builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    invoke-static       {}, LTest;->method()Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 3;
+    options.outline.maxSize = 3;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    DexCode code = getMethod(processedApplication, signature1).getCode().asDexCode();
+    InvokeStatic invoke;
+    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    invoke = (InvokeStatic) code.instructions[0];
+    assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("", result);
+  }
+
+  @Test
+  public void outlineDifferentReturnType() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+
+    // The naming of the methods in this test is important. The method name that don't use the
+    // output from StringBuilder.toString must sort before the method name that does.
+    String returnType1 = "void";
+    MethodSignature signature1 = builder.addStaticMethod(
+        returnType1,
+        "method1",
+        parameters,
+        2,
+        "    move                v0, p0",
+        "    const-string        v1, \"Test\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    return-void"
+    );
+
+    String returnType2 = "java.lang.String";
+    MethodSignature signature2 = builder.addStaticMethod(
+        returnType2,
+        "method2",
+        parameters,
+        2,
+        "    move                v0, p0",
+        "    const-string        v1, \"Test\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method1(Ljava/lang/StringBuilder;)V",
+        "    invoke-static       { v1 }, LTest;->method2(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v2",
+        "    invoke-virtual      { v0, v2 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 3;
+    options.outline.maxSize = 3;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Check that three outlining methods was created.
+    DexInspector inspector = new DexInspector(processedApplication);
+    ClassSubject clazz = inspector.clazz(options.outline.className);
+    assertTrue(clazz.isPresent());
+    assertEquals(3, clazz.getDexClass().directMethods.length);
+    // Collect the return types of the putlines for the body of method1 and method2.
+    List<DexType> r = new ArrayList<>();
+    for (int i = 0; i < clazz.getDexClass().directMethods.length; i++) {
+      if (clazz.getDexClass().directMethods[i].getCode().asDexCode().instructions[0]
+          instanceof InvokeVirtual) {
+        r.add(clazz.getDexClass().directMethods[i].method.proto.returnType);
+      }
+    }
+    assert r.size() == 2;
+    DexType r1 = r.get(0);
+    DexType r2 = r.get(1);
+    DexItemFactory factory = processedApplication.dexItemFactory;
+    assertTrue(r1 == factory.voidType && r2 == factory.stringType ||
+        r1 == factory.stringType && r2 == factory.voidType);
+
+    // Run the code.
+    String result = runArt(processedApplication, options);
+    assertEquals("TestTestTestTest", result);
+  }
+
+  @Test
+  public void outlineMultipleTimes() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    String returnType = "java.lang.String";
+    List<String> parameters = Collections.singletonList("java.lang.StringBuilder");
+    MethodSignature signature = builder.addStaticMethod(
+        returnType,
+        DEFAULT_METHOD_NAME,
+        parameters,
+        2,
+        "    move                v0, p0",
+        "    const-string        v1, \"Test\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0, v1 }, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+        "    move-result-object  v0",
+        "    invoke-virtual      { v0 }, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;",
+        "    move-result-object  v0",
+        "    return-object       v0"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    new-instance        v1, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v1 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    invoke-static       { v1 }, LTest;->method(Ljava/lang/StringBuilder;)Ljava/lang/String;",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 3;
+    options.outline.maxSize = 3;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    final int count = 10;
+    // Process the application several times. Each time will outline the previous outline.
+    for (int i = 0; i < count; i++) {
+      // Build a new application with the Outliner class.
+      DexApplication.Builder appBuilder =
+          new DexApplication.Builder(processedApplication);
+      originalApplication = appBuilder.build();
+      processedApplication = processApplication(originalApplication, options);
+      assertEquals(i + 3, Iterables.size(processedApplication.classes()));
+    }
+
+    // Process the application several times. No more outlining as threshold has been raised.
+    options.outline.threshold = 2;
+    for (int i = 0; i < count; i++) {
+      // Build a new application with the Outliner class.
+      DexApplication.Builder appBuilder =
+          new DexApplication.Builder(processedApplication);
+      originalApplication = appBuilder.build();
+      processedApplication = processApplication(originalApplication, options);
+      assertEquals(count - 1 + 3, Iterables.size(processedApplication.classes()));
+    }
+
+    // Run the application with several levels of outlining.
+    String result = runArt(processedApplication, options);
+    assertEquals("TestTestTestTest", result);
+  }
+
+  @Test
+  public void outlineReturnLong() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = builder.addStaticMethod(
+        "long",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int"),
+        2,
+        "    new-instance        v0, Ljava/util/GregorianCalendar;",
+        "    invoke-direct       { v0 }, Ljava/util/GregorianCalendar;-><init>()V",
+        "    invoke-virtual      { v0, p0, p0 }, Ljava/util/Calendar;->set(II)V",
+        "    invoke-virtual      { v0, p0, p0 }, Ljava/util/Calendar;->set(II)V",
+        "    invoke-virtual      { v0 }, Ljava/util/Calendar;->getTimeInMillis()J",
+        "    move-result-wide    v0",
+        "    return-wide         v0"
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 0",
+        "    invoke-static       { v1 }, LTest;->method(I)J",
+        "    move-result-wide    v1",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/io/PrintStream;->print(J)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 5;
+    options.outline.maxSize = 5;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    // The calls to set, set and getTimeInMillis was outlined.
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(3, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    assertTrue(code.instructions[1] instanceof MoveResultWide);
+    assertTrue(code.instructions[2] instanceof ReturnWide);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+    assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+    // Run the code and expect a parsable long.
+    String result = runArt(processedApplication, options);
+    Long.parseLong(result);
+  }
+
+  @Test
+  public void outlineArrayType() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticMethod(
+        "void",
+        "addToList",
+        ImmutableList.of("java.util.List", "int[]"),
+        0,
+        "    invoke-interface    { p0, p1 }, Ljava/util/List;->add(Ljava/lang/Object;)Z",
+        "    return-void "
+    );
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int[]",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int[]", "int[]"),
+        1,
+        "    new-instance        v0, Ljava/util/ArrayList;",
+        "    invoke-direct       { v0 }, Ljava/util/ArrayList;-><init>()V",
+        "    invoke-static       { v0, p0 }, LTest;->addToList(Ljava/util/List;[I)V",
+        "    invoke-static       { v0, p1 }, LTest;->addToList(Ljava/util/List;[I)V",
+        "    return-object       p0"
+    );
+
+    builder.addMainMethod(
+        3,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 0",
+        "    invoke-static       { v1, v1 }, LTest;->method([I[I)[I",
+        "    move-result-object  v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 4;
+    options.outline.maxSize = 4;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    assertTrue(code.instructions[1] instanceof ReturnObject);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+    assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("null", result);
+  }
+
+  @Test
+  public void outlineArithmeticBinop() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticMethod(
+        "void",
+        "addToList",
+        ImmutableList.of("java.util.List", "int[]"),
+        0,
+        "    invoke-interface    { p0, p1 }, Ljava/util/List;->add(Ljava/lang/Object;)Z",
+        "    return-void "
+    );
+
+    MethodSignature signature1 = builder.addStaticMethod(
+        "int",
+        "method1",
+        ImmutableList.of("int", "int"),
+        1,
+        "    add-int             v0, v1, v2",
+        "    sub-int             v0, v0, v1",
+        "    mul-int             v0, v2, v0",
+        "    div-int             v0, v0, v1",
+        "    return              v0"
+    );
+
+    MethodSignature signature2 = builder.addStaticMethod(
+        "int",
+        "method2",
+        ImmutableList.of("int", "int"),
+        1,
+        "    add-int             v0, v2, v1",
+        "    sub-int             v0, v0, v1",
+        "    mul-int             v0, v0, v2",
+        "    div-int             v0, v0, v1",
+        "    return              v0"
+    );
+
+    builder.addMainMethod(
+        4,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 1",
+        "    const/4             v2, 2",
+        "    invoke-static       { v1, v2 }, LTest;->method1(II)I",
+        "    move-result         v4",
+        "    invoke-virtual      { v0, v4 }, Ljava/io/PrintStream;->print(I)V",
+        "    invoke-static       { v1, v2 }, LTest;->method2(II)I",
+        "    move-result         v4",
+        "    invoke-virtual      { v0, v4 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 4;
+    options.outline.maxSize = 4;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method1 = getMethod(processedApplication, signature1);
+    DexCode code1 = method1.getCode().asDexCode();
+    assertEquals(3, code1.instructions.length);
+    assertTrue(code1.instructions[0] instanceof InvokeStatic);
+    assertTrue(code1.instructions[1] instanceof MoveResult);
+    assertTrue(code1.instructions[2] instanceof Return);
+    InvokeStatic invoke1 = (InvokeStatic) code1.instructions[0];
+    assertTrue(isOutlineMethodName(options, invoke1.getMethod().qualifiedName()));
+
+    DexEncodedMethod method2 = getMethod(processedApplication, signature2);
+    DexCode code2 = method2.getCode().asDexCode();
+    assertTrue(code2.instructions[0] instanceof InvokeStatic);
+    InvokeStatic invoke2 = (InvokeStatic) code2.instructions[0];
+    assertEquals(invoke1.getMethod().qualifiedName(), invoke2.getMethod().qualifiedName());
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("44", result);
+  }
+
+  @Test
+  public void outlineWithHandler() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticMethod(
+        "void",
+        "addToList",
+        ImmutableList.of("java.util.List", "int[]"),
+        0,
+        "    invoke-interface    { p0, p1 }, Ljava/util/List;->add(Ljava/lang/Object;)Z",
+        "    return-void "
+    );
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        "method",
+        ImmutableList.of("int", "int"),
+        1,
+        "    :try_start",
+        // Throwing instruction to ensure the handler range does not get collapsed.
+        "    div-int             v0, v1, v2",
+        "    add-int             v0, v1, v2",
+        "    sub-int             v0, v0, v1",
+        "    mul-int             v0, v2, v0",
+        "    div-int             v0, v0, v1",
+        "    :try_end",
+        "    :return",
+        "    return              v0",
+        "    .catch Ljava/lang/ArithmeticException; {:try_start .. :try_end} :catch",
+        "    :catch",
+        "    const/4             v0, -1",
+        "    goto :return"
+    );
+
+    builder.addMainMethod(
+        4,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 1",
+        "    const/4             v2, 2",
+        "    invoke-static       { v1, v2 }, LTest;->method(II)I",
+        "    move-result         v4",
+        "    invoke-virtual      { v0, v4 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 3;  // Outline add, sub and mul.
+    options.outline.maxSize = 3;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(7, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof DivInt);
+    assertTrue(code.instructions[1] instanceof InvokeStatic);
+    assertTrue(code.instructions[2] instanceof MoveResult);
+    assertTrue(code.instructions[3] instanceof DivInt);
+    assertTrue(code.instructions[4] instanceof Return);
+    assertTrue(code.instructions[5] instanceof Const4);
+    assertTrue(code.instructions[6] instanceof Return);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[1];
+    assertTrue(isOutlineMethodName(options, invoke.getMethod().qualifiedName()));
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("4", result);
+  }
+
+  @Test
+  public void outlineUnusedOutValue() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    // The result from neither the div-int is never used.
+    MethodSignature signature = builder.addStaticMethod(
+        "void",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int", "int"),
+        1,
+        "    div-int             v0, p0, p1",
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    invoke-direct       { v0 }, Ljava/lang/StringBuilder;-><init>()V",
+        "    return-void"
+    );
+
+    builder.addMainMethod(
+        2,
+        "    const               v0, 1",
+        "    const               v1, 2",
+        "    invoke-static       { v0, v1 }, LTest;->method(II)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 3;
+    options.outline.maxSize = 3;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    assertTrue(code.instructions[1] instanceof ReturnVoid);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+    assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("", result);
+  }
+
+  @Test
+  public void outlineUnusedNewInstanceOutValue() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    // The result from the new-instance instructions are never used (<init> is not even called).
+    MethodSignature signature = builder.addStaticMethod(
+        "void",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of(),
+        1,
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    new-instance        v0, Ljava/lang/StringBuilder;",
+        "    return-void"
+    );
+
+    builder.addMainMethod(
+        0,
+        "    invoke-static       { }, LTest;->method()V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 1;
+    options.outline.minSize = 3;
+    options.outline.maxSize = 3;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    DexCode code = method.getCode().asDexCode();
+    assertEquals(2, code.instructions.length);
+    assertTrue(code.instructions[0] instanceof InvokeStatic);
+    assertTrue(code.instructions[1] instanceof ReturnVoid);
+    InvokeStatic invoke = (InvokeStatic) code.instructions[0];
+    assertEquals(firstOutlineMethodName(options), invoke.getMethod().qualifiedName());
+
+    // Run code and check result.
+    String result = runArt(processedApplication, options);
+    assertEquals("", result);
+  }
+
+  @Test
+  public void regress33733666() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addStaticMethod(
+        "void",
+        DEFAULT_METHOD_NAME,
+        Collections.emptyList(),
+        4,
+        "    new-instance        v0, Ljava/util/Hashtable;",
+        "    invoke-direct       { v0 }, Ljava/util/Hashtable;-><init>()V",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#home\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x01  # 1",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \" http://schemas.google.com/g/2005#work\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x02  # 2",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#other\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x03  # 3",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#primary\"",
+        "    const/4             v2, 0x04  # 4",
+        "    invoke-static       { v2 }, Ljava/lang/Byte;->valueOf(B)Ljava/lang/Byte;",
+        "    move-result-object  v2",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    sput-object         v0, LA;->A:Ljava/util/Hashtable;",
+        "    invoke-static       { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+        "    move-result-object  v0",
+        "    sput-object         v0, LA;->B:Ljava/util/Hashtable;",
+        "    new-instance        v0, Ljava/util/Hashtable;",
+        "    invoke-direct       { v0 }, Ljava/util/Hashtable;-><init>()V",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#home\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x02  # 2",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#mobile\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x01  # 1",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#pager\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x06  # 6",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#work\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x03  # 3",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#home_fax\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x05  # 5",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#work_fax\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x04  # 4",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#other\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x07  # 7",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    sput-object         v0, LA;->C:Ljava/util/Hashtable;",
+        "    invoke-static       { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+        "    move-result-object  v0",
+        "    sput-object         v0, LA;->D:Ljava/util/Hashtable;",
+        "    new-instance        v0, Ljava/util/Hashtable;",
+        "    invoke-direct       { v0 }, Ljava/util/Hashtable;-><init>()V",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#home\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x01  # 1",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#work\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x02  # 2",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#other\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x03  # 3",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    sput-object         v0, LA;->E:Ljava/util/Hashtable;",
+        "    invoke-static       { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+        "    move-result-object  v0",
+        "    sput-object         v0, LA;->F:Ljava/util/Hashtable;",
+        "    new-instance        v0, Ljava/util/Hashtable;",
+        "    invoke-direct       { v0 }, Ljava/util/Hashtable;-><init>()V",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#home\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x01  # 1",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#work\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x02  # 2",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#other\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x03  # 3",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    sput-object         v0, LA;->G:Ljava/util/Hashtable;",
+        "    invoke-static       { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+        "    move-result-object  v0",
+        "    sput-object         v0, LA;->H:Ljava/util/Hashtable;",
+        "    new-instance        v0, Ljava/util/Hashtable;",
+        "    invoke-direct       { v0 }, Ljava/util/Hashtable;-><init>()V",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#work\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x01  # 1",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#other\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x02  # 2",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    sput-object         v0, LA;->I:Ljava/util/Hashtable;",
+        "    invoke-static       { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+        "    move-result-object  v0",
+        "    sput-object         v0, LA;->J:Ljava/util/Hashtable;",
+        "    new-instance        v0, Ljava/util/Hashtable;",
+        "    invoke-direct       { v0 }, Ljava/util/Hashtable;-><init>()V",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#AIM\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x02  # 2",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#MSN\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x03  # 3",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#YAHOO\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x04  # 4",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#SKYPE\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x05  # 5",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#QQ\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x06  # 6",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#GOOGLE_TALK\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/4             v3, 0x07  # 7",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#ICQ\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/16            v3, 0x0008  # 8",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    const-string        v1, \"http://schemas.google.com/g/2005#JABBER\"",
+        "    new-instance        v2, Ljava/lang/Byte;",
+        "    const/16            v3, 0x0009  # 9",
+        "    invoke-direct       { v2, v3 }, Ljava/lang/Byte;-><init>(B)V",
+        "    invoke-virtual      { v0, v1, v2 }, Ljava/util/Hashtable;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+        "    sput-object         v0, LA;->K:Ljava/util/Hashtable;",
+        "    invoke-static       { v0 }, LA;->a(Ljava/util/Hashtable;)Ljava/util/Hashtable;",
+        "    move-result-object  v0",
+        "    sput-object         v0, LA;->L:Ljava/util/Hashtable;",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    options.outline.threshold = 2;
+
+    DexApplication originalApplication = buildApplicationWithAndroidJar(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(2, Iterables.size(processedApplication.classes()));
+
+    // Verify the code.
+    runDex2Oat(processedApplication, options);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/Regress38014736.java b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
new file mode 100644
index 0000000..91d8023
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/Regress38014736.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.InternalOptions;
+import org.junit.Test;
+
+public class Regress38014736 extends SmaliTestBase {
+
+  @Test
+  public void handlerRangeStartingOnMoveResult() {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    builder.addMainMethod(
+        2,
+        "new-instance        v1, Ljava/lang/Integer;",
+        "const/4             v0, 0x00",
+        "invoke-direct       { v1, v0 }, Ljava/lang/Integer;-><init>(I)V",
+        "invoke-virtual      { v1 }, Ljava/lang/Integer;->intValue()I",
+        "move-result         v1",
+        "invoke-static       { v1 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;",
+        "move-result-object  v1",
+        "invoke-virtual      { v1 }, Ljava/lang/Integer;->intValue()I",
+        ":try_start", // The try block starts on the move result instruction.
+        "move-result         v1",
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        "const/4             v1, 0x00  # 0",
+        "invoke-static       { v1 }, "
+            + "Ljava/lang/Integer;->valueOf(Ljava/lang/String;)Ljava/lang/Integer;",
+        "move-result-object  v1",
+        "invoke-virtual      { v1 }, Ljava/lang/Integer;->intValue()I",
+        ":try_end", // The try block ends on the move result instruction.
+        "move-result         v1",
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(I)V",
+        ".catch Ljava/lang/NumberFormatException; {:try_start .. :try_end} :catch_block",
+        "return-void",
+        ":catch_block",
+        "move-exception      v1",
+        "sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V",
+        "return-void");
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    String result = runArt(processedApplication, options);
+    // The art runtime changed the way exceptions are printed. Therefore, we only check
+    // for the type of the exception and that the message mentions null.
+    assertTrue(result.startsWith("0\njava.lang.NumberFormatException:"));
+    assertTrue(result.contains("null"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
new file mode 100644
index 0000000..45cd8cd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/RunArtSmokeTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Iterables;
+import org.junit.Test;
+
+public class RunArtSmokeTest extends SmaliTestBase {
+
+  @Test
+  public void test() {
+    // Build simple "Hello, world!" application.
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+    MethodSignature mainSignature = builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const-string        v1, \"Hello, world!\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    assertEquals(1, Iterables.size(processedApplication.classes()));
+
+    // Return the processed method for inspection.
+    DexEncodedMethod main = getMethod(processedApplication, mainSignature);
+    assert main != null;
+
+    DexCode code = main.getCode().asDexCode();
+    assertTrue(code.instructions[0] instanceof SgetObject);
+    assertTrue(code.instructions[1] instanceof ConstString);
+    assertTrue(code.instructions[2] instanceof InvokeVirtual);
+    assertTrue(code.instructions[3] instanceof ReturnVoid);
+
+    // Run the generated code in Art.
+    String result = runArt(processedApplication, options);
+    assertEquals("Hello, world!\n", result);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
new file mode 100644
index 0000000..edcd454
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
+import java.nio.file.Paths;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Test;
+
+public class SmaliBuildTest extends SmaliTestBase {
+
+  private void checkJavaLangString(DexApplication application, boolean present) {
+    DexInspector inspector = new DexInspector(application);
+    ClassSubject clazz = inspector.clazz("java.lang.String");
+    assertEquals(present, clazz.isPresent());
+  }
+
+  @Test
+  public void buildWithoutLibrary() {
+    // Build simple "Hello, world!" application.
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const-string        v1, \"Hello, world!\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    // No libraries added - java.lang.String is not present.
+    DexApplication originalApplication = buildApplication(builder, options);
+    checkJavaLangString(originalApplication, false);
+
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    checkJavaLangString(processedApplication, false);
+  }
+
+  @Test
+  public void buildWithLibrary() throws IOException, RecognitionException {
+    // Build simple "Hello, world!" application.
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const-string        v1, \"Hello, world!\"",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    AndroidApp app = AndroidApp.builder()
+        .addDexProgramData(builder.compile())
+        .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+        .build();
+
+    // Java standard library added - java.lang.String is present.
+    DexApplication originalApplication = buildApplication(app, options);
+    checkJavaLangString(originalApplication, true);
+
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    checkJavaLangString(processedApplication, true);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
new file mode 100644
index 0000000..b1fd38f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliDisassembleTest.java
@@ -0,0 +1,371 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Smali;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Test;
+
+public class SmaliDisassembleTest extends SmaliTestBase {
+
+  // Run the provided smali through R8 smali disassembler and expect the exact same output.
+  void roundTripRawSmali(String smali) {
+    try {
+      DexApplication application =
+          new ApplicationReader(
+                  AndroidApp.fromDexProgramData(Smali.compile(smali)),
+                  new InternalOptions(),
+                  new Timing("SmaliTest"))
+              .read();
+      assertEquals(smali, application.smali(new InternalOptions()));
+    } catch (IOException | RecognitionException | ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Test
+  public void simpleSmokeTest() {
+    DexApplication application = singleMethodApplication(
+        "int", Collections.singletonList("int"),
+        4,
+        "    const/4 v0, 1           ",
+        "    const/16 v1, 2          ",
+        "    const v2, 3             ",
+        "    const-wide v3, -1       ",
+        "    add-int/2addr v1, v0    ",
+        "    mul-int/2addr v2, v1    ",
+        "    div-int/2addr v3, v2    ",
+        "    return v3\n             "
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method(I)I\n" +
+            "    .registers 5\n" +
+            "\n" +
+            "    const/4             v0, 0x01  # 1\n" +
+            "    const/16            v1, 0x0002  # 2\n" +
+            "    const               v2, 0x00000003  # 3\n" +
+            "    const-wide          v3, 0xffffffffffffffffL  # -1\n" +
+            "    add-int/2addr       v1, v0\n" +
+            "    mul-int/2addr       v2, v1\n" +
+            "    div-int/2addr       v3, v2\n" +
+            "    return              v3\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+  @Test
+  public void sparseSwitchTest() {
+    DexApplication application = singleMethodApplication(
+        "int", Collections.singletonList("int"),
+        0,
+        "    sparse-switch v0, :sparse_switch_data",
+        "    const/4 v0, 0x0",
+        "    goto :return",
+        "    :case_1",
+        "    const/4 v0, 0x1",
+        "    goto :return",
+        "    :case_2",
+        "    const/4 v0, 0x2",
+        "    :return",
+        "    return v0",
+        "    :sparse_switch_data     ",
+        "      .sparse-switch         ",
+        "      0x1 -> :case_1         ",
+        "      0x2 -> :case_2         ",
+        "    .end sparse-switch     "
+
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method(I)I\n" +
+            "    .registers 1\n" +
+            "\n" +
+            "    sparse-switch       v0, :label_10\n" +
+            "    const/4             v0, 0x00  # 0\n" +
+            "    goto                :label_8\n" +
+            "  :label_5\n" +
+            "    const/4             v0, 0x01  # 1\n" +
+            "    goto                :label_8\n" +
+            "  :label_7\n" +
+            "    const/4             v0, 0x02  # 2\n" +
+            "  :label_8\n" +
+            "    return              v0\n" +
+            "    nop\n" +
+            "  :label_10\n" +
+            "    .sparse-switch\n" +
+            "      0x00000001 -> :label_5  # 1\n" +
+            "      0x00000002 -> :label_7  # 2\n" +
+            "    .end sparse-switch\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+  @Test
+  public void packedSwitchTest() {
+    DexApplication application = singleMethodApplication(
+        "int", Collections.singletonList("int"),
+        0,
+        "    packed-switch v0, :packed_switch_data",
+        "    const/4 v0, 0x0         ",
+        "    goto :return            ",
+        "    :case_1                 ",
+        "    const/4 v0, 0x1         ",
+        "    goto :return            ",
+        "    :case_2                 ",
+        "    const/4 v0, 0x2         ",
+        "    :return                 ",
+        "    return v0               ",
+        "    :packed_switch_data     ",
+        "      .packed-switch 0x1    ",
+        "        :case_1             ",
+        "        :case_2             ",
+        "    .end packed-switch      "
+
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method(I)I\n" +
+            "    .registers 1\n" +
+            "\n" +
+            "    packed-switch       v0, :label_10\n" +
+            "    const/4             v0, 0x00  # 0\n" +
+            "    goto                :label_8\n" +
+            "  :label_5\n" +
+            "    const/4             v0, 0x01  # 1\n" +
+            "    goto                :label_8\n" +
+            "  :label_7\n" +
+            "    const/4             v0, 0x02  # 2\n" +
+            "  :label_8\n" +
+            "    return              v0\n" +
+            "    nop\n" +
+            "  :label_10\n" +
+            "    .packed-switch 0x00000001  # 1\n" +
+            "      :label_5\n" +
+            "      :label_7\n" +
+            "    .end packed-switch\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+  @Test
+  public void fillArrayDataTest8Bit() {
+    DexApplication application = singleMethodApplication(
+        "int[]", ImmutableList.of(),
+        2,
+        "    const/4 v1, 3",
+        "    new-array v0, v1, [I",
+        "    fill-array-data v0, :array_data",
+        "    return-object v0",
+        "    :array_data",
+        "    .array-data 1",
+        "      1 2 255",
+        "    .end array-data"
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method()[I\n" +
+            "    .registers 2\n" +
+            "\n" +
+            "    const/4             v1, 0x03  # 3\n" +
+            "    new-array           v0, v1, [I\n" +
+            "    fill-array-data     v0, :label_8\n" +
+            "    return-object       v0\n" +
+            "    nop\n" +
+            "  :label_8\n" +
+            "    .array-data 0x1  # 1\n" +
+            "      0x01  # 1\n" +
+            "      0x02  # 2\n" +
+            "      0xff  # 255\n" +
+            "    .end array-data\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+  @Test
+  public void fillArrayDataTest16Bit() {
+    DexApplication application = singleMethodApplication(
+        "int[]", ImmutableList.of(),
+        2,
+        "    const/4 v1, 3",
+        "    new-array v0, v1, [I",
+        "    fill-array-data v0, :array_data",
+        "    return-object v0",
+        "    :array_data",
+        "    .array-data 2",
+        "      1 2 65535",
+        "    .end array-data"
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method()[I\n" +
+            "    .registers 2\n" +
+            "\n" +
+            "    const/4             v1, 0x03  # 3\n" +
+            "    new-array           v0, v1, [I\n" +
+            "    fill-array-data     v0, :label_8\n" +
+            "    return-object       v0\n" +
+            "    nop\n" +
+            "  :label_8\n" +
+            "    .array-data 0x2  # 2\n" +
+            "      0x0001  # 1\n" +
+            "      0x0002  # 2\n" +
+            "      0xffff  # 65535\n" +
+            "    .end array-data\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+  @Test
+  public void fillArrayDataTest32Bit() {
+    DexApplication application = singleMethodApplication(
+        "int[]", ImmutableList.of(),
+        2,
+        "    const/4 v1, 3",
+        "    new-array v0, v1, [I",
+        "    fill-array-data v0, :array_data",
+        "    return-object v0",
+        "    :array_data",
+        "    .array-data 4",
+        "      1 2 4294967295",
+        "    .end array-data"
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method()[I\n" +
+            "    .registers 2\n" +
+            "\n" +
+            "    const/4             v1, 0x03  # 3\n" +
+            "    new-array           v0, v1, [I\n" +
+            "    fill-array-data     v0, :label_8\n" +
+            "    return-object       v0\n" +
+            "    nop\n" +
+            "  :label_8\n" +
+            "    .array-data 0x4  # 4\n" +
+            "      0x00000001  # 1\n" +
+            "      0x00000002  # 2\n" +
+            "      0xffffffff  # 4294967295\n" +
+            "    .end array-data\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+  @Test
+  public void fillArrayDataTest64Bit() {
+    DexApplication application = singleMethodApplication(
+        "int[]", ImmutableList.of(),
+        2,
+        "    const/4 v1, 3",
+        "    new-array v0, v1, [I",
+        "    fill-array-data v0, :array_data",
+        "    return-object v0",
+        "    :array_data",
+        "    .array-data 8",
+        "      1 2 -1",
+        "    .end array-data"
+    );
+
+    String expected =
+        ".class public LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public static method()[I\n" +
+            "    .registers 2\n" +
+            "\n" +
+            "    const/4             v1, 0x03  # 3\n" +
+            "    new-array           v0, v1, [I\n" +
+            "    fill-array-data     v0, :label_8\n" +
+            "    return-object       v0\n" +
+            "    nop\n" +
+            "  :label_8\n" +
+            "    .array-data 0x8  # 8\n" +
+            "      0x0000000000000001  # 1\n" +
+            "      0x0000000000000002  # 2\n" +
+            "      0xffffffffffffffff  # -1\n" +
+            "    .end array-data\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+
+
+  @Test
+  public void interfaceClass() {
+    SmaliBuilder builder = new SmaliBuilder();
+    builder.addInterface("Test");
+    builder.addAbstractMethod("int", "test", ImmutableList.of());
+    DexApplication application = buildApplication(builder);
+    assertEquals(1, Iterables.size(application.classes()));
+
+    String expected =
+        ".class public interface abstract LTest;\n" +
+            "\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            ".method public abstract test()I\n" +
+            ".end method\n";
+
+    assertEquals(expected, application.smali(new InternalOptions()));
+
+    roundTripRawSmali(expected);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
new file mode 100644
index 0000000..314adb1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -0,0 +1,490 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.ValueNumberGenerator;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Smali;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Iterables;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+
+public class SmaliTestBase {
+
+  public static final String DEFAULT_CLASS_NAME = "Test";
+  public static final String DEFAULT_MAIN_CLASS_NAME = DEFAULT_CLASS_NAME;
+  public static final String DEFAULT_METHOD_NAME = "method";
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  public static class MethodSignature {
+
+    public final String clazz;
+    public final String name;
+    public final String returnType;
+    public final List<String> parameterTypes;
+
+    public MethodSignature(String clazz, String name, String returnType,
+        List<String> parameterTypes) {
+      this.clazz = clazz;
+      this.name = name;
+      this.returnType = returnType;
+      this.parameterTypes = parameterTypes;
+    }
+  }
+
+  public static class SmaliBuilder {
+
+    abstract class Builder {
+      String name;
+      String superName;
+      List<String> source = new ArrayList<>();
+
+      Builder(String name, String superName) {
+        this.name = name;
+        this.superName = superName;
+      }
+
+      protected void writeSource(StringBuilder builder) {
+        for (String sourceLine : source) {
+          builder.append(sourceLine);
+          builder.append("\n");
+        }
+      }
+    }
+
+    public class ClassBuilder extends Builder {
+
+      ClassBuilder(String name, String superName) {
+        super(name, superName);
+      }
+
+      public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(".class public ");
+        builder.append(DescriptorUtils.javaTypeToDescriptor(name));
+        builder.append("\n");
+        builder.append(".super ");
+        builder.append(DescriptorUtils.javaTypeToDescriptor(superName));
+        builder.append("\n\n");
+        writeSource(builder);
+        return builder.toString();
+      }
+    }
+
+    public class InterfaceBuilder extends Builder {
+
+      InterfaceBuilder(String name, String superName) {
+        super(name, superName);
+      }
+
+      public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(".class public interface abstract ");
+        builder.append(DescriptorUtils.javaTypeToDescriptor(name));
+        builder.append("\n");
+        builder.append(".super ");
+        builder.append(DescriptorUtils.javaTypeToDescriptor(superName));
+        builder.append("\n\n");
+        writeSource(builder);
+        return builder.toString();
+      }
+    }
+
+    private String currentClassName;
+    private final Map<String, Builder> classes = new HashMap<>();
+
+    public SmaliBuilder() {
+      // No default class.
+    }
+
+    public SmaliBuilder(String name) {
+      addClass(name);
+    }
+
+    public SmaliBuilder(String name, String superName) {
+      addClass(name, superName);
+    }
+
+    private List<String> getSource(String clazz) {
+      return classes.get(clazz).source;
+    }
+
+    public String getCurrentClassName() {
+      return currentClassName;
+    }
+
+    public String getCurrentClassDescriptor() {
+      return DescriptorUtils.javaTypeToDescriptor(currentClassName);
+    }
+
+    public void addClass(String name) {
+      addClass(name, "java.lang.Object");
+    }
+
+    public void addClass(String name, String superName) {
+      assert !classes.containsKey(name);
+      currentClassName = name;
+      classes.put(name, new ClassBuilder(name, superName));
+    }
+
+    public void addInterface(String name) {
+      addInterface(name, "java.lang.Object");
+    }
+
+    public void addInterface(String name, String superName) {
+      assert !classes.containsKey(name);
+      currentClassName = name;
+      classes.put(name, new InterfaceBuilder(name, superName));
+    }
+
+    public void addDefaultConstructor() {
+      String superDescriptor =
+          DescriptorUtils.javaTypeToDescriptor(classes.get(currentClassName).superName);
+      addMethodRaw(
+          "  .method public constructor <init>()V",
+          "    .locals 0",
+          "    invoke-direct {p0}, " + superDescriptor + "-><init>()V",
+          "    return-void",
+          "  .end method"
+      );
+    }
+
+    public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
+        int locals, String... instructions) {
+      StringBuilder builder = new StringBuilder();
+      for (String instruction : instructions) {
+        builder.append(instruction);
+        builder.append("\n");
+      }
+      return addStaticMethod(returnType, name, parameters, locals, builder.toString());
+    }
+
+    public MethodSignature addStaticMethod(String returnType, String name, List<String> parameters,
+        int locals, String code) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(".method public static ");
+      builder.append(name);
+      builder.append("(");
+      for (String parameter : parameters) {
+        builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+      }
+      builder.append(")");
+      builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+      builder.append("\n");
+      builder.append(".locals ");
+      builder.append(locals);
+      builder.append("\n\n");
+      builder.append(code);
+      builder.append(".end method");
+      getSource(currentClassName).add(builder.toString());
+      return new MethodSignature(currentClassName, name, returnType, parameters);
+    }
+
+    public MethodSignature addAbstractMethod(
+        String returnType, String name, List<String> parameters) {
+      StringBuilder builder = new StringBuilder();
+      builder.append(".method public abstract ");
+      builder.append(name);
+      builder.append("(");
+      for (String parameter : parameters) {
+        builder.append(DescriptorUtils.javaTypeToDescriptor(parameter));
+      }
+      builder.append(")");
+      builder.append(DescriptorUtils.javaTypeToDescriptor(returnType));
+      builder.append("\n");
+      builder.append(".end method");
+      getSource(currentClassName).add(builder.toString());
+      return new MethodSignature(currentClassName, name, returnType, parameters);
+    }
+
+    public MethodSignature addMainMethod(int locals, String... instructions) {
+      return addStaticMethod(
+          "void", "main", Collections.singletonList("java.lang.String[]"), locals, instructions);
+    }
+
+    public void addMethodRaw(String... source) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : source) {
+        builder.append(line);
+        builder.append("\n");
+      }
+      getSource(currentClassName).add(builder.toString());
+    }
+
+    public List<String> build() {
+      List<String> result = new ArrayList<>(classes.size());
+      for (String clazz : classes.keySet()) {
+        Builder classBuilder = classes.get(clazz);
+        result.add(classBuilder.toString());
+      }
+      return result;
+    }
+
+    public byte[] compile() throws IOException, RecognitionException {
+      return Smali.compile(build());
+    }
+
+    @Override
+    public String toString() {
+      return String.join("\n\n", build());
+    }
+  }
+
+  public class TestApplication {
+
+    public final DexApplication application;
+    public final DexEncodedMethod method;
+    public final IRCode code;
+    public final List<IRCode> additionalCode;
+    public final ValueNumberGenerator valueNumberGenerator;
+    public final InternalOptions options;
+
+    public TestApplication(
+        DexApplication application,
+        DexEncodedMethod method,
+        IRCode code,
+        ValueNumberGenerator valueNumberGenerator,
+        InternalOptions options) {
+      this(application, method, code, null, valueNumberGenerator, options);
+    }
+
+    public TestApplication(
+        DexApplication application,
+        DexEncodedMethod method,
+        IRCode code,
+        List<IRCode> additionalCode,
+        ValueNumberGenerator valueNumberGenerator,
+        InternalOptions options) {
+      this.application = application;
+      this.method = method;
+      this.code = code;
+      this.additionalCode = additionalCode;
+      this.valueNumberGenerator = valueNumberGenerator;
+      this.options = options;
+    }
+
+    public int countArgumentInstructions() {
+      int count = 0;
+      ListIterator<Instruction> iterator = code.blocks.get(0).listIterator();
+      while (iterator.next().isArgument()) {
+        count++;
+      }
+      return count;
+    }
+
+    public InstructionListIterator listIteratorAt(BasicBlock block, int index) {
+      InstructionListIterator iterator = block.listIterator();
+      for (int i = 0; i < index; i++) {
+        iterator.next();
+      }
+      return iterator;
+    }
+
+    public String run() {
+      AppInfo appInfo = new AppInfo(application);
+      IRConverter converter = new IRConverter(application, appInfo, options, null);
+      converter.replaceCodeForTesting(method, code);
+      return runArt(application, options);
+    }
+  }
+
+  protected DexApplication buildApplication(SmaliBuilder builder) {
+    return buildApplication(builder, new InternalOptions());
+  }
+
+  protected DexApplication buildApplication(SmaliBuilder builder, InternalOptions options) {
+    try {
+      return buildApplication(AndroidApp.fromDexProgramData(builder.compile()), options);
+    } catch (IOException | RecognitionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexApplication buildApplicationWithAndroidJar(
+      SmaliBuilder builder, InternalOptions options) {
+    try {
+      AndroidApp input = AndroidApp.builder()
+          .addDexProgramData(builder.compile())
+          .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+          .build();
+      return buildApplication(input, options);
+    } catch (IOException | RecognitionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
+    try {
+      options.itemFactory.resetSortedIndices();
+      return new ApplicationReader(input, options, new Timing("SmaliTest")).read();
+    } catch (IOException | ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexApplication processApplication(DexApplication application, InternalOptions options) {
+    try {
+      R8 r8 = new R8(options);
+      return r8.optimize(application, new AppInfoWithSubtyping(application));
+    } catch (IOException | ProguardRuleParserException | ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  protected DexClass getClass(DexApplication application, String className) {
+    DexInspector inspector = new DexInspector(application);
+    ClassSubject clazz = inspector.clazz(className);
+    assertTrue(clazz.isPresent());
+    return clazz.getDexClass();
+  }
+
+  protected DexClass getClass(DexApplication application, MethodSignature signature) {
+    return getClass(application, signature.clazz);
+  }
+
+  protected DexEncodedMethod getMethod(DexApplication application, String className,
+      String returnType, String methodName, List<String> parameters) {
+    DexInspector inspector = new DexInspector(application);
+    ClassSubject clazz = inspector.clazz(className);
+    assertTrue(clazz.isPresent());
+    MethodSubject method = clazz.method(returnType, methodName, parameters);
+    assertTrue(method.isPresent());
+    return method.getMethod();
+  }
+
+  protected DexEncodedMethod getMethod(DexApplication application, MethodSignature signature) {
+    return getMethod(application,
+        signature.clazz, signature.returnType, signature.name, signature.parameterTypes);
+  }
+
+  /**
+   * Create an application with one method, and processed that application using R8.
+   *
+   * Returns the processed method for inspection.
+   *
+   * @param returnType the return type for the method
+   * @param parameters the parameter types for the method
+   * @param locals number of locals needed for the application
+   * @param instructions instructions for the method
+   * @return the processed method for inspection
+   */
+  public DexApplication singleMethodApplication(String returnType, List<String> parameters,
+      int locals, String... instructions) {
+    // Build a one class method.
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+    builder.addStaticMethod(returnType, DEFAULT_METHOD_NAME, parameters, locals, instructions);
+
+    // Read the one class method as an application.
+    DexApplication application = buildApplication(builder);
+    assertEquals(1, Iterables.size(application.classes()));
+    return application;
+  }
+
+  /**
+   * Create an application with one method, and processed that application using R8.
+   *
+   * Returns the processed method for inspection.
+   *
+   * @param returnType the return type for the method
+   * @param parameters the parameter types for the method
+   * @param locals number of locals needed for the application
+   * @param instructions instructions for the method
+   * @return the processed method for inspection
+   */
+  public DexEncodedMethod oneMethodApplication(String returnType, List<String> parameters,
+      int locals, String... instructions) {
+    InternalOptions options = new InternalOptions();
+
+    // Build a one class application.
+    DexApplication application = singleMethodApplication(
+        returnType, parameters, locals, instructions);
+
+    // Process the application with R8.
+    DexApplication processdApplication = processApplication(application, options);
+    assertEquals(1, Iterables.size(processdApplication.classes()));
+
+    // Return the processed method for inspection.
+    return getMethod(
+        processdApplication, DEFAULT_CLASS_NAME, returnType, DEFAULT_METHOD_NAME, parameters);
+  }
+
+  public String runArt(DexApplication application, InternalOptions options) {
+    try {
+      AndroidApp app = writeDex(application, options);
+      Path out = temp.getRoot().toPath().resolve("run-art-input.zip");
+      // TODO(sgjesse): Pass in a unique temp directory for each run.
+      app.writeToZip(out, true);
+      return ToolHelper.runArtNoVerificationErrors(out.toString(), DEFAULT_MAIN_CLASS_NAME);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public void runDex2Oat(DexApplication application, InternalOptions options) {
+    try {
+      AndroidApp app = writeDex(application, options);
+      Path dexOut = temp.getRoot().toPath().resolve("run-dex2oat-input.zip");
+      Path oatFile = temp.getRoot().toPath().resolve("oat-file");
+      app.writeToZip(dexOut);
+      ToolHelper.runDex2Oat(dexOut, oatFile);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public AndroidApp writeDex(DexApplication application, InternalOptions options) {
+    AppInfo appInfo = new AppInfo(application);
+    try {
+      return R8.writeApplication(
+          Executors.newSingleThreadExecutor(),
+          application,
+          appInfo,
+          NamingLens.getIdentityLens(),
+          null,
+          null,
+          options);
+    } catch (ExecutionException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
new file mode 100644
index 0000000..12bb1f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -0,0 +1,277 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.smali;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.PackedSwitch;
+import com.android.tools.r8.code.SparseSwitch;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.Switch;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class SwitchRewritingTest extends SmaliTestBase {
+
+  private void runSingleCaseDexTest(Switch.Type type, int key) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+    String switchInstruction;
+    String switchData;
+    if (type == Switch.Type.PACKED) {
+      switchInstruction = "packed-switch";
+      switchData = StringUtils.join(
+          "\n",
+          "  :switch_data",
+          "  .packed-switch " + key,
+          "    :case_0",
+          "  .end packed-switch");
+    } else {
+      switchInstruction = "sparse-switch";
+      switchData = StringUtils.join(
+          "\n",
+          "  :switch_data",
+          "  .sparse-switch",
+          "    " + key + " -> :case_0",
+          "  .end sparse-switch");
+    }
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int"),
+        0,
+        "    " + switchInstruction + " p0, :switch_data",
+        "    const/4 p0, 0x5",
+        "    goto :return",
+        "  :case_0",
+        "    const/4 p0, 0x3",
+        "  :return",
+        "    return p0",
+        switchData);
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 0",
+        "    invoke-static       { v1 }, LTest;->method(I)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    DexCode code = method.getCode().asDexCode();
+
+    if (key == 0) {
+      assertEquals(5, code.instructions.length);
+      assertTrue(code.instructions[0] instanceof IfEqz);
+    } else {
+      assertEquals(6, code.instructions.length);
+      assertTrue(code.instructions[0] instanceof Const4);
+      assertTrue(code.instructions[1] instanceof IfEq);
+    }
+  }
+
+  @Test
+  public void singleCaseDex() {
+    runSingleCaseDexTest(Switch.Type.PACKED, 0);
+    runSingleCaseDexTest(Switch.Type.SPARSE, 0);
+    runSingleCaseDexTest(Switch.Type.PACKED, 1);
+    runSingleCaseDexTest(Switch.Type.SPARSE, 1);
+  }
+
+  private void runTwoCaseSparseToPackedDexTest(int key1, int key2) {
+    SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
+
+    MethodSignature signature = builder.addStaticMethod(
+        "int",
+        DEFAULT_METHOD_NAME,
+        ImmutableList.of("int"),
+        0,
+        "    sparse-switch p0, :sparse_switch_data",
+        "    const/4 v0, 0x5",
+        "    goto :return",
+        "  :case_1",
+        "    const/4 v0, 0x3",
+        "    goto :return",
+        "  :case_2",
+        "    const/4 v0, 0x4",
+        "  :return",
+        "    return v0",
+        "  :sparse_switch_data",
+        "  .sparse-switch",
+        "    " + key1 + " -> :case_1",
+        "    " + key2 + " -> :case_2",
+        "  .end sparse-switch");
+
+    builder.addMainMethod(
+        2,
+        "    sget-object         v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "    const/4             v1, 0",
+        "    invoke-static       { v1 }, LTest;->method(I)I",
+        "    move-result         v1",
+        "    invoke-virtual      { v0, v1 }, Ljava/io/PrintStream;->print(I)V",
+        "    return-void"
+    );
+
+    InternalOptions options = new InternalOptions();
+    DexApplication originalApplication = buildApplication(builder, options);
+    DexApplication processedApplication = processApplication(originalApplication, options);
+    DexEncodedMethod method = getMethod(processedApplication, signature);
+    DexCode code = method.getCode().asDexCode();
+    if (key1 + 1 == key2) {
+      assertTrue(code.instructions[0] instanceof PackedSwitch);
+    } else {
+      assertTrue(code.instructions[0] instanceof SparseSwitch);
+    }
+  }
+
+  @Test
+  public void twoCaseSparseToPackedDex() {
+    runTwoCaseSparseToPackedDexTest(0, 1);
+    runTwoCaseSparseToPackedDexTest(-1, 0);
+    runTwoCaseSparseToPackedDexTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 1);
+    runTwoCaseSparseToPackedDexTest(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+    runTwoCaseSparseToPackedDexTest(0, 2);
+    runTwoCaseSparseToPackedDexTest(-1, 1);
+    runTwoCaseSparseToPackedDexTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 2);
+    runTwoCaseSparseToPackedDexTest(Integer.MAX_VALUE - 2, Integer.MAX_VALUE);
+  }
+
+  private void runSingleCaseJarTest(Switch.Type type, int key) throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    String switchCode;
+    if (type == Switch.Type.PACKED) {
+      switchCode = StringUtils.join(
+          "\n",
+          "    tableswitch " + key,
+          "      case_0",
+          "      default : case_default");
+    } else {
+      switchCode = StringUtils.join(
+          "\n",
+          "    lookupswitch",
+          "      " + key + " : case_0",
+          "      default : case_default");
+    }
+
+    clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+        "    .limit stack 1",
+        "    .limit locals 1",
+        "    iload 0",
+        switchCode,
+        "  case_0:",
+        "    iconst_3",
+        "    goto return_",
+        "  case_default:",
+        "    ldc 5",
+        "  return_:",
+        "    ireturn");
+
+    clazz.addMainMethod(
+        "    .limit stack 2",
+        "    .limit locals 1",
+        "    getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "    ldc 2",
+        "    invokestatic Test/test(I)I",
+        "    invokevirtual java/io/PrintStream/print(I)V",
+        "    return");
+
+    DexApplication app = builder.read();
+    app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+
+    MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
+    DexEncodedMethod method = getMethod(app, signature);
+    DexCode code = method.getCode().asDexCode();
+    if (key == 0) {
+      assertEquals(5, code.instructions.length);
+      assertTrue(code.instructions[0] instanceof IfEqz);
+    } else {
+      assertEquals(6, code.instructions.length);
+      assertTrue(code.instructions[1] instanceof IfEq);
+      assertTrue(code.instructions[2] instanceof Const4);
+    }
+  }
+
+  @Test
+  public void singleCaseJar() throws Exception {
+    runSingleCaseJarTest(Switch.Type.PACKED, 0);
+    runSingleCaseJarTest(Switch.Type.SPARSE, 0);
+    runSingleCaseJarTest(Switch.Type.PACKED, 1);
+    runSingleCaseJarTest(Switch.Type.SPARSE, 1);
+  }
+
+  private void runTwoCaseSparseToPackedJarTest(int key1, int key2) throws Exception {
+    JasminBuilder builder = new JasminBuilder();
+    JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+    clazz.addStaticMethod("test", ImmutableList.of("I"), "I",
+        "    .limit stack 1",
+        "    .limit locals 1",
+        "    iload 0",
+        "    lookupswitch",
+        "      " + key1 + " : case_1",
+        "      " + key2 + " : case_2",
+        "      default : case_default",
+        "  case_1:",
+        "    iconst_3",
+        "    goto return_",
+        "  case_2:",
+        "    iconst_4",
+        "    goto return_",
+        "  case_default:",
+        "    iconst_5",
+        "  return_:",
+        "    ireturn");
+
+    clazz.addMainMethod(
+        "    .limit stack 2",
+        "    .limit locals 1",
+        "    getstatic java/lang/System/out Ljava/io/PrintStream;",
+        "    ldc 2",
+        "    invokestatic Test/test(I)I",
+        "    invokevirtual java/io/PrintStream/print(I)V",
+        "    return");
+
+    DexApplication app = builder.read();
+    app = new R8(new InternalOptions()).optimize(app, new AppInfoWithSubtyping(app));
+
+    MethodSignature signature = new MethodSignature("Test", "test", "int", ImmutableList.of("int"));
+    DexEncodedMethod method = getMethod(app, signature);
+    DexCode code = method.getCode().asDexCode();
+    if (key1 + 1 == key2) {
+      assertTrue(code.instructions[0] instanceof PackedSwitch);
+    } else {
+      assertTrue(code.instructions[0] instanceof SparseSwitch);
+    }
+  }
+
+  @Test
+  public void twoCaseSparseToPackedJar() throws Exception {
+    runTwoCaseSparseToPackedJarTest(0, 1);
+    runTwoCaseSparseToPackedJarTest(-1, 0);
+    runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 1);
+    runTwoCaseSparseToPackedJarTest(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+    runTwoCaseSparseToPackedJarTest(0, 2);
+    runTwoCaseSparseToPackedJarTest(-1, 1);
+    runTwoCaseSparseToPackedJarTest(Integer.MIN_VALUE, Integer.MIN_VALUE + 2);
+    runTwoCaseSparseToPackedJarTest(Integer.MAX_VALUE - 2, Integer.MAX_VALUE);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ArtCommandBuilderTest.java b/src/test/java/com/android/tools/r8/utils/ArtCommandBuilderTest.java
new file mode 100644
index 0000000..0344a27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ArtCommandBuilderTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.ToolHelper.DexVm;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ArtCommandBuilderTest {
+
+  @Test
+  public void noArguments() {
+    ArtCommandBuilder builder = new ArtCommandBuilder();
+    Assert.assertEquals("/bin/bash " + ToolHelper.getArtBinary(), builder.build());
+  }
+
+  @Test
+  public void simple() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder.appendClasspath("xxx.dex").setMainClass("Test");
+    assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -cp xxx.dex Test", builder.build());
+  }
+
+  @Test
+  public void classpath() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder.appendClasspath("xxx.dex").appendClasspath("yyy.jar");
+    assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -cp xxx.dex:yyy.jar",
+                 builder.build());
+  }
+
+  @Test
+  public void artOptions() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder.appendArtOption("-d").appendArtOption("--test");
+    assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -d --test", builder.build());
+  }
+
+  @Test
+  public void artSystemProperties() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder.appendArtSystemProperty("a.b.c", "1").appendArtSystemProperty("x.y.z", "2");
+    assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " -Da.b.c=1 -Dx.y.z=2",
+                 builder.build());
+  }
+
+  @Test
+  public void programOptions() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder.setMainClass("Test").appendProgramArgument("hello").appendProgramArgument("world");
+    assertEquals("/bin/bash " + ToolHelper.getArtBinary() + " Test hello world", builder.build());
+  }
+
+  @Test
+  public void allOfTheAbove() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder
+        .appendArtOption("-d")
+        .appendArtOption("--test")
+        .appendArtSystemProperty("a.b.c", "1")
+        .appendArtSystemProperty("x.y.z", "2")
+        .appendClasspath("xxx.dex")
+        .appendClasspath("yyy.jar")
+        .setMainClass("Test")
+        .appendProgramArgument("hello")
+        .appendProgramArgument("world");
+    assertEquals(
+        "/bin/bash " + ToolHelper.getArtBinary()
+            + " -d --test -Da.b.c=1 -Dx.y.z=2 -cp xxx.dex:yyy.jar Test hello world",
+        builder.build());
+  }
+
+  @Test
+  public void allOfTheAboveWithClasspathAndSystemPropertiesBeforeOptions() {
+    ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder();
+    builder
+        .appendClasspath("xxx.dex")
+        .appendClasspath("yyy.jar")
+        .appendArtSystemProperty("a.b.c", "1")
+        .appendArtSystemProperty("x.y.z", "2")
+        .appendArtOption("-d")
+        .appendArtOption("--test")
+        .setMainClass("Test")
+        .appendProgramArgument("hello")
+        .appendProgramArgument("world");
+    assertEquals(
+        "/bin/bash " + ToolHelper.getArtBinary()
+            + " -d --test -Da.b.c=1 -Dx.y.z=2 -cp xxx.dex:yyy.jar Test hello world",
+        builder.build());
+  }
+
+  @Test
+  public void testVersion() {
+    for (DexVm version : ToolHelper.getArtVersions()) {
+      ToolHelper.ArtCommandBuilder builder = new ToolHelper.ArtCommandBuilder(version);
+      builder.setMainClass("Test").appendProgramArgument("hello").appendProgramArgument("world");
+      assertEquals("/bin/bash " + ToolHelper.getArtBinary(version)
+          + " Test hello world", builder.build());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java b/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
new file mode 100644
index 0000000..406c63f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/ArtErrorParser.java
@@ -0,0 +1,293 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArtErrorParser {
+  private static final String VERIFICATION_ERROR_HEADER = "Verification error in ";
+  private static final String METHOD_EXCEEDS_INSTRUCITON_LIMIT =
+      "Method exceeds compiler instruction limit: ";
+
+  public static class ArtErrorParserException extends Exception {
+    public ArtErrorParserException(String message) {
+      super(message);
+    }
+  }
+
+  private static class ParseInput {
+    public final String input;
+    public int pos = 0;
+
+    public ParseInput(String input) {
+      this.input = input;
+    }
+
+    public boolean hasNext() {
+      return pos < input.length();
+    }
+
+    public char next() {
+      assert hasNext();
+      return input.charAt(pos++);
+    }
+
+    public char peek() {
+      return input.charAt(pos);
+    }
+
+    public void whitespace() {
+      while (peek() == ' ') {
+        next();
+      }
+    }
+
+    public String until(int end) {
+      String result = input.substring(pos, end);
+      pos = end;
+      return result;
+    }
+
+    public String until(char match) throws ArtErrorParserException {
+      int end = input.indexOf(match, pos);
+      if (end < 0) {
+        throw new ArtErrorParserException("Expected to find " + match + " at " + pos);
+      }
+      return until(end);
+    }
+
+    public void check(char expected) throws ArtErrorParserException {
+      if (peek() != expected) {
+        throw new ArtErrorParserException("Expected " + expected + " found " + pos);
+      }
+    }
+  }
+
+  public static abstract class ArtErrorInfo {
+    public abstract int consumedLines();
+    public abstract String getMessage();
+    public abstract String dump(DexInspector inspector, boolean markLocation);
+  }
+
+  private static class ArtMethodError extends ArtErrorInfo {
+    public final String className;
+    public final String methodName;
+    public final String methodReturn;
+    public final List<String> methodFormals;
+    public final String methodSignature;
+    public final String errorMessage;
+    public final List<Long> locations;
+    private final int consumedLines;
+
+    public ArtMethodError(String className,
+        String methodName, String methodReturn, List<String> methodFormals,
+        String methodSignature, String errorMessage,
+        List<Long> locations,
+        int consumedLines) {
+      this.className = className;
+      this.methodName = methodName;
+      this.methodReturn = methodReturn;
+      this.methodFormals = methodFormals;
+      this.methodSignature = methodSignature;
+      this.errorMessage = errorMessage;
+      this.locations = locations;
+      this.consumedLines = consumedLines;
+    }
+
+    @Override
+    public int consumedLines() {
+      return consumedLines;
+    }
+
+    @Override
+    public String getMessage() {
+      StringBuilder builder = new StringBuilder();
+      builder.append("\n")
+          .append(VERIFICATION_ERROR_HEADER)
+          .append(methodSignature)
+          .append(":\n")
+          .append(errorMessage);
+      return builder.toString();
+    }
+
+    @Override
+    public String dump(DexInspector inspector, boolean markLocation) {
+      ClassSubject clazz = inspector.clazz(className);
+      MethodSubject method = clazz.method(methodReturn, methodName, methodFormals);
+      DexEncodedMethod dex = method.getMethod();
+      if (dex == null) {
+        StringBuilder builder = new StringBuilder("Failed to lookup method: ");
+        builder.append(className).append(".").append(methodName);
+        StringUtils.append(builder, methodFormals);
+        return builder.toString();
+      }
+
+      String code = method.getMethod().codeToString();
+      if (markLocation && !locations.isEmpty()) {
+        for (Long location : locations) {
+          String locationString = "" + location + ":";
+          code = code.replaceFirst(":(\\s*) " + locationString, ":$1*" + locationString);
+        }
+      }
+      return code;
+    }
+  }
+
+  public static List<ArtErrorInfo> parse(String message) throws ArtErrorParserException {
+    List<ArtErrorInfo> errors = new ArrayList<>();
+    String[] lines = message.split("\n");
+    for (int i = 0; i < lines.length; i++) {
+      ArtErrorInfo error = parse(lines, i);
+      if (error != null) {
+        errors.add(error);
+        i += error.consumedLines();
+      }
+    }
+    return errors;
+  }
+
+  private static ArtErrorInfo parse(String[] lines, final int line) throws ArtErrorParserException {
+    String methodSig = null;
+    StringBuilder errorMessageContent = new StringBuilder();
+    int currentLine = line;
+    {
+      int index = lines[currentLine].indexOf(VERIFICATION_ERROR_HEADER);
+      if (index >= 0) {
+        // Read/skip the header line.
+        String lineContent = lines[currentLine++].substring(index);
+        // Append the content of each subsequent line that can be parsed as an "error message".
+        for (; currentLine < lines.length; ++currentLine) {
+          lineContent = lines[currentLine].substring(index);
+          String[] parts = lineContent.split(":");
+          if (parts.length == 2) {
+            if (methodSig == null) {
+              methodSig = parts[0];
+              errorMessageContent.append(parts[1]);
+            } else if (methodSig.equals(parts[0])) {
+              errorMessageContent.append(parts[1]);
+            } else {
+              break;
+            }
+          } else if (parts.length >= 3) {
+            if (methodSig == null) {
+              methodSig = parts[1];
+              for (int i = 2; i < parts.length; ++i) {
+                errorMessageContent.append(parts[i]);
+              }
+            } else if (methodSig.equals(parts[1])) {
+              for (int i = 2; i < parts.length; ++i) {
+                errorMessageContent.append(parts[i]);
+              }
+            } else {
+              break;
+            }
+          } else {
+            break;
+          }
+        }
+        if (methodSig == null) {
+          throw new ArtErrorParserException("Unexpected art error message: " + lineContent);
+        }
+      }
+    }
+    if (methodSig == null) {
+      int index = lines[currentLine].indexOf(METHOD_EXCEEDS_INSTRUCITON_LIMIT);
+      if (index >= 0) {
+        String lineContent = lines[currentLine++].substring(index);
+        String[] parts = lineContent.split(":");
+        if (parts.length == 2) {
+          errorMessageContent.append(lineContent);
+          methodSig = parts[1].substring(parts[1].indexOf(" in ") + 4);
+        } else {
+          throw new ArtErrorParserException("Unexpected art error message parts: " + parts);
+        }
+      }
+    }
+
+    // Return if we failed to identify an error description.
+    if (methodSig == null) {
+      return null;
+    }
+
+    String errorMessage = errorMessageContent.toString();
+    ParseInput input = new ParseInput(methodSig);
+    String methodReturn = parseType(input);
+    String[] qualifiedName = parseQualifiedName(input);
+    List<String> methodFormals = parseTypeList(input);
+    List<Long> locations = parseLocations(errorMessage);
+    return new ArtMethodError(getClassName(qualifiedName), getMethodName(qualifiedName),
+        methodReturn, methodFormals, methodSig, errorMessage, locations, currentLine - line);
+  }
+
+  private static String getClassName(String[] parts) {
+    StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < parts.length - 1; i++) {
+      if (i != 0) {
+        builder.append('.');
+      }
+      builder.append(parts[i]);
+    }
+    return builder.toString();
+  }
+
+  private static String getMethodName(String[] parts) {
+    return parts[parts.length - 1];
+  }
+
+  private static String parseType(ParseInput input) throws ArtErrorParserException {
+    input.whitespace();
+    String type = input.until(' ');
+    assert !type.contains("<");
+    input.whitespace();
+    return type;
+  }
+
+  private static String[] parseQualifiedName(ParseInput input) throws ArtErrorParserException {
+    input.whitespace();
+    return input.until('(').split("\\.");
+  }
+
+  private static List<String> parseTypeList(ParseInput input) throws ArtErrorParserException {
+    input.check('(');
+    input.next();
+    String content = input.until(')').trim();
+    if (content.isEmpty()) {
+      return new ArrayList<>();
+    }
+    String[] rawList = content.split(",");
+    List<String> types = new ArrayList<>();
+    for (String type : rawList) {
+      types.add(type.trim());
+    }
+    input.check(')');
+    input.next();
+    return types;
+  }
+
+  private static boolean isHexChar(char c) {
+    return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F';
+  }
+
+  private static List<Long> parseLocations(String input) {
+    List<Long> locations = new ArrayList<>();
+    int length = input.length();
+    int start = 0;
+    while (start < length && (start = input.indexOf("0x", start)) >= 0) {
+      int end = start + 2;
+      while (end < length && isHexChar(input.charAt(end))) {
+        ++end;
+      }
+      long l = Long.parseLong(input.substring(start + 2, end), 16);
+      if (l >= 0) {
+        locations.add(l);
+      }
+      start = end;
+    }
+    return locations;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
new file mode 100644
index 0000000..a203323
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class D8CommandTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void emptyCommand() throws Throwable {
+    verifyEmptyCommand(D8Command.builder().build());
+    verifyEmptyCommand(D8Command.parse(new String[]{}).build());
+  }
+
+  private void verifyEmptyCommand(D8Command command) {
+    assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
+    assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
+    assertEquals(0, ToolHelper.getApp(command).getDexLibraryResources().size());
+    assertEquals(0, ToolHelper.getApp(command).getClassLibraryResources().size());
+    assertFalse(ToolHelper.getApp(command).hasMainDexList());
+    assertFalse(ToolHelper.getApp(command).hasProguardMap());
+    assertFalse(ToolHelper.getApp(command).hasProguardSeeds());
+    assertFalse(ToolHelper.getApp(command).hasPackageDistribution());
+    assertNull(command.getOutputPath());
+    assertEquals(CompilationMode.DEBUG, command.getMode());
+  }
+
+  @Test
+  public void defaultOutIsCwd() throws IOException, InterruptedException {
+    Path working = temp.getRoot().toPath();
+    Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+    Path output = working.resolve("classes.dex");
+    assertFalse(Files.exists(output));
+    assertEquals(0, ToolHelper.forkD8(working, input.toString()).exitCode);
+    assertTrue(Files.exists(output));
+  }
+
+  @Test
+  public void validOutputPath() throws Throwable {
+    Path existingDir = temp.getRoot().toPath();
+    Path nonExistingZip = existingDir.resolve("a-non-existing-archive.zip");
+    assertEquals(
+        existingDir,
+        D8Command.builder().setOutputPath(existingDir).build().getOutputPath());
+    assertEquals(
+        nonExistingZip,
+        D8Command.builder().setOutputPath(nonExistingZip).build().getOutputPath());
+    assertEquals(
+        existingDir,
+        parse("--output", existingDir.toString()).getOutputPath());
+    assertEquals(
+        nonExistingZip,
+        parse("--output", nonExistingZip.toString()).getOutputPath());
+  }
+
+  @Test
+  public void nonExistingOutputDir() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+    D8Command.builder().setOutputPath(nonExistingDir).build();
+  }
+
+  @Test
+  public void existingOutputZip() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+    D8Command.builder().setOutputPath(existingZip).build();
+  }
+
+  @Test
+  public void invalidOutputFileType() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+    D8Command.builder().setOutputPath(invalidType).build();
+  }
+
+  @Test
+  public void nonExistingOutputDirParse() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+    parse("--output", nonExistingDir.toString());
+  }
+
+  @Test
+  public void existingOutputZipParse() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+    parse("--output", existingZip.toString());
+  }
+
+  @Test
+  public void invalidOutputFileTypeParse() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+    parse("--output", invalidType.toString());
+  }
+
+  private D8Command parse(String... args) throws IOException, CompilationException {
+    return D8Command.parse(args).build();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DescriptorUtilsTest.java b/src/test/java/com/android/tools/r8/utils/DescriptorUtilsTest.java
new file mode 100644
index 0000000..034a288
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/DescriptorUtilsTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import org.junit.Test;
+
+public class DescriptorUtilsTest {
+
+  @Test
+  public void toShorty() throws IOException {
+    assertEquals("Z", DescriptorUtils.javaTypeToShorty("boolean"));
+    assertEquals("B", DescriptorUtils.javaTypeToShorty("byte"));
+    assertEquals("S", DescriptorUtils.javaTypeToShorty("short"));
+    assertEquals("C", DescriptorUtils.javaTypeToShorty("char"));
+    assertEquals("I", DescriptorUtils.javaTypeToShorty("int"));
+    assertEquals("J", DescriptorUtils.javaTypeToShorty("long"));
+    assertEquals("F", DescriptorUtils.javaTypeToShorty("float"));
+    assertEquals("D", DescriptorUtils.javaTypeToShorty("double"));
+    assertEquals("L", DescriptorUtils.javaTypeToShorty("int[]"));
+    assertEquals("L", DescriptorUtils.javaTypeToShorty("int[][]"));
+    assertEquals("L", DescriptorUtils.javaTypeToShorty("java.lang.Object"));
+    assertEquals("L", DescriptorUtils.javaTypeToShorty("a.b.C"));
+  }
+
+  @Test
+  public void toDescriptor() throws IOException {
+    assertEquals("Z", DescriptorUtils.javaTypeToDescriptor("boolean"));
+    assertEquals("B", DescriptorUtils.javaTypeToDescriptor("byte"));
+    assertEquals("S", DescriptorUtils.javaTypeToDescriptor("short"));
+    assertEquals("C", DescriptorUtils.javaTypeToDescriptor("char"));
+    assertEquals("I", DescriptorUtils.javaTypeToDescriptor("int"));
+    assertEquals("J", DescriptorUtils.javaTypeToDescriptor("long"));
+    assertEquals("F", DescriptorUtils.javaTypeToDescriptor("float"));
+    assertEquals("D", DescriptorUtils.javaTypeToDescriptor("double"));
+    assertEquals("[I", DescriptorUtils.javaTypeToDescriptor("int[]"));
+    assertEquals("[[I", DescriptorUtils.javaTypeToDescriptor("int[][]"));
+    assertEquals("Ljava/lang/Object;", DescriptorUtils.javaTypeToDescriptor("java.lang.Object"));
+    assertEquals("La/b/C;", DescriptorUtils.javaTypeToDescriptor("a.b.C"));
+  }
+
+  @Test
+  public void toJavaType() throws IOException {
+    assertEquals("boolean", DescriptorUtils.descriptorToJavaType("Z"));
+    assertEquals("byte", DescriptorUtils.descriptorToJavaType("B"));
+    assertEquals("short", DescriptorUtils.descriptorToJavaType("S"));
+    assertEquals("char", DescriptorUtils.descriptorToJavaType("C"));
+    assertEquals("int", DescriptorUtils.descriptorToJavaType("I"));
+    assertEquals("long", DescriptorUtils.descriptorToJavaType("J"));
+    assertEquals("float", DescriptorUtils.descriptorToJavaType("F"));
+    assertEquals("double", DescriptorUtils.descriptorToJavaType("D"));
+    assertEquals("int[]", DescriptorUtils.descriptorToJavaType("[I"));
+    assertEquals("int[][]", DescriptorUtils.descriptorToJavaType("[[I"));
+    assertEquals("java.lang.Object", DescriptorUtils.descriptorToJavaType("Ljava/lang/Object;"));
+    assertEquals("a.b.C", DescriptorUtils.descriptorToJavaType("La/b/C;"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
new file mode 100644
index 0000000..694ccc5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -0,0 +1,1059 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.code.Iget;
+import com.android.tools.r8.code.IgetBoolean;
+import com.android.tools.r8.code.IgetByte;
+import com.android.tools.r8.code.IgetChar;
+import com.android.tools.r8.code.IgetObject;
+import com.android.tools.r8.code.IgetShort;
+import com.android.tools.r8.code.IgetWide;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.InvokeDirectRange;
+import com.android.tools.r8.code.InvokeInterface;
+import com.android.tools.r8.code.InvokeInterfaceRange;
+import com.android.tools.r8.code.InvokeStatic;
+import com.android.tools.r8.code.InvokeStaticRange;
+import com.android.tools.r8.code.InvokeSuper;
+import com.android.tools.r8.code.InvokeSuperRange;
+import com.android.tools.r8.code.InvokeVirtual;
+import com.android.tools.r8.code.InvokeVirtualRange;
+import com.android.tools.r8.code.Iput;
+import com.android.tools.r8.code.IputBoolean;
+import com.android.tools.r8.code.IputByte;
+import com.android.tools.r8.code.IputChar;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.IputShort;
+import com.android.tools.r8.code.IputWide;
+import com.android.tools.r8.code.Sget;
+import com.android.tools.r8.code.SgetBoolean;
+import com.android.tools.r8.code.SgetByte;
+import com.android.tools.r8.code.SgetChar;
+import com.android.tools.r8.code.SgetObject;
+import com.android.tools.r8.code.SgetShort;
+import com.android.tools.r8.code.SgetWide;
+import com.android.tools.r8.code.Sput;
+import com.android.tools.r8.code.SputBoolean;
+import com.android.tools.r8.code.SputByte;
+import com.android.tools.r8.code.SputChar;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.code.SputShort;
+import com.android.tools.r8.code.SputWide;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.ClassNaming;
+import com.android.tools.r8.naming.MemberNaming;
+import com.android.tools.r8.naming.MemberNaming.FieldSignature;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.ProguardMapReader;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class DexInspector {
+
+  private final DexApplication application;
+  private final DexItemFactory dexItemFactory;
+  private final ClassNameMapper mapping;
+  private final BiMap<String, String> originalToObfuscatedMapping;
+
+  private final InstructionSubjectFactory factory = new InstructionSubjectFactory();
+
+  public static MethodSignature MAIN =
+      new MethodSignature("main", "void", new String[]{"java.lang.String[]"});
+
+  public DexInspector(Path file, String mappingFile) throws IOException, ExecutionException {
+    this(Collections.singletonList(file), mappingFile);
+  }
+
+  public DexInspector(Path file) throws IOException, ExecutionException {
+    this(Collections.singletonList(file), null);
+  }
+
+  public DexInspector(List<Path> files) throws IOException, ExecutionException {
+    this(files, null);
+  }
+
+  public DexInspector(List<Path> files, String mappingFile)
+      throws IOException, ExecutionException {
+    ExecutorService executor = Executors.newSingleThreadExecutor();
+    if (mappingFile != null) {
+      this.mapping = ProguardMapReader.mapperFromFile(Paths.get(mappingFile));
+      originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
+    } else {
+      this.mapping = null;
+      originalToObfuscatedMapping = null;
+    }
+    Timing timing = new Timing("DexInspector");
+    InternalOptions options = new InternalOptions();
+    dexItemFactory = options.itemFactory;
+    AndroidApp input = AndroidApp.fromProgramFiles(files);
+    application = new ApplicationReader(input, options, timing).read(executor);
+    executor.shutdown();
+  }
+
+  public DexInspector(AndroidApp app) throws IOException, ExecutionException {
+    this(new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector")).read());
+  }
+
+  public DexInspector(DexApplication application) {
+    dexItemFactory = application.dexItemFactory;
+    this.application = application;
+    this.mapping = application.getProguardMap();
+    originalToObfuscatedMapping =
+        mapping == null ? null : mapping.getObfuscatedToOriginalMapping().inverse();
+  }
+
+  public DexItemFactory getFactory() {
+    return dexItemFactory;
+  }
+
+  private DexType toDexType(String string) {
+    return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(string));
+  }
+
+  private static <S, T extends Subject> void forAll(S[] items,
+      BiFunction<S, FoundClassSubject, ? extends T> constructor,
+      FoundClassSubject clazz,
+      Consumer<T> consumer) {
+    for (S item : items) {
+      consumer.accept(constructor.apply(item, clazz));
+    }
+  }
+
+  private static <S, T extends Subject> void forAll(Iterable<S> items, Function<S, T> constructor,
+      Consumer<T> consumer) {
+    for (S item : items) {
+      consumer.accept(constructor.apply(item));
+    }
+  }
+
+
+  public ClassSubject clazz(String name) {
+    ClassNaming naming = null;
+    if (mapping != null) {
+      String obfuscated = originalToObfuscatedMapping.get(name);
+      if (obfuscated != null) {
+        naming = mapping.getClassNaming(obfuscated);
+        name = obfuscated;
+      }
+    }
+    DexClass clazz = application.definitionFor(toDexType(name));
+    if (clazz == null) {
+      return new AbsentClassSubject();
+    }
+    return new FoundClassSubject(clazz, naming);
+  }
+
+  public void forAllClasses(Consumer<FoundClassSubject> inspection) {
+    forAll(application.classes(), clazz -> {
+      ClassNaming naming = null;
+      if (mapping != null) {
+        String obfuscated = originalToObfuscatedMapping.get(clazz.type.toSourceString());
+        if (obfuscated != null) {
+          naming = mapping.getClassNaming(obfuscated);
+        }
+      }
+      return new FoundClassSubject(clazz, naming);
+    }, inspection);
+  }
+
+  private String getObfuscatedTypeName(String originalTypeName) {
+    String obfuscatedType = null;
+    if (mapping != null) {
+      obfuscatedType = originalToObfuscatedMapping.get(originalTypeName);
+    }
+    obfuscatedType = obfuscatedType == null ? originalTypeName : obfuscatedType;
+    return obfuscatedType;
+  }
+
+  public abstract class Subject {
+
+    public abstract boolean isPresent();
+  }
+
+  public abstract class AnnotationSubject extends Subject {
+
+    public abstract DexEncodedAnnotation getAnnotation();
+  }
+
+  public class FoundAnnotationSubject extends AnnotationSubject {
+
+    private final DexAnnotation annotation;
+
+    private FoundAnnotationSubject(DexAnnotation annotation) {
+      this.annotation = annotation;
+    }
+
+    @Override
+    public boolean isPresent() {
+      return true;
+    }
+
+    @Override
+    public DexEncodedAnnotation getAnnotation() {
+      return annotation.annotation;
+    }
+  }
+
+  public class AbsentAnnotationSubject extends AnnotationSubject {
+
+    @Override
+    public boolean isPresent() {
+      return false;
+    }
+
+    @Override
+    public DexEncodedAnnotation getAnnotation() {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+
+  public abstract class ClassSubject extends Subject {
+
+    public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
+
+    public abstract MethodSubject method(String returnType, String name, List<String> parameters);
+
+    public MethodSubject method(MethodSignature signature) {
+      return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
+    }
+
+    public abstract void forAllFields(Consumer<FoundFieldSubject> inspection);
+
+    public abstract FieldSubject field(String type, String name);
+
+    public abstract boolean isAbstract();
+
+    public String dumpMethods() {
+      StringBuilder dump = new StringBuilder();
+      forAllMethods((FoundMethodSubject method) ->
+          dump.append(method.getMethod().toString())
+              .append(method.getMethod().codeToString()));
+      return dump.toString();
+    }
+
+    public abstract DexClass getDexClass();
+
+    public abstract AnnotationSubject annotation(String name);
+
+    public abstract String getOriginalDescriptor();
+
+    public abstract String getFinalDescriptor();
+  }
+
+  private class AbsentClassSubject extends ClassSubject {
+
+    @Override
+    public boolean isPresent() {
+      return false;
+    }
+
+    @Override
+    public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
+    }
+
+    @Override
+    public MethodSubject method(String returnType, String name, List<String> parameters) {
+      return new AbsentMethodSubject();
+    }
+
+    @Override
+    public void forAllFields(Consumer<FoundFieldSubject> inspection) {
+    }
+
+    @Override
+    public FieldSubject field(String type, String name) {
+      return new AbsentFieldSubject();
+    }
+
+    @Override
+    public boolean isAbstract() {
+      return false;
+    }
+
+    @Override
+    public DexClass getDexClass() {
+      return null;
+    }
+
+    @Override
+    public AnnotationSubject annotation(String name) {
+      return new AbsentAnnotationSubject();
+    }
+
+    @Override
+    public String getOriginalDescriptor() {
+      return null;
+    }
+
+    @Override
+    public String getFinalDescriptor() {
+      return null;
+    }
+  }
+
+  public class FoundClassSubject extends ClassSubject {
+
+    private final DexClass dexClass;
+    private final ClassNaming naming;
+
+    private FoundClassSubject(DexClass dexClass, ClassNaming naming) {
+      this.dexClass = dexClass;
+      this.naming = naming;
+    }
+
+    @Override
+    public boolean isPresent() {
+      return true;
+    }
+
+    @Override
+    public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
+      forAll(dexClass.directMethods(), FoundMethodSubject::new, this, inspection);
+      forAll(dexClass.virtualMethods(), FoundMethodSubject::new, this, inspection);
+    }
+
+    @Override
+    public MethodSubject method(String returnType, String name, List<String> parameters) {
+      DexType[] parameterTypes = new DexType[parameters.size()];
+      for (int i = 0; i < parameters.size(); i++) {
+        parameterTypes[i] = toDexType(getObfuscatedTypeName(parameters.get(i)));
+      }
+      DexProto proto = dexItemFactory.createProto(toDexType(getObfuscatedTypeName(returnType)),
+          parameterTypes);
+      if (naming != null) {
+        String[] parameterStrings = new String[parameterTypes.length];
+        Signature signature = new MethodSignature(name, returnType,
+            parameters.toArray(parameterStrings));
+        MemberNaming methodNaming = naming.lookupByOriginalSignature(signature);
+        if (methodNaming != null) {
+          name = methodNaming.getRenamedName();
+        }
+      }
+      DexMethod dexMethod =
+          dexItemFactory.createMethod(dexClass.type, proto, dexItemFactory.createString(name));
+      DexEncodedMethod encoded = findMethod(dexClass.directMethods(), dexMethod);
+      if (encoded == null) {
+        encoded = findMethod(dexClass.virtualMethods(), dexMethod);
+      }
+      return encoded == null ? new AbsentMethodSubject() : new FoundMethodSubject(encoded, this);
+    }
+
+    private DexEncodedMethod findMethod(DexEncodedMethod[] methods, DexMethod dexMethod) {
+      for (DexEncodedMethod method : methods) {
+        if (method.method.equals(dexMethod)) {
+          return method;
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public void forAllFields(Consumer<FoundFieldSubject> inspection) {
+      forAll(dexClass.staticFields(), FoundFieldSubject::new, this, inspection);
+      forAll(dexClass.instanceFields(), FoundFieldSubject::new, this, inspection);
+    }
+
+    @Override
+    public FieldSubject field(String type, String name) {
+      String obfuscatedType = getObfuscatedTypeName(type);
+      MemberNaming fieldNaming = null;
+      if (naming != null) {
+        fieldNaming = naming.lookupByOriginalSignature(
+            new FieldSignature(name, type));
+      }
+      String obfuscatedName = fieldNaming == null ? name : fieldNaming.getRenamedName();
+
+      DexField field = dexItemFactory.createField(dexClass.type,
+          toDexType(obfuscatedType), dexItemFactory.createString(obfuscatedName));
+      DexEncodedField encoded = findField(dexClass.staticFields(), field);
+      if (encoded == null) {
+        encoded = findField(dexClass.instanceFields(), field);
+      }
+      return encoded == null ? new AbsentFieldSubject() : new FoundFieldSubject(encoded, this);
+    }
+
+    @Override
+    public boolean isAbstract() {
+      return dexClass.accessFlags.isAbstract();
+    }
+
+    private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
+      for (DexEncodedField field : fields) {
+        if (field.field.equals(dexField)) {
+          return field;
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public DexClass getDexClass() {
+      return dexClass;
+    }
+
+    @Override
+    public AnnotationSubject annotation(String name) {
+      DexAnnotation annotation = findAnnotation(name);
+      return annotation == null
+          ? new AbsentAnnotationSubject()
+          : new FoundAnnotationSubject(annotation);
+    }
+
+    private DexAnnotation findAnnotation(String name) {
+      for (DexAnnotation annotation : dexClass.annotations.annotations) {
+        DexType type = annotation.annotation.type;
+        String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
+        if (original.equals(name)) {
+          return annotation;
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public String getOriginalDescriptor() {
+      if (naming != null) {
+        return DescriptorUtils.javaTypeToDescriptor(naming.originalName);
+      } else {
+        return getFinalDescriptor();
+      }
+    }
+
+    @Override
+    public String getFinalDescriptor() {
+      return dexClass.type.descriptor.toString();
+    }
+  }
+
+  public abstract class MemberSubject extends Subject {
+
+    public abstract boolean hasAll(DexAccessFlags flags);
+
+    public abstract boolean hasNone(DexAccessFlags flags);
+
+    public abstract boolean isStatic();
+
+    public abstract boolean isFinal();
+
+    public abstract Signature getOriginalSignature();
+  }
+
+  public abstract class MethodSubject extends MemberSubject {
+
+    public abstract boolean isAbstract();
+
+    public abstract DexEncodedMethod getMethod();
+
+    public Iterator<InstructionSubject> iterateInstructions() {
+      return null;
+    }
+
+    public <T extends InstructionSubject> Iterator<T> iterateInstructions(
+        Predicate<InstructionSubject> filter) {
+      return null;
+    }
+  }
+
+  public class AbsentMethodSubject extends MethodSubject {
+
+    @Override
+    public boolean isPresent() {
+      return false;
+    }
+
+    @Override
+    public boolean hasAll(DexAccessFlags flags) {
+      return false;
+    }
+
+    @Override
+    public boolean hasNone(DexAccessFlags flags) {
+      return true;
+    }
+
+    @Override
+    public boolean isStatic() {
+      return false;
+    }
+
+    @Override
+    public boolean isFinal() {
+      return false;
+    }
+
+    @Override
+    public boolean isAbstract() {
+      return false;
+    }
+
+    @Override
+    public DexEncodedMethod getMethod() {
+      return null;
+    }
+
+    @Override
+    public Signature getOriginalSignature() {
+      return null;
+    }
+  }
+
+  public class FoundMethodSubject extends MethodSubject {
+
+    private final FoundClassSubject clazz;
+    private final DexEncodedMethod dexMethod;
+
+    public FoundMethodSubject(DexEncodedMethod encoded, FoundClassSubject clazz) {
+      this.clazz = clazz;
+      this.dexMethod = encoded;
+    }
+
+    @Override
+    public boolean isPresent() {
+      return true;
+    }
+
+    @Override
+    public boolean hasAll(DexAccessFlags flags) {
+      return dexMethod.accessFlags.containsAllOf(flags);
+    }
+
+    @Override
+    public boolean hasNone(DexAccessFlags flags) {
+      return dexMethod.accessFlags.containsNoneOf(flags);
+    }
+
+    @Override
+    public boolean isStatic() {
+      return dexMethod.accessFlags.isStatic();
+    }
+
+    @Override
+    public boolean isFinal() {
+      return dexMethod.accessFlags.isFinal();
+    }
+
+    @Override
+    public boolean isAbstract() {
+      return dexMethod.accessFlags.isAbstract();
+    }
+
+    @Override
+    public DexEncodedMethod getMethod() {
+      return dexMethod;
+    }
+
+    @Override
+    public MethodSignature getOriginalSignature() {
+      MethodSignature signature = MemberNaming.MethodSignature.fromDexMethod(dexMethod.method);
+      return clazz.naming != null ?
+          (MethodSignature) clazz.naming.lookup(signature).getOriginalSignature() :
+          signature;
+    }
+
+    @Override
+    public Iterator<InstructionSubject> iterateInstructions() {
+      return new InstructionIterator(this);
+    }
+
+    @Override
+    public <T extends InstructionSubject> Iterator<T> iterateInstructions(
+        Predicate<InstructionSubject> filter) {
+      return new FilteredInstructionIterator<>(this, filter);
+    }
+  }
+
+  public abstract class FieldSubject extends MemberSubject {
+
+    public abstract DexEncodedField getField();
+  }
+
+  public class AbsentFieldSubject extends FieldSubject {
+
+    @Override
+    public boolean hasAll(DexAccessFlags flags) {
+      return false;
+    }
+
+    @Override
+    public boolean hasNone(DexAccessFlags flags) {
+      return true;
+    }
+
+    @Override
+    public boolean isStatic() {
+      return false;
+    }
+
+    @Override
+    public boolean isFinal() {
+      return false;
+    }
+
+    @Override
+    public boolean isPresent() {
+      return false;
+    }
+
+    @Override
+    public Signature getOriginalSignature() {
+      return null;
+    }
+
+    @Override
+    public DexEncodedField getField() {
+      return null;
+    }
+  }
+
+  public class FoundFieldSubject extends FieldSubject {
+
+    private final FoundClassSubject clazz;
+    private final DexEncodedField dexField;
+
+    public FoundFieldSubject(DexEncodedField dexField, FoundClassSubject clazz) {
+      this.clazz = clazz;
+      this.dexField = dexField;
+    }
+
+    @Override
+    public boolean hasAll(DexAccessFlags flags) {
+      return dexField.accessFlags.containsAllOf(flags);
+    }
+
+    @Override
+    public boolean hasNone(DexAccessFlags flags) {
+      return dexField.accessFlags.containsNoneOf(flags);
+    }
+
+    @Override
+    public boolean isStatic() {
+      return dexField.accessFlags.isStatic();
+    }
+
+    @Override
+    public boolean isFinal() {
+      return dexField.accessFlags.isFinal();
+    }
+
+    @Override
+    public boolean isPresent() {
+      return true;
+    }
+
+    public TypeSubject type() {
+      return new TypeSubject(dexField.field.type);
+    }
+
+    @Override
+    public FieldSignature getOriginalSignature() {
+      FieldSignature signature = MemberNaming.FieldSignature.fromDexField(dexField.field);
+      return clazz.naming != null ?
+          (FieldSignature) clazz.naming.lookup(signature).getOriginalSignature() :
+          signature;
+    }
+
+    @Override
+    public DexEncodedField getField() {
+      return dexField;
+    }
+  }
+
+  public class TypeSubject extends Subject {
+
+    private final DexType dexType;
+
+    public TypeSubject(DexType dexType) {
+      this.dexType = dexType;
+    }
+
+    @Override
+    public boolean isPresent() {
+      return true;
+    }
+
+    public boolean is(String type) {
+      return dexType.equals(toDexType(type));
+    }
+
+    public String toString() {
+      return dexType.toSourceString();
+    }
+  }
+
+  private class InstructionSubjectFactory {
+
+    InstructionSubject create(Instruction instruction) {
+      if (isInvoke(instruction)) {
+        return new InvokeInstructionSubject(this, instruction);
+      } else if (isFieldAccess(instruction)) {
+        return new FieldAccessInstructionSubject(this, instruction);
+      } else {
+        return new InstructionSubject(this, instruction);
+      }
+    }
+
+    boolean isInvoke(Instruction instruction) {
+      return isInvokeVirtual(instruction)
+          || isInvokeInterface(instruction)
+          || isInvokeDirect(instruction)
+          || isInvokeSuper(instruction)
+          || isInvokeStatic(instruction);
+    }
+
+    boolean isInvokeVirtual(Instruction instruction) {
+      return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
+    }
+
+    boolean isInvokeInterface(Instruction instruction) {
+      return instruction instanceof InvokeInterface || instruction instanceof InvokeInterfaceRange;
+    }
+
+    boolean isInvokeDirect(Instruction instruction) {
+      return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
+    }
+
+    boolean isInvokeSuper(Instruction instruction) {
+      return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
+    }
+
+    boolean isInvokeStatic(Instruction instruction) {
+      return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
+    }
+
+    boolean isFieldAccess(Instruction instruction) {
+      return isInstanceGet(instruction)
+          || isInstancePut(instruction)
+          || isStaticGet(instruction)
+          || isStaticSet(instruction);
+    }
+
+    boolean isInstanceGet(Instruction instruction) {
+      return instruction instanceof Iget
+          || instruction instanceof IgetBoolean
+          || instruction instanceof IgetByte
+          || instruction instanceof IgetShort
+          || instruction instanceof IgetChar
+          || instruction instanceof IgetWide
+          || instruction instanceof IgetObject;
+    }
+
+    boolean isInstancePut(Instruction instruction) {
+      return instruction instanceof Iput
+          || instruction instanceof IputBoolean
+          || instruction instanceof IputByte
+          || instruction instanceof IputShort
+          || instruction instanceof IputChar
+          || instruction instanceof IputWide
+          || instruction instanceof IputObject;
+    }
+
+    boolean isStaticGet(Instruction instruction) {
+      return instruction instanceof Sget
+          || instruction instanceof SgetBoolean
+          || instruction instanceof SgetByte
+          || instruction instanceof SgetShort
+          || instruction instanceof SgetChar
+          || instruction instanceof SgetWide
+          || instruction instanceof SgetObject;
+    }
+
+    boolean isStaticSet(Instruction instruction) {
+      return instruction instanceof Sput
+          || instruction instanceof SputBoolean
+          || instruction instanceof SputByte
+          || instruction instanceof SputShort
+          || instruction instanceof SputChar
+          || instruction instanceof SputWide
+          || instruction instanceof SputObject;
+    }
+  }
+
+  public class InstructionSubject {
+
+    protected final InstructionSubjectFactory factory;
+    protected final Instruction instruction;
+
+    protected InstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
+      this.factory = factory;
+      this.instruction = instruction;
+    }
+
+    public boolean isInvoke() {
+      return factory.isInvoke(instruction);
+    }
+
+    public boolean isFieldAccess() {
+      return factory.isFieldAccess(instruction);
+    }
+
+    public boolean isInvokeVirtual() {
+      return factory.isInvokeVirtual(instruction);
+    }
+
+    public boolean isInvokeInterface() {
+      return factory.isInvokeInterface(instruction);
+    }
+
+    public boolean isInvokeDirect() {
+      return factory.isInvokeDirect(instruction);
+    }
+
+    public boolean isInvokeSuper() {
+      return factory.isInvokeSuper(instruction);
+    }
+
+    public boolean isInvokeStatic() {
+      return factory.isInvokeStatic(instruction);
+    }
+
+    boolean isFieldAccess(Instruction instruction) {
+      return factory.isFieldAccess(instruction);
+    }
+  }
+
+  public class InvokeInstructionSubject extends InstructionSubject {
+
+    InvokeInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
+      super(factory, instruction);
+      assert isInvoke();
+    }
+
+    public TypeSubject holder() {
+      return new TypeSubject(invokedMethod().getHolder());
+    }
+
+    public DexMethod invokedMethod() {
+      if (instruction instanceof InvokeVirtual) {
+        return ((InvokeVirtual) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeVirtualRange) {
+        return ((InvokeVirtualRange) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeInterface) {
+        return ((InvokeInterface) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeInterfaceRange) {
+        return ((InvokeInterfaceRange) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeDirect) {
+        return ((InvokeDirect) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeDirectRange) {
+        return ((InvokeDirectRange) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeSuper) {
+        return ((InvokeSuper) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeSuperRange) {
+        return ((InvokeSuperRange) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeDirect) {
+        return ((InvokeDirect) instruction).getMethod();
+      }
+      if (instruction instanceof InvokeDirectRange) {
+        return ((InvokeDirectRange) instruction).getMethod();
+      }
+      assert false;
+      return null;
+    }
+  }
+
+  public class FieldAccessInstructionSubject extends InstructionSubject {
+
+    FieldAccessInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
+      super(factory, instruction);
+      assert isFieldAccess();
+    }
+
+    public TypeSubject holder() {
+      return new TypeSubject(accessedField().getHolder());
+    }
+
+    public DexField accessedField() {
+      if (instruction instanceof Iget) {
+        return ((Iget) instruction).getField();
+      }
+      if (instruction instanceof IgetBoolean) {
+        return ((IgetBoolean) instruction).getField();
+      }
+      if (instruction instanceof IgetByte) {
+        return ((IgetByte) instruction).getField();
+      }
+      if (instruction instanceof IgetShort) {
+        return ((IgetShort) instruction).getField();
+      }
+      if (instruction instanceof IgetChar) {
+        return ((IgetChar) instruction).getField();
+      }
+      if (instruction instanceof IgetWide) {
+        return ((IgetWide) instruction).getField();
+      }
+      if (instruction instanceof IgetObject) {
+        return ((IgetObject) instruction).getField();
+      }
+      if (instruction instanceof Iput) {
+        return ((Iput) instruction).getField();
+      }
+      if (instruction instanceof IputBoolean) {
+        return ((IputBoolean) instruction).getField();
+      }
+      if (instruction instanceof IputByte) {
+        return ((IputByte) instruction).getField();
+      }
+      if (instruction instanceof IputShort) {
+        return ((IputShort) instruction).getField();
+      }
+      if (instruction instanceof IputChar) {
+        return ((IputChar) instruction).getField();
+      }
+      if (instruction instanceof IputWide) {
+        return ((IputWide) instruction).getField();
+      }
+      if (instruction instanceof IputObject) {
+        return ((IputObject) instruction).getField();
+      }
+      if (instruction instanceof Sget) {
+        return ((Sget) instruction).getField();
+      }
+      if (instruction instanceof SgetBoolean) {
+        return ((SgetBoolean) instruction).getField();
+      }
+      if (instruction instanceof SgetByte) {
+        return ((SgetByte) instruction).getField();
+      }
+      if (instruction instanceof SgetShort) {
+        return ((SgetShort) instruction).getField();
+      }
+      if (instruction instanceof SgetChar) {
+        return ((SgetChar) instruction).getField();
+      }
+      if (instruction instanceof SgetWide) {
+        return ((SgetWide) instruction).getField();
+      }
+      if (instruction instanceof SgetObject) {
+        return ((SgetObject) instruction).getField();
+      }
+      if (instruction instanceof Sput) {
+        return ((Sput) instruction).getField();
+      }
+      if (instruction instanceof SputBoolean) {
+        return ((SputBoolean) instruction).getField();
+      }
+      if (instruction instanceof SputByte) {
+        return ((SputByte) instruction).getField();
+      }
+      if (instruction instanceof SputShort) {
+        return ((SputShort) instruction).getField();
+      }
+      if (instruction instanceof SputChar) {
+        return ((SputChar) instruction).getField();
+      }
+      if (instruction instanceof SputWide) {
+        return ((SputWide) instruction).getField();
+      }
+      if (instruction instanceof SputObject) {
+        return ((SputObject) instruction).getField();
+      }
+      assert false;
+      return null;
+    }
+  }
+
+  private class InstructionIterator implements Iterator<InstructionSubject> {
+
+    private final DexCode code;
+    private int index;
+
+    InstructionIterator(MethodSubject method) {
+      assert method.isPresent();
+      this.code = method.getMethod().getCode().asDexCode();
+      this.index = 0;
+    }
+
+    @Override
+    public boolean hasNext() {
+      return index < code.instructions.length;
+    }
+
+    @Override
+    public InstructionSubject next() {
+      if (index == code.instructions.length) {
+        throw new NoSuchElementException();
+      }
+      return factory.create(code.instructions[index++]);
+    }
+  }
+
+  private class FilteredInstructionIterator<T extends InstructionSubject> implements Iterator<T> {
+
+    private final InstructionIterator iterator;
+    private final Predicate<InstructionSubject> predicate;
+    private InstructionSubject pendingNext = null;
+
+    FilteredInstructionIterator(MethodSubject method, Predicate<InstructionSubject> predicate) {
+      this.iterator = new InstructionIterator(method);
+      this.predicate = predicate;
+      hasNext();
+    }
+
+    @Override
+    public boolean hasNext() {
+      if (pendingNext == null) {
+        while (iterator.hasNext()) {
+          pendingNext = iterator.next();
+          if (predicate.test(pendingNext)) {
+            break;
+          }
+          pendingNext = null;
+        }
+      }
+      return pendingNext != null;
+    }
+
+    @Override
+    public T next() {
+      hasNext();
+      if (pendingNext == null) {
+        throw new NoSuchElementException();
+      }
+      // We cannot tell if the provided predicate will only match instruction subjects of type T.
+      @SuppressWarnings("unchecked")
+      T result = (T) pendingNext;
+      pendingNext = null;
+      return result;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/HashMapIntTest.java b/src/test/java/com/android/tools/r8/utils/HashMapIntTest.java
new file mode 100644
index 0000000..5d8fffe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/HashMapIntTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Random;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HashMapIntTest {
+
+  static private String getString(int i) {
+    String SALTCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+    StringBuilder salt = new StringBuilder();
+    Random rnd = new Random();
+    while (salt.length() < 18) {
+      int index = (int) (rnd.nextFloat() * SALTCHARS.length());
+      salt.append(SALTCHARS.charAt(index));
+    }
+    salt.append(" ");
+    salt.append(i);
+    return salt.toString();
+  }
+
+  @Test(expected = RuntimeException.class)
+  public void putInvalid() throws Exception {
+    HashMapInt<Object> map = new HashMapInt<>();
+    map.put(null, 12);
+  }
+
+  @Test
+  public void put() throws Exception {
+    HashMapInt<Object> map = new HashMapInt<>();
+    Object key = new Object();
+    int value = 0;
+    map.put(key, value);
+    Assert.assertTrue(map.containsKey(key));
+    Assert.assertFalse(map.containsKey(new Object[0]));
+    Assert.assertEquals(map.get(key), value);
+    Assert.assertEquals(map.size(), 1);
+  }
+
+  @Test
+  public void random() throws Exception {
+    final int length = 5999;
+    HashMapInt<String> map = new HashMapInt<>();
+    String[] array = new String[length];
+    for (int i = 0; i < length; i++) {
+      array[i] = getString(i);
+      map.put(array[i], i * 3);
+    }
+    Assert.assertEquals(length, map.size());
+    for (int i = 0; i < length; i++) {
+      Assert.assertEquals(map.get(array[i]), i * 3);
+    }
+    for (int i : map.values()) {
+      Assert.assertTrue(i < length * 3 && i >= 0 && i % 3 == 0);
+    }
+    Assert.assertEquals(length, Iterables.size(map.values()));
+    Set<String> items = ImmutableSet.copyOf(array);
+    for (String s : map.keys()) {
+      Assert.assertTrue(items.contains(s));
+    }
+    Assert.assertEquals(length, Iterables.size(map.keys()));
+  }
+
+  @Test
+  public void overwrite() throws Exception {
+    HashMapInt<Object> map = new HashMapInt<>();
+    Object key = new Object();
+    int value1 = 0;
+    map.put(key, value1);
+    Assert.assertEquals(map.get(key), value1);
+    int value2 = -1;
+    map.put(key, value2);
+    Assert.assertEquals(map.get(key), value2);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/IdentityHashMapIntTest.java b/src/test/java/com/android/tools/r8/utils/IdentityHashMapIntTest.java
new file mode 100644
index 0000000..dd57960
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/IdentityHashMapIntTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IdentityHashMapIntTest {
+
+  @Test(expected = RuntimeException.class)
+  public void putInvalid() throws Exception {
+    IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+    map.put(null, 12);
+  }
+
+  @Test
+  public void put() throws Exception {
+    IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+    Object key = new AllEqual();
+    int value = 0;
+    map.put(key, value);
+    Assert.assertTrue(map.containsKey(key));
+    Assert.assertFalse(map.containsKey(new Object()));
+    Assert.assertFalse(map.containsKey(new AllEqual()));
+    Assert.assertEquals(map.get(key), value);
+    Assert.assertEquals(map.size(), 1);
+  }
+
+  @Test
+  public void random() throws Exception {
+    final int length = 5999;
+    IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+    AllEqual[] array = new AllEqual[length];
+    for (int i = 0; i < length; i++) {
+      array[i] = new AllEqual();
+      map.put(array[i], i * 3);
+    }
+    Assert.assertEquals(length, map.size());
+    for (int i = 0; i < length; i++) {
+      Assert.assertEquals(map.get(array[i]), i * 3);
+    }
+    for (int i : map.values()) {
+      Assert.assertTrue(i < length * 3 && i >= 0 && i % 3 == 0);
+    }
+    Assert.assertEquals(length, Iterables.size(map.values()));
+    Set<Object> items = Sets.newIdentityHashSet();
+    Collections.addAll(items, array);
+    for (Object o : map.keys()) {
+      Assert.assertTrue(items.contains(o));
+    }
+    Assert.assertEquals(length, Iterables.size(map.keys()));
+  }
+
+  @Test
+  public void overwrite() throws Exception {
+    IdentityHashMapInt<Object> map = new IdentityHashMapInt<>();
+    Object key = new Object();
+    int value1 = 0;
+    map.put(key, value1);
+    Assert.assertEquals(map.get(key), value1);
+    int value2 = -1;
+    map.put(key, value2);
+    Assert.assertEquals(map.get(key), value2);
+  }
+
+  private static class AllEqual {
+
+    @Override
+    public boolean equals(Object o) {
+      return true;
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/IntHashMapTest.java b/src/test/java/com/android/tools/r8/utils/IntHashMapTest.java
new file mode 100644
index 0000000..c4b4dc2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/IntHashMapTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Random;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IntHashMapTest {
+
+  static private String getRandomString() {
+    String SALTCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+    StringBuilder salt = new StringBuilder();
+    Random rnd = new Random();
+    while (salt.length() < 18) {
+      int index = (int) (rnd.nextFloat() * SALTCHARS.length());
+      salt.append(SALTCHARS.charAt(index));
+    }
+    return salt.toString();
+  }
+
+  @Test(expected = RuntimeException.class)
+  public void putInvalid() throws Exception {
+    IntHashMap<Object> map = new IntHashMap<>();
+    map.put(12, null);
+  }
+
+  @Test
+  public void put() throws Exception {
+    IntHashMap<Object> map = new IntHashMap<>();
+    int key = 1;
+    Object value = new Object();
+    map.put(key, value);
+    Assert.assertEquals(map.get(key), value);
+    Assert.assertEquals(map.size(), 1);
+    map.put(key + 1, value);
+    Assert.assertEquals(map.get(key + 1), value);
+    Assert.assertEquals(map.size(), 2);
+    Assert.assertEquals(map.get(0), null);
+    map.put(0, value);
+    Assert.assertEquals(map.get(0), value);
+  }
+
+  @Test
+  public void random() throws Exception {
+    final int length = 5999;
+    IntHashMap<String> map = new IntHashMap<>();
+    String[] array = new String[length];
+    for (int i = 0; i < length; i++) {
+      array[i] = getRandomString();
+      map.put(i, array[i]);
+    }
+    Assert.assertEquals(length, map.size());
+    for (int i = 0; i < length; i++) {
+      Assert.assertEquals(map.get(i), array[i]);
+    }
+    Set<String> items = ImmutableSet.copyOf(array);
+    for (String s : map.values()) {
+      Assert.assertTrue(items.contains(s));
+    }
+    Assert.assertEquals(length, Iterables.size(map.values()));
+    for (int i : map.keys()) {
+      Assert.assertTrue(i < length && i >= 0);
+    }
+    Assert.assertEquals(length, Iterables.size(map.keys()));
+  }
+
+  @Test
+  public void overwrite() throws Exception {
+    IntHashMap<Object> map = new IntHashMap<>();
+    int key = 1;
+    Object value1 = new Object();
+    map.put(key, value1);
+    Assert.assertEquals(map.get(key), value1);
+    Object value2 = new Object[0];
+    map.put(key, value2);
+    Assert.assertEquals(map.get(key), value2);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/IntIntHashMapTest.java b/src/test/java/com/android/tools/r8/utils/IntIntHashMapTest.java
new file mode 100644
index 0000000..d129a03
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/IntIntHashMapTest.java
@@ -0,0 +1,75 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.Iterables;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Random;
+import java.util.Set;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class IntIntHashMapTest {
+
+  @Test(expected = RuntimeException.class)
+  public void putInvalid() throws Exception {
+    IntIntHashMap map = new IntIntHashMap();
+    map.put(-1, 12);
+  }
+
+  @Test
+  public void put() throws Exception {
+    IntIntHashMap map = new IntIntHashMap();
+    int key = 22;
+    int value = 0;
+    map.put(key, value);
+    Assert.assertTrue(map.containsKey(key));
+    Assert.assertFalse(map.containsKey(33));
+    Assert.assertEquals(map.get(key), value);
+    Assert.assertEquals(map.size(), 1);
+  }
+
+  @Test
+  public void random() throws Exception {
+    final int length = 5999;
+    IntIntHashMap map = new IntIntHashMap();
+    Random rnd = new Random();
+    Set<Integer> seen = new LinkedHashSet<>();
+    String[] array = new String[length];
+    for (int i = 0; i < length; i++) {
+      int next;
+      do {
+        next = rnd.nextInt(4 * length);
+      } while (seen.contains(next));
+      seen.add(next);
+      map.put(next, i * 3);
+    }
+    Assert.assertEquals(seen.size(), map.size());
+    Iterator<Integer> it = seen.iterator();
+    for (int i = 0; i < length; i++) {
+      Assert.assertEquals(map.get(it.next()), i * 3);
+    }
+    for (int i : map.values()) {
+      Assert.assertTrue(i < length * 3 && i >= 0 && i % 3 == 0);
+    }
+    Assert.assertEquals(length, Iterables.size(map.values()));
+    for (Integer key : map.keys()) {
+      Assert.assertTrue(seen.contains(key));
+    }
+    Assert.assertEquals(seen.size(), Iterables.size(map.keys()));
+  }
+
+  @Test
+  public void overwrite() throws Exception {
+    IntIntHashMap map = new IntIntHashMap();
+    int key = 42;
+    int value1 = 0;
+    map.put(key, value1);
+    Assert.assertEquals(map.get(key), value1);
+    int value2 = -1;
+    map.put(key, value2);
+    Assert.assertEquals(map.get(key), value2);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/JarBuilder.java b/src/test/java/com/android/tools/r8/utils/JarBuilder.java
new file mode 100644
index 0000000..9186ff4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/JarBuilder.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.io.ByteStreams;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+
+public class JarBuilder {
+  public static void buildJar(File[] files, File jarFile) throws IOException {
+    JarOutputStream target = new JarOutputStream(new FileOutputStream(jarFile));
+    for (File file : files) {
+      // Only use the file name in the JAR entry (classes.dex, classes2.dex, ...)
+      JarEntry entry = new JarEntry(file.getName());
+      entry.setTime(file.lastModified());
+      target.putNextEntry(entry);
+      InputStream in = new BufferedInputStream(new FileInputStream(file));
+      ByteStreams.copy(in, target);
+      in.close();
+      target.closeEntry();
+    }
+    target.close();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
new file mode 100644
index 0000000..a6e10a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -0,0 +1,149 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+
+public class R8CommandTest {
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void emptyCommand() throws Throwable {
+    verifyEmptyCommand(R8Command.builder().build());
+    verifyEmptyCommand(parse());
+  }
+
+  private void verifyEmptyCommand(R8Command command) {
+    assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
+    assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
+    assertEquals(0, ToolHelper.getApp(command).getDexLibraryResources().size());
+    assertEquals(0, ToolHelper.getApp(command).getClassLibraryResources().size());
+    assertFalse(ToolHelper.getApp(command).hasMainDexList());
+    assertFalse(ToolHelper.getApp(command).hasProguardMap());
+    assertFalse(ToolHelper.getApp(command).hasProguardSeeds());
+    assertFalse(ToolHelper.getApp(command).hasPackageDistribution());
+    assertNull(command.getOutputPath());
+    assertFalse(command.useMinification());
+    assertFalse(command.useTreeShaking());
+    assertEquals(CompilationMode.RELEASE, command.getMode());
+  }
+
+  @Test
+  public void defaultOutIsCwd() throws IOException, InterruptedException {
+    Path working = temp.getRoot().toPath();
+    Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
+    Path output = working.resolve("classes.dex");
+    assertFalse(Files.exists(output));
+    ProcessResult result = ToolHelper.forkR8(working, input.toString());
+    assertEquals("R8 run failed: " + result.stderr, 0, result.exitCode);
+    assertTrue(Files.exists(output));
+  }
+
+  @Test
+  public void validOutputPath() throws Throwable {
+    Path existingDir = temp.getRoot().toPath();
+    Path nonExistingZip = existingDir.resolve("a-non-existing-archive.zip");
+    assertEquals(
+        existingDir,
+        R8Command.builder().setOutputPath(existingDir).build().getOutputPath());
+    assertEquals(
+        nonExistingZip,
+        R8Command.builder().setOutputPath(nonExistingZip).build().getOutputPath());
+    assertEquals(
+        existingDir,
+        parse("--output", existingDir.toString()).getOutputPath());
+    assertEquals(
+        nonExistingZip,
+        parse("--output", nonExistingZip.toString()).getOutputPath());
+  }
+
+  @Test
+  public void nonExistingOutputDir() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+    R8Command.builder().setOutputPath(nonExistingDir).build();
+  }
+
+  @Test
+  public void existingOutputZip() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+    R8Command.builder().setOutputPath(existingZip).build();
+  }
+
+  @Test
+  public void invalidOutputFileType() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+    R8Command.builder().setOutputPath(invalidType).build();
+  }
+
+  @Test
+  public void nonExistingOutputDirParse() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path nonExistingDir = temp.getRoot().toPath().resolve("a/path/that/does/not/exist");
+    parse("--output", nonExistingDir.toString());
+  }
+
+  @Test
+  public void existingOutputZipParse() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path existingZip = temp.newFile("an-existing-archive.zip").toPath();
+    parse("--output", existingZip.toString());
+  }
+
+  @Test
+  public void invalidOutputFileTypeParse() throws Throwable {
+    thrown.expect(CompilationException.class);
+    Path invalidType = temp.getRoot().toPath().resolve("an-invalid-output-file-type.foobar");
+    parse("--output", invalidType.toString());
+  }
+
+  @Test
+  public void argumentsInFile() throws Throwable {
+    Path inputFile = temp.newFile("foobar.dex").toPath();
+    Path pgConfFile = temp.newFile("pgconf.config").toPath();
+    Path argsFile = temp.newFile("more-args.txt").toPath();
+    FileUtils.writeTextFile(argsFile, ImmutableList.of(
+        "--debug --no-minification",
+        "--pg-conf " + pgConfFile,
+        inputFile.toString()
+    ));
+    R8Command command = parse("@" + argsFile.toString());
+    assertEquals(CompilationMode.DEBUG, command.getMode());
+    assertFalse(command.useMinification());
+    assertTrue(command.useTreeShaking());
+    assertEquals(1, ToolHelper.getApp(command).getDexProgramResources().size());
+  }
+
+  private R8Command parse(String... args)
+      throws CompilationException, ProguardRuleParserException, IOException {
+    return R8Command.parse(args).build();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/R8InliningTest.java b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
new file mode 100644
index 0000000..50db347
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class R8InliningTest {
+
+  private static final String JAR_EXTENSION = ".jar";
+  private static final String DEFAULT_DEX_FILENAME = "classes.dex";
+  private static final String DEFAULT_MAP_FILENAME = "proguard.map";
+
+  @Parameters(name = "{0}")
+  public static Collection<Object[]> data() {
+    return Arrays.asList(new Object[][]{{"Inlining"}});
+  }
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+  private final String name;
+  private final String keepRulesFile;
+
+  public R8InliningTest(String name) {
+    this.name = name.toLowerCase();
+    this.keepRulesFile = ToolHelper.EXAMPLES_DIR + this.name + "/keep-rules.txt";
+  }
+
+  private Path getInputFile() {
+    return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, name + JAR_EXTENSION);
+  }
+
+  private Path getOriginalDexFile() {
+    return Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, name, DEFAULT_DEX_FILENAME);
+  }
+
+  private Path getGeneratedDexFile() throws IOException {
+    return Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_DEX_FILENAME);
+  }
+
+  private String getGeneratedProguardMap() throws IOException {
+    Path mapFile = Paths.get(temp.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME);
+    if (Files.exists(mapFile)) {
+      return mapFile.toAbsolutePath().toString();
+    }
+    return null;
+  }
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Before
+  public void generateR8Version()
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+    Path out = temp.getRoot().toPath();
+    R8Command command =
+        R8Command.builder()
+            .addProgramFiles(getInputFile())
+            .setOutputPath(out)
+            .addProguardConfigurationFiles(Paths.get(keepRulesFile))
+            .build();
+    ToolHelper.runR8(command);
+    ToolHelper.runArtNoVerificationErrors(out + "/classes.dex", "inlining.Inlining");
+  }
+
+  private void checkAbsent(ClassSubject clazz, String name) {
+    assertTrue(clazz.isPresent());
+    MethodSubject method = clazz.method("boolean", name, Collections.emptyList());
+    assertFalse(method.isPresent());
+  }
+
+  private void dump(DexEncodedMethod method) {
+    System.out.println(method);
+    System.out.println(method.codeToString());
+  }
+
+  private void dump(Path path, String title) throws Throwable {
+    System.out.println(title + ":");
+    DexInspector inspector = new DexInspector(path.toAbsolutePath());
+    inspector.clazz("inlining.Inlining").forAllMethods(m -> dump(m.getMethod()));
+    System.out.println(title + " size: " + Files.size(path));
+  }
+
+  @Test
+  public void checkNoInvokes() throws Throwable {
+    DexInspector inspector = new DexInspector(getGeneratedDexFile().toAbsolutePath(),
+        getGeneratedProguardMap());
+    ClassSubject clazz = inspector.clazz("inlining.Inlining");
+    // Simple constant inlining.
+    checkAbsent(clazz, "longExpression");
+    checkAbsent(clazz, "intExpression");
+    checkAbsent(clazz, "doubleExpression");
+    checkAbsent(clazz, "floatExpression");
+    // Simple return argument inlining.
+    checkAbsent(clazz, "longArgumentExpression");
+    checkAbsent(clazz, "intArgumentExpression");
+    checkAbsent(clazz, "doubleArgumentExpression");
+    checkAbsent(clazz, "floatArgumentExpression");
+  }
+
+  @Test
+  public void processedFileIsSmaller() throws Throwable {
+    long original = Files.size(getOriginalDexFile());
+    long generated = Files.size(getGeneratedDexFile());
+    final boolean ALWAYS_DUMP = false;  // Used for debugging.
+    if (ALWAYS_DUMP || generated > original) {
+      dump(getOriginalDexFile(), "Original");
+      dump(getGeneratedDexFile(), "Generated");
+    }
+    assertTrue("Inlining failed to reduce size", original > generated);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
new file mode 100644
index 0000000..fbc5e16
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.List;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.TokenSource;
+import org.antlr.runtime.tree.CommonTree;
+import org.antlr.runtime.tree.CommonTreeNodeStream;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.writer.builder.DexBuilder;
+import org.jf.dexlib2.writer.io.MemoryDataStore;
+import org.jf.smali.LexerErrorInterface;
+import org.jf.smali.smaliFlexLexer;
+import org.jf.smali.smaliParser;
+import org.jf.smali.smaliTreeWalker;
+
+// Adapted from org.jf.smali.SmaliTestUtils.
+public class Smali {
+
+  public static byte[] compile(String smaliText) throws RecognitionException, IOException {
+    return compile(smaliText, 15);
+  }
+
+  public static byte[] compile(String... smaliText) throws RecognitionException, IOException {
+    return compile(Arrays.asList(smaliText), 15);
+  }
+
+  public static byte[] compile(String smaliText, int apiLevel)
+      throws IOException, RecognitionException {
+    return compile(ImmutableList.of(smaliText), apiLevel);
+  }
+
+  public static byte[] compile(List<String> smaliTexts)
+      throws RecognitionException, IOException {
+    return compile(smaliTexts, 15);
+  }
+
+  public static byte[] compile(List<String> smaliTexts, int apiLevel)
+      throws RecognitionException, IOException {
+    DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(apiLevel));
+
+    for (String smaliText : smaliTexts) {
+      Reader reader = new StringReader(smaliText);
+
+      LexerErrorInterface lexer = new smaliFlexLexer(reader);
+      CommonTokenStream tokens = new CommonTokenStream((TokenSource) lexer);
+
+      smaliParser parser = new smaliParser(tokens);
+      parser.setVerboseErrors(true);
+      parser.setAllowOdex(false);
+      parser.setApiLevel(apiLevel);
+
+      smaliParser.smali_file_return result = parser.smali_file();
+
+      if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
+        throw new RuntimeException(
+            "Error occured while compiling text:\n" + StringUtils.join(smaliTexts, "\n"));
+      }
+
+      CommonTree t = result.getTree();
+
+      CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
+      treeStream.setTokenStream(tokens);
+
+      smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
+      dexGen.setApiLevel(apiLevel);
+      dexGen.setVerboseErrors(true);
+      dexGen.setDexBuilder(dexBuilder);
+      dexGen.smali_file();
+
+      if (dexGen.getNumberOfSyntaxErrors() > 0) {
+        throw new RuntimeException("Error occured while compiling text");
+      }
+    }
+
+    MemoryDataStore dataStore = new MemoryDataStore();
+
+    dexBuilder.writeTo(dataStore);
+
+    // TODO(sgjesse): This returns the full backingstore from MemoryDataStore, which by default
+    // is 1024k bytes. Our dex file reader does not complain though.
+    return dataStore.getData();
+  }
+}
diff --git a/src/test/proguard/invalid/including-1.flags b/src/test/proguard/invalid/including-1.flags
new file mode 100644
index 0000000..c61ff34
--- /dev/null
+++ b/src/test/proguard/invalid/including-1.flags
@@ -0,0 +1,6 @@
+# Some empty lines to move error away from first lines
+
+
+
+
+-include does-not-exist.flags
\ No newline at end of file
diff --git a/src/test/proguard/invalid/including-2.flags b/src/test/proguard/invalid/including-2.flags
new file mode 100644
index 0000000..e1018ee
--- /dev/null
+++ b/src/test/proguard/invalid/including-2.flags
@@ -0,0 +1,6 @@
+# Some empty lines to move error away from first lines
+
+
+
+
+@does-not-exist.flags
\ No newline at end of file
diff --git a/src/test/proguard/valid/access-flags.flags b/src/test/proguard/valid/access-flags.flags
new file mode 100644
index 0000000..8096b59
--- /dev/null
+++ b/src/test/proguard/valid/access-flags.flags
@@ -0,0 +1,4 @@
+-keep !public abstract !final @com.company.Something class * {
+  public !static <fields>;
+  !public !protected ! volatile <methods>;
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
new file mode 100644
index 0000000..5f9485e
--- /dev/null
+++ b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-assumenosideeffects class com.example.FeatureFlags {
+    public static boolean returnsTrue() return true;
+    public static boolean returnsFalse() return false;
+    public static int returns1() return 1;
+    public static int returns2To4() return 2..4;
+    public static int returnsField() return com.google.C.X;
+}
diff --git a/src/test/proguard/valid/assume-no-side-effects.flags b/src/test/proguard/valid/assume-no-side-effects.flags
new file mode 100644
index 0000000..9377b0b
--- /dev/null
+++ b/src/test/proguard/valid/assume-no-side-effects.flags
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-assumenosideeffects class android.util.Log {
+    public static boolean isLoggable(java.lang.String, int);
+    public static int v(...);
+    public static int i(...);
+    public static int d(...);
+}
+
diff --git a/src/test/proguard/valid/assume-values-with-return-value.flags b/src/test/proguard/valid/assume-values-with-return-value.flags
new file mode 100644
index 0000000..6877f10
--- /dev/null
+++ b/src/test/proguard/valid/assume-values-with-return-value.flags
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-assumevalues class com.example.SomeClass {
+    public static final boolean isTrue() return true;
+    public static final boolean isFalse() return false;
+    public static final int is1() return 1;
+    public static final int is2To4() return 2..4;
+    public static final int isField() return com.google.C.X;
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/dontobfuscate.flags b/src/test/proguard/valid/dontobfuscate.flags
new file mode 100644
index 0000000..0674e77
--- /dev/null
+++ b/src/test/proguard/valid/dontobfuscate.flags
@@ -0,0 +1 @@
+-dontobfuscate
\ No newline at end of file
diff --git a/src/test/proguard/valid/dontoptimize.flags b/src/test/proguard/valid/dontoptimize.flags
new file mode 100644
index 0000000..50ed61c
--- /dev/null
+++ b/src/test/proguard/valid/dontoptimize.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontoptimize
diff --git a/src/test/proguard/valid/dontskipnonpubliclibraryclasses.flags b/src/test/proguard/valid/dontskipnonpubliclibraryclasses.flags
new file mode 100644
index 0000000..3e76e60
--- /dev/null
+++ b/src/test/proguard/valid/dontskipnonpubliclibraryclasses.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-dontskipnonpubliclibraryclasses
diff --git a/src/test/proguard/valid/empty.flags b/src/test/proguard/valid/empty.flags
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/proguard/valid/empty.flags
diff --git a/src/test/proguard/valid/ignorewarnings.flags b/src/test/proguard/valid/ignorewarnings.flags
new file mode 100644
index 0000000..7079fd3
--- /dev/null
+++ b/src/test/proguard/valid/ignorewarnings.flags
@@ -0,0 +1 @@
+-ignorewarnings
\ No newline at end of file
diff --git a/src/test/proguard/valid/including.flags b/src/test/proguard/valid/including.flags
new file mode 100644
index 0000000..a2e3e71
--- /dev/null
+++ b/src/test/proguard/valid/including.flags
@@ -0,0 +1,10 @@
+-include empty.flags
+@empty.flags
+-include sub/sub-empty.flags
+@sub/sub-empty.flags
+-basedirectory sub
+-include sub-including.flags
+@sub-including.flags
+-basedirectory ..
+-include sub/sub-including.flags
+@sub/sub-including.flags
diff --git a/src/test/proguard/valid/library-jars.flags b/src/test/proguard/valid/library-jars.flags
new file mode 100644
index 0000000..9cb77d4
--- /dev/null
+++ b/src/test/proguard/valid/library-jars.flags
@@ -0,0 +1,2 @@
+-libraryjars some/library.jar
+-libraryjars some/library.jar:some/other/library.jar:yet/another/library.jar
\ No newline at end of file
diff --git a/src/test/proguard/valid/multiple-name-patterns.flags b/src/test/proguard/valid/multiple-name-patterns.flags
new file mode 100644
index 0000000..3d996a5
--- /dev/null
+++ b/src/test/proguard/valid/multiple-name-patterns.flags
@@ -0,0 +1,6 @@
+-keep class
+    com.company.hello.**,
+    com.company.world.**
+    extends some.library.Class {
+  protected java.lang.Object[][] getContents();
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/parse-and-skip-single-argument.flags b/src/test/proguard/valid/parse-and-skip-single-argument.flags
new file mode 100644
index 0000000..ef5ba4c
--- /dev/null
+++ b/src/test/proguard/valid/parse-and-skip-single-argument.flags
@@ -0,0 +1 @@
+-renamesourcefileattribute RenamedAttribute
\ No newline at end of file
diff --git a/src/test/proguard/valid/proguard.flags b/src/test/proguard/valid/proguard.flags
new file mode 100644
index 0000000..99692d3
--- /dev/null
+++ b/src/test/proguard/valid/proguard.flags
@@ -0,0 +1,95 @@
+# This is a comment.
+
+   # I bet there will be many of those.
+
+-keep @Annotation class hello.Hello {
+  public *;
+}
+
+-keep @Annotation class hello.Hello {
+  public <methods>;
+}
+
+-keep @Annotation class hello.Hello {
+  public <fields>;
+}
+
+-keep @Annotation class hello.Hello {
+  public <init>();
+}
+
+-keep  @Annotation class hello.Hello {
+  public <init>(hello.***.B, hello.**.A, hello.?);
+}
+
+-keep @Annotation class hello.Hello{  # Missing space before '{' on purpose.
+  public <init>(hello.A);
+}
+
+-keep class hello.Hello {
+  public static static void main(java.lang.String[]);
+}
+
+-keep class hello.Hello {
+  public static static % method(...);
+}
+
+-keep class hello.Hello {
+  public static boolean theFlag;
+}
+
+-keep class hello.Hello {
+  public Hello(...);
+}
+
+-keepnames class hello.Hello extends Hello.Base {
+  public *;
+}
+
+-keepclassmembers class hello.Hello implements @Hep Hello.Base {
+  public *;
+}
+
+-keepclasseswithmembers class hello.Hello {
+  public *;
+}
+
+-keepclassmembernames class hello.Hello {
+  public *;
+}
+
+-keepclasseswithmembernames class hello.Hello {
+  public *;
+}
+
+-keepclassmembers,includedescriptorclasses @com.company.Annotation* public class **JNI {
+  public static *** methodPrefix_*(...);
+}
+
+-keepclassmembernames,includedescriptorclasses @com.company.Annotation* public class * {
+  public <methods>;
+  protected <methods>;
+}
+
+-keepclasseswithmembernames public class **JNI {
+  public final static native <methods>;
+}
+
+-keep class com.company.some.package.MyClass
+
+-keep @interface com.company.SomeAnnotation
+
+-keep @com.company.SomeAnnotation class *
+
+-keepclasseswithmembers class * {
+  @com.company.SomeAnnotation <fields>;
+}
+
+-keepclasseswithmembers class * {
+  @com.company.SomeAnnotation <methods>;
+}
+
+-keep public @com.company.Something class * {
+  public <fields>;
+  public <methods>;
+}
diff --git a/src/test/proguard/valid/seeds-2.flags b/src/test/proguard/valid/seeds-2.flags
new file mode 100644
index 0000000..18ce6b5
--- /dev/null
+++ b/src/test/proguard/valid/seeds-2.flags
@@ -0,0 +1 @@
+-printseeds path/to/seedfile.txt
\ No newline at end of file
diff --git a/src/test/proguard/valid/seeds.flags b/src/test/proguard/valid/seeds.flags
new file mode 100644
index 0000000..c4d1ca2
--- /dev/null
+++ b/src/test/proguard/valid/seeds.flags
@@ -0,0 +1 @@
+-printseeds
\ No newline at end of file
diff --git a/src/test/proguard/valid/skipnonpubliclibraryclasses.flags b/src/test/proguard/valid/skipnonpubliclibraryclasses.flags
new file mode 100644
index 0000000..e6e9f8b
--- /dev/null
+++ b/src/test/proguard/valid/skipnonpubliclibraryclasses.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-skipnonpubliclibraryclasses
diff --git a/src/test/proguard/valid/sub/sub-empty.flags b/src/test/proguard/valid/sub/sub-empty.flags
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/proguard/valid/sub/sub-empty.flags
diff --git a/src/test/proguard/valid/sub/sub-including.flags b/src/test/proguard/valid/sub/sub-including.flags
new file mode 100644
index 0000000..5de6ef5
--- /dev/null
+++ b/src/test/proguard/valid/sub/sub-including.flags
@@ -0,0 +1,14 @@
+@sub-empty.flags
+-include sub-empty.flags
+@../empty.flags
+-include ../empty.flags
+-basedirectory ..
+@empty.flags
+-include empty.flags
+@sub/sub-empty.flags
+-include sub/sub-empty.flags
+-basedirectory sub
+@sub-empty.flags
+-include sub-empty.flags
+@../empty.flags
+-include ../empty.flags
diff --git a/src/test/proguard/valid/verbose.flags b/src/test/proguard/valid/verbose.flags
new file mode 100644
index 0000000..4254a8d
--- /dev/null
+++ b/src/test/proguard/valid/verbose.flags
@@ -0,0 +1,5 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+-verbose
diff --git a/src/test/proguard/valid/why-are-you-keeping.flags b/src/test/proguard/valid/why-are-you-keeping.flags
new file mode 100644
index 0000000..e57da11
--- /dev/null
+++ b/src/test/proguard/valid/why-are-you-keeping.flags
@@ -0,0 +1 @@
+-whyareyoukeeping class * extends foo.bar { *; }
\ No newline at end of file
diff --git a/src/test/smali/arithmetic/Arithmetic.smali b/src/test/smali/arithmetic/Arithmetic.smali
new file mode 100644
index 0000000..f76b9b3
--- /dev/null
+++ b/src/test/smali/arithmetic/Arithmetic.smali
@@ -0,0 +1,254 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static addMinusOne(I)I
+    .locals 1
+
+    const/4 v0, -1
+    add-int v0, p0, v0
+    return v0
+.end method
+
+.method static addTwoConstants()I
+    .locals 2
+
+    const/4 v0, 1
+    const/4 v1, 2
+    add-int v0, v0, v1
+    return v0
+.end method
+
+.method static addSameTwoConstants()I
+    .locals 2
+
+    const/4 v0, 1
+    add-int v0, v0, v0
+    return v0
+.end method
+
+.method static subMinusOne(I)I
+    .locals 1
+
+    const/4 v0, -1
+    sub-int v0, p0, v0
+    return v0
+.end method
+
+.method static subSameTwoConstants()I
+    .locals 2
+
+    const/4 v0, 1
+    sub-int v0, v0, v0
+    return v0
+.end method
+
+.method static subtractConstants()I
+    .locals 9
+
+    const/4 v0, 0
+
+    const v1, 127  # Max 8-bit signed integer.
+    const v2, -128  # Min 8-bit signed integer.
+    const v3, 128  # Max 8-bit signed integer plus one.
+    const v4, -129  # Min 8-bit signed integer minus one.
+
+    const v5, 32767  # Max 16-bit signed integer.
+    const v6, -32768  # Min 16-bit signed integer.
+    const v7, 32768  # Max 16-bit signed integer plus one.
+    const v8, -32769  # Min 16-bit signed integer minus one.
+
+    sub-int v0, v0, v1
+    sub-int v0, v0, v2
+    sub-int v0, v0, v3
+    sub-int v0, v0, v4
+    sub-int v0, v0, v5
+    sub-int v0, v0, v6
+    sub-int v0, v0, v7
+    sub-int v0, v0, v8
+    sub-int v0, v1, v0
+    sub-int v0, v2, v0
+    sub-int v0, v3, v0
+    sub-int v0, v4, v0
+    sub-int v0, v5, v0
+    sub-int v0, v6, v0
+    sub-int v0, v7, v0
+    sub-int v0, v8, v0
+
+    return v0
+.end method
+
+.method static sixteenIntArgMethod(IIIIIIIIIIIIIIII)V
+    .locals 0
+    return-void
+.end method
+
+# Same code as subtractConstants, but try to force the register allocator to allocate registers for
+# the arithmetic operations above 15.
+.method static subtractConstants8bitRegisters()I
+    .locals 32
+
+    const/4 v0, 0
+
+    const v1, 127  # Max 8-bit signed integer.
+    const v2, -128  # Min 8-bit signed integer.
+    const v3, 128  # Max 8-bit signed integer plus one.
+    const v4, -129  # Min 8-bit signed integer minus one.
+
+    const v5, 32767  # Max 16-bit signed integer.
+    const v6, -32768  # Min 16-bit signed integer.
+    const v7, 32768  # Max 16-bit signed integer plus one.
+    const v8, -32769  # Min 16-bit signed integer minus one.
+
+    const v9, 9
+    const v10, 10
+    const v11, 11
+    const v12, 12
+    const v13, 13
+    const v14, 14
+    const v15, 15
+
+    sub-int v16, v0, v1
+    sub-int v17, v16, v2
+    sub-int v18, v17, v3
+    sub-int v19, v18, v4
+    sub-int v20, v19, v5
+    sub-int v21, v20, v6
+    sub-int v22, v21, v7
+    sub-int v23, v22, v8
+    sub-int v24, v1, v23
+    sub-int v25, v2, v24
+    sub-int v26, v3, v25
+    sub-int v27, v4, v26
+    sub-int v28, v5, v27
+    sub-int v29, v6, v28
+    sub-int v30, v7, v29
+    sub-int v31, v8, v30
+
+    invoke-static/range {v16 .. v31}, LTest;->sixteenIntArgMethod(IIIIIIIIIIIIIIII)V
+
+    return v31
+.end method
+
+.method static addConstantUsedTwice()I
+    .locals 4
+
+    const/4 v0, 0
+    const/4 v1, 1
+    add-int/2addr v0, v1
+    add-int/2addr v0, v1
+    return v0
+.end method
+
+.method static addTwoLongConstants()J
+    .locals 4
+
+    const-wide v0, 1
+    const-wide v2, 2
+    add-long v0, v0, v2
+    return-wide v0
+.end method
+
+.method static addTwoDoubleConstants()D
+    .locals 4
+
+    const-wide v0, 0x3ff0000000000000L  # 1.0
+    const-wide v2, 0x4000000000000000L  # 2.0
+    add-double v0, v0, v2
+    return-wide v0
+.end method
+
+.method static cmpFold()I
+    .locals 4
+
+    const-wide v0, 0
+    const-wide v2, 0
+    cmp-long v0, v0, v2
+    return v0
+.end method
+
+.method static addFoldLeft(I)I
+    .locals 2
+
+    const/4 v0, 1
+    const/4 v1, 2
+    add-int/2addr v0, v1
+    add-int/2addr v0, p0
+    return v0
+.end method
+
+.method static subFoldLeft(I)I
+    .locals 2
+
+    const/4 v0, 1
+    const/4 v1, 2
+    sub-int/2addr v0, v1
+    sub-int/2addr v0, p0
+    return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 3
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    # Calculate: 0 + (-1).
+    const/4 v1, 0
+    invoke-static {v1}, LTest;->addMinusOne(I)I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->addTwoConstants()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->addSameTwoConstants()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->addTwoLongConstants()J
+    move-result-wide v1
+    invoke-virtual {v0, v1, v2}, Ljava/io/PrintStream;->println(J)V
+
+    invoke-static {}, LTest;->addTwoDoubleConstants()D
+    move-result-wide v1
+    invoke-virtual {v0, v1, v2}, Ljava/io/PrintStream;->println(D)V
+
+    # Calculate: 0 - (-1).
+    const/4 v1, 0
+    invoke-static {v1}, LTest;->subMinusOne(I)I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->subSameTwoConstants()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->subtractConstants()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->subtractConstants8bitRegisters()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->addConstantUsedTwice()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    const/4 v1, 1
+    invoke-static {v1}, LTest;->addFoldLeft(I)I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    const/4 v1, 1
+    invoke-static {v1}, LTest;->subFoldLeft(I)I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    return-void
+.end method
diff --git a/src/test/smali/bad-codegen/Test.java b/src/test/smali/bad-codegen/Test.java
new file mode 100644
index 0000000..f301ed7
--- /dev/null
+++ b/src/test/smali/bad-codegen/Test.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import java.util.Map;
+
+public class Test {
+  public Test a;
+  public static Test P;
+
+  public Test() {
+  }
+
+  public Test(int a, Test b, Test c, Long d, Test e, Map f) {
+  }
+
+  public Test(int i1, int i2, int i3) {
+  }
+
+  public long b() {
+    return 0;
+  }
+
+  public boolean c() {
+    return false;
+  }
+
+  public boolean d() {
+    return false;
+  }
+
+  public static void main(String[] args) {
+    Test test = new Test();
+    try {
+      new TestObject().a(test, test, test, test, true);
+    } catch (Throwable t) {
+      System.out.println(t);
+    }
+  }
+}
diff --git a/src/test/smali/bad-codegen/TestObject.java b/src/test/smali/bad-codegen/TestObject.java
new file mode 100644
index 0000000..dc96fca
--- /dev/null
+++ b/src/test/smali/bad-codegen/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public Test a(Test a, Test b, Test c, Test d, boolean e) {
+    return new Test();
+  }
+}
diff --git a/src/test/smali/bad-codegen/TestObject.smali b/src/test/smali/bad-codegen/TestObject.smali
new file mode 100644
index 0000000..b0a1a58
--- /dev/null
+++ b/src/test/smali/bad-codegen/TestObject.smali
@@ -0,0 +1,1488 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field public a:LTest;
+.field public b:Ljava/util/List;
+
+.method public final a(LTest;LTest;LTest;LTest;Z)LTest;
+    .registers 34
+    move-object/from16 v0, p0
+    iget-object v12, v0, LTestObject;->a:LTest;
+    iget-object v4, v12, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->b()J
+    move-result-wide v14
+    const/4 v10, 0x0
+    const/4 v4, 0x0
+    if-eqz p2, :cond_9cf
+    move-object/from16 v0, p2
+    iget-object v10, v0, LTest;->a:LTest;
+    move-object/from16 v0, p2
+    iget-object v4, v0, LTest;->b:Ljava/util/List;
+    move-object v13, v4
+    :goto_17
+    if-eqz v10, :cond_1f
+    invoke-virtual {v10}, LTest;->d()Z
+    move-result v4
+    if-nez v4, :cond_21d
+    :cond_1f
+    new-instance v4, LTest;
+    const/4 v5, 0x0
+    const/4 v6, 0x0
+    sget-object v7, LTest;->P:LTest;
+    const-wide/16 v8, 0x0
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v14, v4
+    :goto_2c
+    if-nez p4, :cond_44
+    iget-object v4, v14, LTest;->a:LTest;
+    sget-object v5, LTest;->P:LTest;
+    if-ne v4, v5, :cond_44
+    iget-object v4, v14, LTest;->a:LTest;
+    new-instance p4, LTest;
+    iget v5, v4, LTest;->c:I
+    iget v4, v4, LTest;->d:I
+    const v6, 0x2faf080
+    move-object/from16 v0, p4
+    invoke-direct {v0, v5, v4, v6}, LTest;-><init>(III)V
+    :cond_44
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    invoke-virtual {v4}, LTest;->a()Z
+    move-result v5
+    if-eqz v5, :cond_6c5
+    iget-object v8, v4, LTest;->a:LTest;
+    if-nez p1, :cond_61e
+    const/4 v4, 0x0
+    :cond_53
+    :goto_53
+    if-eqz v4, :cond_6c8
+    move-object v12, v4
+    :goto_56
+    iget-object v4, v12, LTest;->a:LTest;
+    sget-object v5, LTest;->P:LTest;
+    if-ne v4, v5, :cond_931
+    const/4 v4, 0x1
+    :goto_5d
+    iget-object v5, v14, LTest;->a:LTest;
+    sget-object v6, LTest;->P:LTest;
+    if-ne v5, v6, :cond_934
+    const/4 v5, 0x1
+    :goto_64
+    if-nez v4, :cond_937
+    if-nez v5, :cond_937
+    const/4 v4, 0x0
+    move-object v13, v4
+    :goto_6a
+    sget-object v4, LTest;->P:LTest;
+    invoke-static {v4}, LTest;->a(LTest;)Z
+    move-result v4
+    if-eqz v4, :cond_212
+    const/4 v5, 0x0
+    const/4 v4, 0x0
+    if-eqz v13, :cond_9c8
+    if-ne v13, v14, :cond_976
+    const/4 v5, 0x1
+    move v11, v4
+    move v15, v5
+    :goto_7b
+    if-eqz v12, :cond_97b
+    iget-object v4, v12, LTest;->a:LTest;
+    sget-object v5, LTest;->P:LTest;
+    if-ne v4, v5, :cond_97b
+    const/4 v4, 0x1
+    :goto_84
+    if-eqz v4, :cond_9c
+    const/4 v7, 0x0
+    iget v4, v12, LTest;->a:I
+    packed-switch v4, :pswitch_data_9d2
+    :goto_8c
+    :pswitch_8c
+    if-eqz v7, :cond_9c
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    const-string v5, ""
+    const-string v6, ""
+    const-wide/16 v8, 0x1
+    const/4 v10, 0x1
+    invoke-interface/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+    :cond_9c
+    iget-object v4, v14, LTest;->a:LTest;
+    sget-object v5, LTest;->P:LTest;
+    if-ne v4, v5, :cond_98e
+    const/4 v4, 0x1
+    :goto_a3
+    if-eqz v4, :cond_bb
+    const/4 v7, 0x0
+    iget v4, v14, LTest;->a:I
+    packed-switch v4, :pswitch_data_9e0
+    :goto_ab
+    if-eqz v7, :cond_bb
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    const-string v5, ""
+    const-string v6, ""
+    const-wide/16 v8, 0x1
+    const/4 v10, 0x1
+    invoke-interface/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+    :cond_bb
+    if-eqz v15, :cond_999
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    const-string v5, ""
+    const-string v6, ""
+    const-string v7, ""
+    const-wide/16 v8, 0x1
+    const/4 v10, 0x1
+    invoke-interface/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+    :goto_cd
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    if-eqz v4, :cond_119
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v4}, LTest;->a()Z
+    move-result v7
+    if-eqz v7, :cond_f6
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v7, v4, LTest;->a:LTest;
+    if-eqz v7, :cond_f6
+    iget-object v7, v4, LTest;->a:LTest;
+    iget-object v7, v7, LTest;->a:LTest;
+    if-eqz v7, :cond_f6
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    invoke-virtual {v4, v5, v6}, LTest;->a(LTest;Ljava/lang/String;)V
+    :cond_f6
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v4}, LTest;->b()Z
+    move-result v7
+    if-eqz v7, :cond_119
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v7, v4, LTest;->a:LTest;
+    if-eqz v7, :cond_119
+    iget-object v7, v4, LTest;->a:LTest;
+    iget-object v7, v7, LTest;->a:LTest;
+    if-eqz v7, :cond_119
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    invoke-virtual {v4, v5, v6}, LTest;->a(LTest;Ljava/lang/String;)V
+    :cond_119
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    if-eqz v4, :cond_212
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v4}, LTest;->a()Z
+    move-result v7
+    if-eqz v7, :cond_212
+    iget-object v4, v4, LTest;->a:LTest;
+    invoke-static {v5}, LTest;->a(Ljava/lang/Object;)Ljava/lang/Object;
+    invoke-static {v6}, LTest;->a(Ljava/lang/Object;)Ljava/lang/Object;
+    iget-object v7, v4, LTest;->a:LTest;
+    iget-object v15, v7, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v7, v4, LTest;->a:LTest;
+    if-eqz v7, :cond_9bf
+    iget-object v4, v4, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->b()J
+    move-result-wide v8
+    move-wide/from16 v16, v8
+    :goto_149
+    iget-wide v8, v15, LTest;->l:J
+    const-wide/16 v10, -0x1
+    cmp-long v4, v8, v10
+    if-eqz v4, :cond_191
+    iget-wide v8, v15, LTest;->l:J
+    cmp-long v4, v16, v8
+    if-lez v4, :cond_191
+    iget-wide v8, v15, LTest;->l:J
+    sub-long v8, v16, v8
+    long-to-float v4, v8
+    const v7, 0x4ca4cb80    # 8.64E7f
+    div-float v10, v4, v7
+    const-string v7, ""
+    iget v4, v15, LTest;->g:I
+    int-to-long v8, v4
+    invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+    const-string v7, ""
+    iget v4, v15, LTest;->c:I
+    int-to-long v8, v4
+    invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+    const-string v7, ""
+    iget v4, v15, LTest;->b:I
+    int-to-long v8, v4
+    invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+    const-string v7, ""
+    iget v4, v15, LTest;->a:I
+    int-to-long v8, v4
+    invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+    const-string v7, ""
+    iget v4, v15, LTest;->d:I
+    int-to-long v8, v4
+    invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+    const-string v7, ""
+    iget v4, v15, LTest;->h:I
+    int-to-long v8, v4
+    invoke-static/range {v5 .. v10}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JF)V
+    :cond_191
+    const-string v7, ""
+    iget v4, v15, LTest;->h:I
+    int-to-long v8, v4
+    iget v10, v15, LTest;->g:I
+    const/4 v11, 0x0
+    invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+    move-result v4
+    iput v4, v15, LTest;->h:I
+    const-string v7, ""
+    iget-wide v8, v15, LTest;->k:J
+    iget v10, v15, LTest;->g:I
+    const/4 v11, 0x0
+    invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+    move-result v4
+    int-to-long v8, v4
+    iput-wide v8, v15, LTest;->k:J
+    const-string v7, ""
+    iget v4, v15, LTest;->i:I
+    int-to-long v8, v4
+    iget v10, v15, LTest;->j:I
+    const/4 v11, 0x0
+    invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+    move-result v4
+    iput v4, v15, LTest;->i:I
+    const/4 v4, 0x0
+    iput v4, v15, LTest;->j:I
+    const-string v7, ""
+    iget v4, v15, LTest;->e:I
+    int-to-long v8, v4
+    iget v10, v15, LTest;->a:I
+    const/4 v11, 0x0
+    invoke-static/range {v5 .. v11}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;JII)I
+    move-result v4
+    iput v4, v15, LTest;->e:I
+    const-string v4, ""
+    iget v7, v15, LTest;->a:I
+    const/4 v8, 0x0
+    invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+    move-result v4
+    iput v4, v15, LTest;->a:I
+    const-string v4, ""
+    iget v7, v15, LTest;->b:I
+    const/4 v8, 0x0
+    invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+    move-result v4
+    iput v4, v15, LTest;->b:I
+    const-string v4, ""
+    iget v7, v15, LTest;->c:I
+    const/4 v8, 0x0
+    invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+    move-result v4
+    iput v4, v15, LTest;->c:I
+    const-string v4, ""
+    iget v7, v15, LTest;->d:I
+    const/4 v8, 0x0
+    invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+    move-result v4
+    iput v4, v15, LTest;->d:I
+    const-string v4, ""
+    iget v7, v15, LTest;->f:I
+    const/4 v8, 0x0
+    invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+    move-result v4
+    iput v4, v15, LTest;->f:I
+    const-string v4, ""
+    iget v7, v15, LTest;->g:I
+    const/4 v8, 0x0
+    invoke-static {v5, v6, v4, v7, v8}, LTest;->a(LTest;Ljava/lang/String;Ljava/lang/String;II)I
+    move-result v4
+    iput v4, v15, LTest;->g:I
+    move-wide/from16 v0, v16
+    iput-wide v0, v15, LTest;->l:J
+    :cond_212
+    if-eqz p5, :cond_9c5
+    if-ne v13, v14, :cond_9c5
+    const/4 v4, 0x1
+    :goto_217
+    new-instance v5, LTest;
+    invoke-direct {v5, v13, v12, v14, v4}, LTest;-><init>(LTest;LTest;LTest;Z)V
+    return-object v5
+    :cond_21d
+    invoke-virtual {v12}, LTest;->a()Z
+    move-result v4
+    if-eqz v4, :cond_57c
+    iget-object v8, v12, LTest;->a:LTest;
+    iget-wide v4, v8, LTest;->b:J
+    const-wide/16 v6, 0x1
+    add-long/2addr v4, v6
+    iput-wide v4, v8, LTest;->b:J
+    if-eqz v10, :cond_578
+    iget v4, v10, LTest;->k:I
+    const/4 v5, 0x3
+    if-ne v4, v5, :cond_29b
+    const/4 v4, 0x1
+    :goto_234
+    if-eqz v4, :cond_578
+    iget-object v9, v8, LTest;->a:LTest;
+    invoke-static {v10}, LTest;->a(LTest;)J
+    move-result-wide v16
+    const-wide/16 v4, 0x0
+    cmp-long v4, v16, v4
+    if-gez v4, :cond_2ca
+    const/4 v4, 0x0
+    :cond_243
+    :goto_243
+    if-nez v4, :cond_543
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget v5, v4, LTest;->d:I
+    add-int/lit8 v5, v5, 0x1
+    iput v5, v4, LTest;->d:I
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-boolean v4, v5, LTest;->b:Z
+    if-nez v4, :cond_53c
+    iget-object v4, v5, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->f:Ljava/lang/String;
+    if-eqz v4, :cond_539
+    const/4 v4, 0x1
+    :goto_260
+    if-eqz v4, :cond_53c
+    iget-object v4, v5, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->b()J
+    move-result-wide v6
+    iget-wide v8, v5, LTest;->c:J
+    sub-long/2addr v6, v8
+    iget v4, v5, LTest;->j:I
+    int-to-long v4, v4
+    cmp-long v4, v6, v4
+    if-lez v4, :cond_53c
+    const/4 v4, 0x1
+    :goto_273
+    if-eqz v4, :cond_53f
+    new-instance v4, LTest;
+    const/4 v5, 0x0
+    const/4 v6, 0x0
+    sget-object v7, LTest;->P:LTest;
+    const-wide/16 v8, 0x0
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v6, v4
+    :goto_282
+    if-eqz v6, :cond_57c
+    iget-object v4, v6, LTest;->a:LTest;
+    sget-object v5, LTest;->P:LTest;
+    if-ne v4, v5, :cond_57c
+    new-instance v4, LTest;
+    iget v5, v6, LTest;->a:I
+    iget-object v6, v6, LTest;->a:LTest;
+    sget-object v7, LTest;->P:LTest;
+    iget-wide v8, v10, LTest;->i:J
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v14, v4
+    goto/16 :goto_2c
+    :cond_29b
+    const/4 v5, 0x4
+    if-ne v4, v5, :cond_2c7
+    sget-object v4, LTest;->P:LTest;
+    invoke-virtual {v4}, LTest;->b()Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, Ljava/lang/Integer;
+    invoke-virtual {v4}, Ljava/lang/Integer;->intValue()I
+    move-result v4
+    and-int/lit8 v4, v4, 0x1
+    if-nez v4, :cond_2c4
+    sget-object v4, LTest;->P:LTest;
+    invoke-virtual {v4}, LTest;->a()Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, Ljava/lang/Long;
+    invoke-virtual {v4}, Ljava/lang/Long;->intValue()I
+    move-result v4
+    and-int/lit8 v4, v4, 0x1
+    if-nez v4, :cond_2c1
+    const/4 v4, 0x1
+    goto/16 :goto_234
+    :cond_2c1
+    const/4 v4, 0x0
+    goto/16 :goto_234
+    :cond_2c4
+    const/4 v4, 0x1
+    goto/16 :goto_234
+    :cond_2c7
+    const/4 v4, 0x0
+    goto/16 :goto_234
+    :cond_2ca
+    iget-object v11, v9, LTest;->a:LTest;
+    iget-object v4, v11, LTest;->a:LTest;
+    if-eqz v4, :cond_325
+    iget-wide v4, v11, LTest;->d:J
+    cmp-long v4, v16, v4
+    if-nez v4, :cond_325
+    iget-object v4, v11, LTest;->a:LTest;
+    :goto_2d8
+    if-nez v4, :cond_243
+    invoke-virtual {v10}, LTest;->a()Ljava/lang/String;
+    move-result-object v5
+    iget v6, v10, LTest;->k:I
+    const/4 v7, 0x3
+    if-ne v6, v7, :cond_4e1
+    const-string v6, ""
+    invoke-virtual {v5, v6}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;
+    move-result-object v5
+    array-length v6, v5
+    const/4 v7, 0x5
+    if-ne v6, v7, :cond_2f7
+    const/4 v6, 0x0
+    aget-object v6, v5, v6
+    invoke-static {v6}, LTest;->a(Ljava/lang/String;)I
+    move-result v6
+    const/4 v7, 0x3
+    if-eq v6, v7, :cond_489
+    :cond_2f7
+    const/4 v5, 0x0
+    :goto_2f8
+    if-eqz v5, :cond_2fe
+    iget-object v6, v9, LTest;->a:LTest;
+    iput-object v5, v6, LTest;->f:Ljava/lang/String;
+    :cond_2fe
+    iget-object v5, v9, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->b()J
+    move-result-wide v6
+    iget-wide v0, v9, LTest;->g:J
+    move-wide/from16 v16, v0
+    sub-long v16, v6, v16
+    iget-wide v0, v9, LTest;->e:J
+    move-wide/from16 v18, v0
+    cmp-long v5, v16, v18
+    if-lez v5, :cond_243
+    iget-wide v0, v9, LTest;->f:J
+    move-wide/from16 v16, v0
+    sub-long v16, v6, v16
+    iget-object v5, v9, LTest;->a:LTest;
+    iget-object v5, v5, LTest;->a:LTest;
+    move-wide/from16 v0, v16
+    invoke-virtual {v5, v0, v1}, LTest;->a(J)V
+    iput-wide v6, v9, LTest;->g:J
+    goto/16 :goto_243
+    :cond_325
+    iget-object v4, v11, LTest;->a:LTest;
+    const/4 v5, 0x1
+    move-wide/from16 v0, v16
+    invoke-virtual {v4, v0, v1, v5}, LTest;->a(JZ)[B
+    move-result-object v5
+    if-nez v5, :cond_332
+    const/4 v4, 0x0
+    goto :goto_2d8
+    :cond_332
+    iget-object v0, v11, LTest;->a:LTest;
+    move-object/from16 v18, v0
+    new-instance v4, LTest;
+    sget-object v6, LTest;->P:LTest;
+    invoke-direct {v4, v6}, LTest;-><init>(LTest;)V
+    array-length v6, v5
+    const/4 v7, 0x6
+    if-ge v6, v7, :cond_346
+    const/4 v4, 0x0
+    :cond_342
+    :goto_342
+    if-nez v4, :cond_481
+    const/4 v4, 0x0
+    goto :goto_2d8
+    :cond_346
+    const/4 v6, 0x0
+    invoke-static {v5, v6}, LTest;->b([BI)I
+    move-result v19
+    const/4 v6, 0x3
+    move-object/from16 v0, v18
+    move/from16 v1, v19
+    invoke-virtual {v0, v1, v5, v6}, LTest;->a(I[BI)[D
+    move-result-object v6
+    if-eqz v6, :cond_35d
+    array-length v7, v6
+    const/16 v20, 0x3
+    move/from16 v0, v20
+    if-eq v7, v0, :cond_35f
+    :cond_35d
+    const/4 v4, 0x0
+    goto :goto_342
+    :cond_35f
+    const/4 v7, 0x2
+    const/16 v20, 0x0
+    aget-wide v20, v6, v20
+    const-wide v22, 0x416312d000000000L    # 1.0E7
+    mul-double v20, v20, v22
+    move-wide/from16 v0, v20
+    double-to-int v0, v0
+    move/from16 v20, v0
+    move/from16 v0, v20
+    int-to-long v0, v0
+    move-wide/from16 v20, v0
+    move-wide/from16 v0, v20
+    invoke-virtual {v4, v7, v0, v1}, LTest;->b(IJ)LTest;
+    const/4 v7, 0x3
+    const/16 v20, 0x1
+    aget-wide v20, v6, v20
+    const-wide v22, 0x416312d000000000L    # 1.0E7
+    mul-double v20, v20, v22
+    move-wide/from16 v0, v20
+    double-to-int v0, v0
+    move/from16 v20, v0
+    move/from16 v0, v20
+    int-to-long v0, v0
+    move-wide/from16 v20, v0
+    move-wide/from16 v0, v20
+    invoke-virtual {v4, v7, v0, v1}, LTest;->b(IJ)LTest;
+    const/4 v7, 0x4
+    const/16 v20, 0x2
+    aget-wide v20, v6, v20
+    move-wide/from16 v0, v20
+    double-to-float v6, v0
+    invoke-virtual {v4, v7, v6}, LTest;->b(IF)LTest;
+    array-length v6, v5
+    const/4 v7, 0x6
+    if-eq v6, v7, :cond_47e
+    array-length v6, v5
+    add-int/lit8 v6, v6, -0x6
+    new-array v0, v6, [B
+    move-object/from16 v20, v0
+    const/4 v7, 0x6
+    const/16 v21, 0x0
+    move-object/from16 v0, v20
+    move/from16 v1, v21
+    invoke-static {v5, v7, v0, v1, v6}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
+    new-instance v21, LTest;
+    move-object/from16 v0, v21
+    move-object/from16 v1, v20
+    invoke-direct {v0, v1}, LTest;-><init>([B)V
+    const/4 v5, 0x0
+    move-object/from16 v0, v21
+    iput v5, v0, LTest;->a:I
+    :goto_3c3
+    move-object/from16 v0, v21
+    iget v5, v0, LTest;->a:I
+    move-object/from16 v0, v21
+    iget-object v6, v0, LTest;->b:[B
+    array-length v6, v6
+    add-int/lit8 v6, v6, -0x1
+    if-ge v5, v6, :cond_47e
+    move-object/from16 v0, v21
+    iget v6, v0, LTest;->a:I
+    invoke-virtual/range {v21 .. v21}, LTest;->a()I
+    move-result v5
+    move-object/from16 v0, v20
+    array-length v7, v0
+    add-int/2addr v5, v6
+    if-ge v7, v5, :cond_3e7
+    const/4 v5, 0x0
+    :goto_3df
+    if-nez v5, :cond_46b
+    const/4 v5, 0x0
+    :goto_3e2
+    if-nez v5, :cond_342
+    const/4 v4, 0x0
+    goto/16 :goto_342
+    :cond_3e7
+    new-instance v5, LTest;
+    sget-object v7, LTest;->P:LTest;
+    invoke-direct {v5, v7}, LTest;-><init>(LTest;)V
+    move-object/from16 v0, v20
+    invoke-static {v0, v6}, LTest;->a([BI)I
+    move-result v22
+    add-int/lit8 v6, v6, 0x1
+    aget-byte v23, v20, v6
+    add-int/lit8 v7, v6, 0x1
+    const/4 v6, 0x0
+    :goto_3fb
+    move/from16 v0, v22
+    if-ge v6, v0, :cond_41a
+    move-object/from16 v0, v20
+    move/from16 v1, v23
+    invoke-static {v0, v7, v1, v6}, LTest;->a([BIBI)I
+    move-result v24
+    const/16 v25, 0x2
+    move/from16 v0, v24
+    int-to-long v0, v0
+    move-wide/from16 v26, v0
+    move/from16 v0, v25
+    move-wide/from16 v1, v26
+    invoke-virtual {v5, v0, v1, v2}, LTest;->a(IJ)V
+    add-int/lit8 v7, v7, 0x1
+    add-int/lit8 v6, v6, 0x1
+    goto :goto_3fb
+    :cond_41a
+    move-object/from16 v0, v18
+    move/from16 v1, v19
+    move-object/from16 v2, v20
+    invoke-virtual {v0, v1, v2, v7}, LTest;->a(I[BI)[D
+    move-result-object v6
+    if-nez v6, :cond_428
+    const/4 v5, 0x0
+    goto :goto_3df
+    :cond_428
+    const/4 v7, 0x3
+    const/16 v22, 0x0
+    aget-wide v22, v6, v22
+    const-wide v24, 0x416312d000000000L    # 1.0E7
+    mul-double v22, v22, v24
+    move-wide/from16 v0, v22
+    double-to-int v0, v0
+    move/from16 v22, v0
+    move/from16 v0, v22
+    int-to-long v0, v0
+    move-wide/from16 v22, v0
+    move-wide/from16 v0, v22
+    invoke-virtual {v5, v7, v0, v1}, LTest;->a(IJ)V
+    const/4 v7, 0x4
+    const/16 v22, 0x1
+    aget-wide v22, v6, v22
+    const-wide v24, 0x416312d000000000L    # 1.0E7
+    mul-double v22, v22, v24
+    move-wide/from16 v0, v22
+    double-to-int v0, v0
+    move/from16 v22, v0
+    move/from16 v0, v22
+    int-to-long v0, v0
+    move-wide/from16 v22, v0
+    move-wide/from16 v0, v22
+    invoke-virtual {v5, v7, v0, v1}, LTest;->a(IJ)V
+    const/4 v7, 0x5
+    const/16 v22, 0x2
+    aget-wide v22, v6, v22
+    move-wide/from16 v0, v22
+    double-to-float v6, v0
+    invoke-virtual {v5, v7, v6}, LTest;->a(IF)V
+    goto/16 :goto_3df
+    :cond_46b
+    const/4 v6, 0x5
+    invoke-virtual {v4, v6, v5}, LTest;->a(ILjava/lang/Object;)V
+    move-object/from16 v0, v21
+    iget v5, v0, LTest;->a:I
+    invoke-virtual/range {v21 .. v21}, LTest;->a()I
+    move-result v6
+    add-int/2addr v5, v6
+    move-object/from16 v0, v21
+    iput v5, v0, LTest;->a:I
+    goto/16 :goto_3c3
+    :cond_47e
+    const/4 v5, 0x1
+    goto/16 :goto_3e2
+    :cond_481
+    iput-object v4, v11, LTest;->a:LTest;
+    move-wide/from16 v0, v16
+    iput-wide v0, v11, LTest;->d:J
+    goto/16 :goto_2d8
+    :cond_489
+    new-instance v6, Ljava/lang/StringBuilder;
+    invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V
+    const/4 v7, 0x0
+    aget-object v7, v5, v7
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x1
+    aget-object v7, v5, v7
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x2
+    aget-object v7, v5, v7
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x4
+    aget-object v7, v5, v7
+    invoke-static {v7}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
+    move-result v7
+    shr-int/lit8 v7, v7, 0x10
+    const v11, 0xffff
+    and-int/2addr v7, v11
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x4
+    aget-object v5, v5, v7
+    invoke-static {v5}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
+    move-result v5
+    const v7, 0xffff
+    and-int/2addr v5, v7
+    invoke-virtual {v6, v5}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
+    move-result-object v5
+    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+    move-result-object v5
+    goto/16 :goto_2f8
+    :cond_4e1
+    iget v6, v10, LTest;->k:I
+    const/4 v7, 0x4
+    if-ne v6, v7, :cond_536
+    const-string v6, ""
+    invoke-virtual {v5, v6}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;
+    move-result-object v5
+    array-length v6, v5
+    const/4 v7, 0x4
+    if-ne v6, v7, :cond_4fa
+    const/4 v6, 0x0
+    aget-object v6, v5, v6
+    invoke-static {v6}, LTest;->a(Ljava/lang/String;)I
+    move-result v6
+    const/4 v7, 0x4
+    if-eq v6, v7, :cond_4fd
+    :cond_4fa
+    const/4 v5, 0x0
+    goto/16 :goto_2f8
+    :cond_4fd
+    new-instance v6, Ljava/lang/StringBuilder;
+    invoke-direct {v6}, Ljava/lang/StringBuilder;-><init>()V
+    const/4 v7, 0x0
+    aget-object v7, v5, v7
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x1
+    aget-object v7, v5, v7
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x2
+    aget-object v7, v5, v7
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const-string v7, ""
+    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v6
+    const/4 v7, 0x3
+    aget-object v5, v5, v7
+    invoke-virtual {v6, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    move-result-object v5
+    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+    move-result-object v5
+    goto/16 :goto_2f8
+    :cond_536
+    const/4 v5, 0x0
+    goto/16 :goto_2f8
+    :cond_539
+    const/4 v4, 0x0
+    goto/16 :goto_260
+    :cond_53c
+    const/4 v4, 0x0
+    goto/16 :goto_273
+    :cond_53f
+    const/4 v4, 0x0
+    move-object v6, v4
+    goto/16 :goto_282
+    :cond_543
+    iget-object v5, v8, LTest;->a:LTest;
+    iget-object v5, v5, LTest;->a:LTest;
+    iget-object v6, v10, LTest;->j:Ljava/util/Collection;
+    invoke-interface {v6}, Ljava/util/Collection;->size()I
+    move-result v6
+    iget v7, v5, LTest;->e:I
+    add-int/2addr v6, v7
+    iput v6, v5, LTest;->e:I
+    invoke-virtual {v8, v10, v4}, LTest;->a(LTest;LTest;)LTest;
+    move-result-object v6
+    if-eqz v6, :cond_578
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget v5, v4, LTest;->c:I
+    add-int/lit8 v5, v5, 0x1
+    iput v5, v4, LTest;->c:I
+    iget-wide v4, v8, LTest;->c:J
+    const-wide/16 v16, 0x1
+    add-long v4, v4, v16
+    iput-wide v4, v8, LTest;->c:J
+    new-instance v4, LTest;
+    const/4 v5, 0x2
+    sget-object v7, LTest;->P:LTest;
+    iget-wide v8, v10, LTest;->i:J
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v6, v4
+    goto/16 :goto_282
+    :cond_578
+    const/4 v4, 0x0
+    move-object v6, v4
+    goto/16 :goto_282
+    :cond_57c
+    const/4 v4, 0x0
+    invoke-virtual {v12, v10, v4, v14, v15}, LTest;->a(LTest;Ljava/util/Map;J)LTest;
+    move-result-object v4
+    if-nez v4, :cond_5a1
+    iget-object v4, v12, LTest;->a:LTest;
+    invoke-interface {v4}, LTest;->c()J
+    move-result-wide v4
+    invoke-virtual {v10, v4, v5}, LTest;->a(J)LTest;
+    move-result-object v4
+    move-object/from16 v0, p3
+    invoke-virtual {v0, v4}, LTest;->a(LTest;)V
+    new-instance v4, LTest;
+    const/4 v5, 0x1
+    const/4 v6, 0x0
+    sget-object v7, LTest;->P:LTest;
+    const-wide/16 v8, 0x0
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v14, v4
+    goto/16 :goto_2c
+    :cond_5a1
+    invoke-virtual {v4}, LTest;->b()Z
+    move-result v4
+    if-nez v4, :cond_5b6
+    new-instance v4, LTest;
+    const/4 v5, 0x0
+    const/4 v6, 0x0
+    sget-object v7, LTest;->P:LTest;
+    const-wide/16 v8, 0x0
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v14, v4
+    goto/16 :goto_2c
+    :cond_5b6
+    if-nez v13, :cond_60a
+    const/4 v4, 0x0
+    :goto_5b9
+    new-instance v16, LTest;
+    add-int/lit8 v4, v4, 0x1
+    move-object/from16 v0, v16
+    invoke-direct {v0, v4}, LTest;-><init>(I)V
+    new-instance v11, LTest;
+    const/4 v13, 0x0
+    invoke-direct/range {v11 .. v16}, LTest;-><init>(LTest;Ljava/util/Map;JLTest;)V
+    iget-wide v0, v10, LTest;->i:J
+    move-wide/from16 v18, v0
+    const-wide/16 v20, 0x7530
+    move-object/from16 v17, p2
+    move-object/from16 v22, v11
+    invoke-virtual/range {v17 .. v22}, LTest;->a(JJLTest;)V
+    new-instance v6, LTest;
+    invoke-virtual/range {v16 .. v16}, LTest;->a()D
+    move-result-wide v4
+    invoke-static {v4, v5}, LTest;->a(D)I
+    move-result v4
+    invoke-virtual/range {v16 .. v16}, LTest;->b()D
+    move-result-wide v8
+    invoke-static {v8, v9}, LTest;->a(D)I
+    move-result v5
+    invoke-virtual/range {v16 .. v16}, LTest;->c()I
+    move-result v7
+    invoke-static {v7}, LTest;->b(I)I
+    move-result v7
+    move-object/from16 v0, v16
+    iget v8, v0, LTest;->d:I
+    invoke-direct {v6, v4, v5, v7, v8}, LTest;-><init>(IIII)V
+    invoke-static {v6}, LTest;->c(LTest;)Z
+    move-result v4
+    if-eqz v4, :cond_60f
+    new-instance v4, LTest;
+    const/4 v5, 0x1
+    sget-object v7, LTest;->P:LTest;
+    iget-wide v8, v10, LTest;->i:J
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v14, v4
+    goto/16 :goto_2c
+    :cond_60a
+    invoke-interface {v13}, Ljava/util/LTest;->size()I
+    move-result v4
+    goto :goto_5b9
+    :cond_60f
+    new-instance v4, LTest;
+    const/4 v5, 0x0
+    const/4 v6, 0x0
+    sget-object v7, LTest;->P:LTest;
+    const-wide/16 v8, 0x0
+    const/4 v11, 0x0
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    move-object v14, v4
+    goto/16 :goto_2c
+    :cond_61e
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget v6, v5, LTest;->d:I
+    add-int/lit8 v6, v6, 0x1
+    iput v6, v5, LTest;->d:I
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v6
+    iput-wide v6, v5, LTest;->f:J
+    move-object/from16 v0, p1
+    invoke-virtual {v8, v0}, LTest;->a(LTest;)LTest;
+    move-result-object v6
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->a()V
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->b()J
+    move-result-wide v10
+    iget-wide v12, v4, LTest;->m:J
+    sub-long v12, v10, v12
+    iget-wide v0, v4, LTest;->k:J
+    move-wide/from16 v16, v0
+    cmp-long v5, v12, v16
+    if-lez v5, :cond_65f
+    iget-wide v12, v4, LTest;->l:J
+    sub-long v12, v10, v12
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5, v12, v13}, LTest;->a(J)V
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5, v12, v13}, LTest;->a(J)V
+    iput-wide v10, v4, LTest;->m:J
+    :cond_65f
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->a()V
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->a()V
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->a()V
+    iget-object v4, v4, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->a()V
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-wide v10, v5, LTest;->f:J
+    const-wide/16 v12, -0x1
+    cmp-long v7, v10, v12
+    if-eqz v7, :cond_698
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v10
+    iget-wide v12, v5, LTest;->f:J
+    sub-long/2addr v10, v12
+    const-wide/16 v12, -0x1
+    iput-wide v12, v5, LTest;->f:J
+    const-wide/16 v12, 0x0
+    cmp-long v4, v10, v12
+    if-ltz v4, :cond_698
+    const-wide/16 v12, 0x7530
+    cmp-long v4, v10, v12
+    if-lez v4, :cond_6b0
+    :cond_698
+    :goto_698
+    const/4 v4, 0x0
+    if-eqz v6, :cond_53
+    new-instance v4, LTest;
+    const/4 v5, 0x4
+    sget-object v7, LTest;->P:LTest;
+    iget-object v8, v8, LTest;->a:LTest;
+    iget-object v8, v8, LTest;->a:LTest;
+    invoke-virtual {v8}, LTest;->c()J
+    move-result-wide v8
+    const/4 v11, 0x0
+    move-object/from16 v10, p1
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    goto/16 :goto_53
+    :cond_6b0
+    long-to-int v4, v10
+    iput v4, v5, LTest;->a:I
+    if-eqz v6, :cond_6c0
+    iget v4, v5, LTest;->e:I
+    add-int/lit8 v4, v4, 0x1
+    iput v4, v5, LTest;->e:I
+    iget v4, v5, LTest;->a:I
+    iput v4, v5, LTest;->b:I
+    goto :goto_698
+    :cond_6c0
+    iget v4, v5, LTest;->a:I
+    iput v4, v5, LTest;->c:I
+    goto :goto_698
+    :cond_6c5
+    const/4 v4, 0x0
+    goto/16 :goto_53
+    :cond_6c8
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    invoke-virtual {v4}, LTest;->b()Z
+    move-result v5
+    if-eqz v5, :cond_91d
+    iget-object v8, v4, LTest;->a:LTest;
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-boolean v4, v5, LTest;->d:Z
+    if-nez v4, :cond_716
+    sget-object v4, LTest;->P:LTest;
+    :goto_6de
+    iput-object v4, v8, LTest;->a:LTest;
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v8, LTest;->a:LTest;
+    iget-object v5, v5, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->b()J
+    move-result-wide v6
+    invoke-virtual {v4, v6, v7}, LTest;->a(J)V
+    if-nez p1, :cond_721
+    iget-object v4, v8, LTest;->a:LTest;
+    const/4 v5, 0x3
+    iget-object v6, v8, LTest;->a:LTest;
+    iget-object v6, v6, LTest;->a:LTest;
+    invoke-virtual {v6}, LTest;->b()J
+    move-result-wide v6
+    invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+    const/4 v4, 0x0
+    :goto_6fe
+    if-eqz v4, :cond_920
+    iget-object v5, v4, LTest;->a:LTest;
+    sget-object v6, LTest;->P:LTest;
+    if-ne v5, v6, :cond_9cc
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->a:LTest;
+    move-object/from16 v0, p1
+    move-object/from16 v1, p4
+    move-object/from16 v2, p3
+    invoke-virtual {v5, v0, v1, v2}, LTest;->a(LTest;LTest;LTest;)LTest;
+    move-object v12, v4
+    goto/16 :goto_56
+    :cond_716
+    new-instance v4, LTest;
+    invoke-direct {v4}, LTest;-><init>()V
+    iget-object v5, v5, LTest;->a:LTest;
+    invoke-virtual {v5, v4}, LTest;->a(Ljava/lang/Object;)Ljava/lang/Object;
+    goto :goto_6de
+    :cond_721
+    iget-object v4, v8, LTest;->a:LTest;
+    move-object/from16 v0, p1
+    invoke-virtual {v4, v0}, LTest;->a(LTest;)V
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+    move-result-object v5
+    iget v6, v5, LTest;->c:I
+    add-int/lit8 v6, v6, 0x1
+    iput v6, v5, LTest;->c:I
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v6
+    iput-wide v6, v5, LTest;->b:J
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+    move-result-object v5
+    iget v6, v5, LTest;->c:I
+    add-int/lit8 v6, v6, 0x1
+    iput v6, v5, LTest;->c:I
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v6
+    iput-wide v6, v5, LTest;->b:J
+    move-object/from16 v0, p1
+    invoke-virtual {v8, v0}, LTest;->a(LTest;)Z
+    move-result v4
+    iget-object v5, v8, LTest;->a:LTest;
+    iget-object v6, v5, LTest;->a:LTest;
+    iget-object v5, v5, LTest;->a:LTest;
+    const-string v7, ""
+    invoke-virtual {v6, v7}, LTest;->a(Ljava/lang/String;)LTest;
+    move-result-object v6
+    iget-wide v10, v6, LTest;->b:J
+    const-wide/16 v12, -0x1
+    cmp-long v7, v10, v12
+    if-eqz v7, :cond_789
+    invoke-virtual {v5}, LTest;->c()J
+    move-result-wide v10
+    iget-wide v12, v6, LTest;->b:J
+    sub-long/2addr v10, v12
+    const-wide/16 v12, -0x1
+    iput-wide v12, v6, LTest;->b:J
+    const-wide/16 v12, 0x0
+    cmp-long v5, v10, v12
+    if-ltz v5, :cond_789
+    const-wide/16 v12, 0x7530
+    cmp-long v5, v10, v12
+    if-lez v5, :cond_83e
+    :cond_789
+    :goto_789
+    if-eqz v4, :cond_793
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->c()Z
+    move-result v4
+    if-eqz v4, :cond_843
+    :cond_793
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->a()V
+    const/4 v6, 0x0
+    :goto_799
+    iget-object v4, v8, LTest;->a:LTest;
+    const/4 v5, 0x0
+    invoke-virtual {v4, v5}, LTest;->a(Ljava/util/LTest;)V
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->a()V
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->b()J
+    move-result-wide v10
+    iget-wide v12, v4, LTest;->q:J
+    sub-long v12, v10, v12
+    iget-wide v0, v4, LTest;->o:J
+    move-wide/from16 v16, v0
+    cmp-long v5, v12, v16
+    if-lez v5, :cond_7c3
+    iget-wide v12, v4, LTest;->p:J
+    sub-long v12, v10, v12
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5, v12, v13}, LTest;->a(J)V
+    iput-wide v10, v4, LTest;->q:J
+    :cond_7c3
+    iget-wide v12, v4, LTest;->s:J
+    sub-long v12, v10, v12
+    iget-wide v0, v4, LTest;->r:J
+    move-wide/from16 v16, v0
+    cmp-long v5, v12, v16
+    if-lez v5, :cond_7d6
+    iget-object v5, v4, LTest;->a:LTest;
+    invoke-virtual {v5}, LTest;->a()V
+    iput-wide v10, v4, LTest;->s:J
+    :cond_7d6
+    iget-object v4, v4, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->a()V
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    const-string v7, ""
+    invoke-virtual {v5, v7}, LTest;->a(Ljava/lang/String;)LTest;
+    move-result-object v7
+    iget-wide v10, v7, LTest;->b:J
+    const-wide/16 v12, -0x1
+    cmp-long v9, v10, v12
+    if-eqz v9, :cond_806
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v10
+    iget-wide v12, v7, LTest;->b:J
+    sub-long/2addr v10, v12
+    const-wide/16 v12, -0x1
+    iput-wide v12, v7, LTest;->b:J
+    const-wide/16 v12, 0x0
+    cmp-long v4, v10, v12
+    if-ltz v4, :cond_806
+    const-wide/16 v12, 0x7530
+    cmp-long v4, v10, v12
+    if-lez v4, :cond_8d7
+    :cond_806
+    :goto_806
+    if-eqz v6, :cond_8dc
+    iget v4, v5, LTest;->f:I
+    add-int/lit8 v4, v4, 0x1
+    iput v4, v5, LTest;->f:I
+    iget v4, v7, LTest;->a:I
+    iput v4, v5, LTest;->g:I
+    :goto_812
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->c()Z
+    move-result v4
+    if-eqz v4, :cond_8e2
+    iget-object v4, v8, LTest;->a:LTest;
+    const/4 v5, 0x7
+    iget-object v6, v8, LTest;->a:LTest;
+    iget-object v6, v6, LTest;->a:LTest;
+    invoke-virtual {v6}, LTest;->b()J
+    move-result-wide v6
+    invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+    new-instance v4, LTest;
+    const/4 v5, 0x0
+    const/4 v6, 0x0
+    sget-object v7, LTest;->P:LTest;
+    iget-object v8, v8, LTest;->a:LTest;
+    iget-object v8, v8, LTest;->a:LTest;
+    invoke-virtual {v8}, LTest;->c()J
+    move-result-wide v8
+    const/4 v11, 0x0
+    move-object/from16 v10, p1
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    goto/16 :goto_6fe
+    :cond_83e
+    long-to-int v5, v10
+    iput v5, v6, LTest;->a:I
+    goto/16 :goto_789
+    :cond_843
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+    move-result-object v5
+    iget v6, v5, LTest;->c:I
+    add-int/lit8 v6, v6, 0x1
+    iput v6, v5, LTest;->c:I
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v6
+    iput-wide v6, v5, LTest;->b:J
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->b()LTest;
+    move-result-object v4
+    iput-object v4, v8, LTest;->a:LTest;
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    const-string v6, ""
+    invoke-virtual {v5, v6}, LTest;->a(Ljava/lang/String;)LTest;
+    move-result-object v5
+    iget-wide v6, v5, LTest;->b:J
+    const-wide/16 v10, -0x1
+    cmp-long v6, v6, v10
+    if-eqz v6, :cond_88e
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v6
+    iget-wide v10, v5, LTest;->b:J
+    sub-long/2addr v6, v10
+    const-wide/16 v10, -0x1
+    iput-wide v10, v5, LTest;->b:J
+    const-wide/16 v10, 0x0
+    cmp-long v4, v6, v10
+    if-ltz v4, :cond_88e
+    const-wide/16 v10, 0x7530
+    cmp-long v4, v6, v10
+    if-lez v4, :cond_8a7
+    :cond_88e
+    :goto_88e
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4}, LTest;->b()Z
+    move-result v4
+    if-nez v4, :cond_8ab
+    iget-object v4, v8, LTest;->a:LTest;
+    const/4 v5, 0x4
+    iget-object v6, v8, LTest;->a:LTest;
+    iget-object v6, v6, LTest;->a:LTest;
+    invoke-virtual {v6}, LTest;->b()J
+    move-result-wide v6
+    invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+    const/4 v6, 0x0
+    goto/16 :goto_799
+    :cond_8a7
+    long-to-int v4, v6
+    iput v4, v5, LTest;->a:I
+    goto :goto_88e
+    :cond_8ab
+    iget-object v4, v8, LTest;->a:LTest;
+    iget-wide v4, v4, LTest;->a:J
+    const/4 v6, 0x2
+    new-array v6, v6, [I
+    invoke-static {v4, v5, v6}, LTest;->a(J[I)[I
+    move-result-object v4
+    iget-object v5, v8, LTest;->a:LTest;
+    iget-object v6, v8, LTest;->a:LTest;
+    iget-object v6, v6, LTest;->a:LTest;
+    iget v6, v6, LTest;->b:I
+    iget-object v7, v8, LTest;->a:LTest;
+    iget-object v7, v7, LTest;->a:LTest;
+    iget v7, v7, LTest;->b:I
+    invoke-virtual {v5, v6, v7, v4}, LTest;->a(II[I)I
+    move-result v5
+    new-instance v6, LTest;
+    const/4 v7, 0x0
+    aget v7, v4, v7
+    const/4 v9, 0x1
+    aget v4, v4, v9
+    const/16 v9, 0x6d
+    invoke-direct {v6, v7, v4, v5, v9}, LTest;-><init>(IIII)V
+    goto/16 :goto_799
+    :cond_8d7
+    long-to-int v4, v10
+    iput v4, v7, LTest;->a:I
+    goto/16 :goto_806
+    :cond_8dc
+    iget v4, v7, LTest;->a:I
+    iput v4, v5, LTest;->h:I
+    goto/16 :goto_812
+    :cond_8e2
+    if-nez v6, :cond_8f5
+    iget-object v4, v8, LTest;->a:LTest;
+    const/4 v5, 0x5
+    iget-object v6, v8, LTest;->a:LTest;
+    iget-object v6, v6, LTest;->a:LTest;
+    invoke-virtual {v6}, LTest;->b()J
+    move-result-wide v6
+    invoke-virtual {v4, v5, v6, v7}, LTest;->a(IJ)V
+    const/4 v4, 0x0
+    goto/16 :goto_6fe
+    :cond_8f5
+    iget-object v4, v8, LTest;->a:LTest;
+    invoke-virtual {v4, v6}, LTest;->a(LTest;)V
+    iget-object v4, v8, LTest;->a:LTest;
+    const/4 v5, 0x1
+    iget-object v7, v8, LTest;->a:LTest;
+    iget-object v7, v7, LTest;->a:LTest;
+    invoke-virtual {v7}, LTest;->b()J
+    move-result-wide v10
+    invoke-virtual {v4, v5, v10, v11}, LTest;->a(IJ)V
+    new-instance v4, LTest;
+    const/4 v5, 0x5
+    sget-object v7, LTest;->P:LTest;
+    iget-object v8, v8, LTest;->a:LTest;
+    iget-object v8, v8, LTest;->a:LTest;
+    invoke-virtual {v8}, LTest;->c()J
+    move-result-wide v8
+    const/4 v11, 0x0
+    move-object/from16 v10, p1
+    invoke-direct/range {v4 .. v11}, LTest;-><init>(ILTest;LTest;JLTest;Ljava/util/Map;)V
+    goto/16 :goto_6fe
+    :cond_91d
+    const/4 v4, 0x0
+    goto/16 :goto_6fe
+    :cond_920
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    move-object/from16 v0, p1
+    move-object/from16 v1, p4
+    move-object/from16 v2, p3
+    invoke-virtual {v4, v0, v1, v2}, LTest;->a(LTest;LTest;LTest;)LTest;
+    move-result-object v4
+    move-object v12, v4
+    goto/16 :goto_56
+    :cond_931
+    const/4 v4, 0x0
+    goto/16 :goto_5d
+    :cond_934
+    const/4 v5, 0x0
+    goto/16 :goto_64
+    :cond_937
+    if-nez v4, :cond_93c
+    move-object v13, v14
+    goto/16 :goto_6a
+    :cond_93c
+    if-eqz v5, :cond_973
+    iget-object v5, v12, LTest;->a:LTest;
+    iget-object v6, v14, LTest;->a:LTest;
+    invoke-static {v5, v6}, LTest;->a(LTest;LTest;)I
+    move-result v4
+    iget v7, v5, LTest;->e:I
+    iget v8, v6, LTest;->e:I
+    add-int/2addr v7, v8
+    const v8, 0x3567e0
+    invoke-static {v7, v8}, Ljava/lang/Math;->max(II)I
+    move-result v7
+    div-int/lit16 v7, v7, 0x3e8
+    if-gt v4, v7, :cond_965
+    const/4 v4, 0x1
+    :goto_957
+    if-eqz v4, :cond_969
+    iget v4, v5, LTest;->e:I
+    iget v5, v6, LTest;->e:I
+    if-le v4, v5, :cond_967
+    const/4 v4, 0x1
+    :goto_960
+    if-eqz v4, :cond_973
+    move-object v13, v14
+    goto/16 :goto_6a
+    :cond_965
+    const/4 v4, 0x0
+    goto :goto_957
+    :cond_967
+    const/4 v4, 0x0
+    goto :goto_960
+    :cond_969
+    iget v4, v5, LTest;->f:I
+    iget v5, v6, LTest;->f:I
+    if-ge v4, v5, :cond_971
+    const/4 v4, 0x1
+    goto :goto_960
+    :cond_971
+    const/4 v4, 0x0
+    goto :goto_960
+    :cond_973
+    move-object v13, v12
+    goto/16 :goto_6a
+    :cond_976
+    const/4 v4, 0x1
+    move v11, v4
+    move v15, v5
+    goto/16 :goto_7b
+    :cond_97b
+    const/4 v4, 0x0
+    goto/16 :goto_84
+    :pswitch_97e
+    const-string v7, ""
+    goto/16 :goto_8c
+    :pswitch_982
+    const-string v7, ""
+    goto/16 :goto_8c
+    :pswitch_986
+    const-string v7, ""
+    goto/16 :goto_8c
+    :pswitch_98a
+    const-string v7, ""
+    goto/16 :goto_8c
+    :cond_98e
+    const/4 v4, 0x0
+    goto/16 :goto_a3
+    :pswitch_991
+    const-string v7, ""
+    goto/16 :goto_ab
+    :pswitch_995
+    const-string v7, ""
+    goto/16 :goto_ab
+    :cond_999
+    if-eqz v11, :cond_9ad
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    const-string v5, ""
+    const-string v6, ""
+    const-string v7, ""
+    const-wide/16 v8, 0x1
+    const/4 v10, 0x1
+    invoke-virtual/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+    goto/16 :goto_cd
+    :cond_9ad
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->a:LTest;
+    const-string v5, ""
+    const-string v6, ""
+    const-string v7, ""
+    const-wide/16 v8, 0x1
+    const/4 v10, 0x1
+    invoke-virtual/range {v4 .. v10}, LTest;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V
+    goto/16 :goto_cd
+    :cond_9bf
+    const-wide/16 v8, 0x0
+    move-wide/from16 v16, v8
+    goto/16 :goto_149
+    :cond_9c5
+    const/4 v4, 0x0
+    goto/16 :goto_217
+    :cond_9c8
+    move v11, v4
+    move v15, v5
+    goto/16 :goto_7b
+    :cond_9cc
+    move-object v12, v4
+    goto/16 :goto_56
+    :cond_9cf
+    move-object v13, v4
+    goto/16 :goto_17
+    :pswitch_data_9d2
+    .packed-switch 0x1
+        :pswitch_97e
+        :pswitch_982
+        :pswitch_8c
+        :pswitch_986
+        :pswitch_98a
+    .end packed-switch
+    :pswitch_data_9e0
+    .packed-switch 0x1
+        :pswitch_991
+        :pswitch_995
+    .end packed-switch
+.end method
\ No newline at end of file
diff --git a/src/test/smali/chain-of-loops/Test.java b/src/test/smali/chain-of-loops/Test.java
new file mode 100644
index 0000000..77d335d
--- /dev/null
+++ b/src/test/smali/chain-of-loops/Test.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+  public int b(int i, String s) {
+    throw new RuntimeException("b(ILjava/lang/String;)");
+  }
+
+  public int b(Test t) {
+    throw new RuntimeException("b(LTest;)");
+  }
+
+  public static int f(int i0, int i1) {
+    throw new RuntimeException("f(II)");
+  }
+
+  public static void main(String[] args) {
+    try {
+      new TestObject().method();
+    } catch (Exception e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/chain-of-loops/TestObject.java b/src/test/smali/chain-of-loops/TestObject.java
new file mode 100644
index 0000000..85cb78a
--- /dev/null
+++ b/src/test/smali/chain-of-loops/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public final int method() {
+    return 0;
+  }
+}
diff --git a/src/test/smali/chain-of-loops/TestObject.smali b/src/test/smali/chain-of-loops/TestObject.smali
new file mode 100644
index 0000000..d516ee3
--- /dev/null
+++ b/src/test/smali/chain-of-loops/TestObject.smali
@@ -0,0 +1,1028 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field public i:I
+.field public e:Ljava/lang/String;
+.field public z:Ljava/lang/String;
+.field public f:LTest;
+.field public a:[LTest;
+
+.method public final method()I
+    .registers 7
+    .prologue
+    const/4 v5, 0x1
+    const/4 v1, 0x0
+    invoke-virtual {p0}, LTestObject;->method2()I
+    move-result v0
+    iget-object v2, p0, LTestObject;->e:Ljava/lang/String;
+    if-eqz v2, :cond_1b
+    iget-object v2, p0, LTestObject;->e:Ljava/lang/String;
+    const-string v3, ""
+    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+    move-result v2
+    if-nez v2, :cond_1b
+    iget-object v2, p0, LTestObject;->e:Ljava/lang/String;
+    invoke-static {v5, v2}, LTest;->b(ILjava/lang/String;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_1b
+    iget-object v2, p0, LTestObject;->f:LTest;
+    if-eqz v2, :cond_27
+    const/4 v2, 0x2
+    iget-object v3, p0, LTestObject;->f:LTest;
+    invoke-static {v2, v3}, LTest;->d(ILTest;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_27
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_47
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_47
+    move v2, v0
+    move v0, v1
+    :goto_32
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_46
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_43
+    const/4 v4, 0x3
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_43
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_32
+    :cond_46
+    move v0, v2
+    :cond_47
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_67
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_67
+    move v2, v0
+    move v0, v1
+    :goto_52
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_66
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_63
+    const/4 v4, 0x4
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_63
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_52
+    :cond_66
+    move v0, v2
+    :cond_67
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_87
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_87
+    move v2, v0
+    move v0, v1
+    :goto_72
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_86
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_83
+    const/4 v4, 0x5
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_83
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_72
+    :cond_86
+    move v0, v2
+    :cond_87
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_a7
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_a7
+    move v2, v0
+    move v0, v1
+    :goto_92
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_a6
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_a3
+    const/4 v4, 0x6
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_a3
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_92
+    :cond_a6
+    move v0, v2
+    :cond_a7
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_c7
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_c7
+    move v2, v0
+    move v0, v1
+    :goto_b2
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_c6
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_c3
+    const/4 v4, 0x7
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_c3
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_b2
+    :cond_c6
+    move v0, v2
+    :cond_c7
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_e8
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_e8
+    move v2, v0
+    move v0, v1
+    :goto_d2
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_e7
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_e4
+    const/16 v4, 0x8
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_e4
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_d2
+    :cond_e7
+    move v0, v2
+    :cond_e8
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_109
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_109
+    move v2, v0
+    move v0, v1
+    :goto_f3
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_108
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_105
+    const/16 v4, 0x9
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_105
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_f3
+    :cond_108
+    move v0, v2
+    :cond_109
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_12a
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_12a
+    move v2, v0
+    move v0, v1
+    :goto_114
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_129
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_126
+    const/16 v4, 0xa
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_126
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_114
+    :cond_129
+    move v0, v2
+    :cond_12a
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_14b
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_14b
+    move v2, v0
+    move v0, v1
+    :goto_135
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_14a
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_147
+    const/16 v4, 0xb
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_147
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_135
+    :cond_14a
+    move v0, v2
+    :cond_14b
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_16c
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_16c
+    move v2, v0
+    move v0, v1
+    :goto_156
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_16b
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_168
+    const/16 v4, 0xc
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_168
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_156
+    :cond_16b
+    move v0, v2
+    :cond_16c
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_18d
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_18d
+    move v2, v0
+    move v0, v1
+    :goto_177
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_18c
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_189
+    const/16 v4, 0xd
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_189
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_177
+    :cond_18c
+    move v0, v2
+    :cond_18d
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_1ae
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_1ae
+    move v2, v0
+    move v0, v1
+    :goto_198
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_1ad
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_1aa
+    const/16 v4, 0xe
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_1aa
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_198
+    :cond_1ad
+    move v0, v2
+    :cond_1ae
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_1cf
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_1cf
+    move v2, v0
+    move v0, v1
+    :goto_1b9
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_1ce
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_1cb
+    const/16 v4, 0xf
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_1cb
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_1b9
+    :cond_1ce
+    move v0, v2
+    :cond_1cf
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_1f0
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_1f0
+    move v2, v0
+    move v0, v1
+    :goto_1da
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_1ef
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_1ec
+    const/16 v4, 0x10
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_1ec
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_1da
+    :cond_1ef
+    move v0, v2
+    :cond_1f0
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_211
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_211
+    move v2, v0
+    move v0, v1
+    :goto_1fb
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_210
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_20d
+    const/16 v4, 0x11
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_20d
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_1fb
+    :cond_210
+    move v0, v2
+    :cond_211
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_232
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_232
+    move v2, v0
+    move v0, v1
+    :goto_21c
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_231
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_22e
+    const/16 v4, 0x12
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_22e
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_21c
+    :cond_231
+    move v0, v2
+    :cond_232
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_253
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_253
+    move v2, v0
+    move v0, v1
+    :goto_23d
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_252
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_24f
+    const/16 v4, 0x13
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_24f
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_23d
+    :cond_252
+    move v0, v2
+    :cond_253
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_274
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_274
+    move v2, v0
+    move v0, v1
+    :goto_25e
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_273
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_270
+    const/16 v4, 0x14
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_270
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_25e
+    :cond_273
+    move v0, v2
+    :cond_274
+    iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+    if-eqz v2, :cond_28b
+    iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+    const-string v3, ""
+    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+    move-result v2
+    if-nez v2, :cond_28b
+    const/16 v2, 0x15
+    iget-object v3, p0, LTestObject;->z:Ljava/lang/String;
+    invoke-static {v2, v3}, LTest;->b(ILjava/lang/String;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_28b
+    iget v2, p0, LTestObject;->i:I
+    if-eq v2, v5, :cond_298
+    const/16 v2, 0x16
+    iget v3, p0, LTestObject;->i:I
+    invoke-static {v2, v3}, LTest;->f(II)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_298
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_2b9
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_2b9
+    move v2, v0
+    move v0, v1
+    :goto_2a3
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_2b8
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_2b5
+    const/16 v4, 0x17
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_2b5
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_2a3
+    :cond_2b8
+    move v0, v2
+    :cond_2b9
+    iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+    if-eqz v2, :cond_2d0
+    iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+    const-string v3, ""
+    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+    move-result v2
+    if-nez v2, :cond_2d0
+    const/16 v2, 0x18
+    iget-object v3, p0, LTestObject;->z:Ljava/lang/String;
+    invoke-static {v2, v3}, LTest;->b(ILjava/lang/String;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_2d0
+    iget-object v2, p0, LTest;->M:LTest;
+    if-eqz v2, :cond_2dd
+    const/16 v2, 0x19
+    iget-object v3, p0, LTest;->M:LTest;
+    invoke-static {v2, v3}, LTest;->d(ILTest;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_2dd
+    iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+    if-eqz v2, :cond_2f4
+    iget-object v2, p0, LTestObject;->z:Ljava/lang/String;
+    const-string v3, ""
+    invoke-virtual {v2, v3}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
+    move-result v2
+    if-nez v2, :cond_2f4
+    const/16 v2, 0x1a
+    iget-object v3, p0, LTestObject;->z:Ljava/lang/String;
+    invoke-static {v2, v3}, LTest;->b(ILjava/lang/String;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_2f4
+    iget-object v2, p0, LTest;->o:[LTest;
+    if-eqz v2, :cond_315
+    iget-object v2, p0, LTest;->o:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_315
+    move v2, v0
+    move v0, v1
+    :goto_2ff
+    iget-object v3, p0, LTest;->o:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_314
+    iget-object v3, p0, LTest;->o:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_311
+    const/16 v4, 0x1b
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_311
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_2ff
+    :cond_314
+    move v0, v2
+    :cond_315
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_336
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_336
+    move v2, v0
+    move v0, v1
+    :goto_320
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_335
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_332
+    const/16 v4, 0x1c
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_332
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_320
+    :cond_335
+    move v0, v2
+    :cond_336
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_357
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_357
+    move v2, v0
+    move v0, v1
+    :goto_341
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_356
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_353
+    const/16 v4, 0x1d
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_353
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_341
+    :cond_356
+    move v0, v2
+    :cond_357
+    iget-object v2, p0, LTest;->t:[LTest;
+    if-eqz v2, :cond_378
+    iget-object v2, p0, LTest;->t:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_378
+    move v2, v0
+    move v0, v1
+    :goto_362
+    iget-object v3, p0, LTest;->t:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_377
+    iget-object v3, p0, LTest;->t:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_374
+    const/16 v4, 0x1e
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_374
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_362
+    :cond_377
+    move v0, v2
+    :cond_378
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_399
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_399
+    move v2, v0
+    move v0, v1
+    :goto_383
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_398
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_395
+    const/16 v4, 0x1f
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_395
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_383
+    :cond_398
+    move v0, v2
+    :cond_399
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_3ba
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_3ba
+    move v2, v0
+    move v0, v1
+    :goto_3a4
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_3b9
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_3b6
+    const/16 v4, 0x20
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_3b6
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_3a4
+    :cond_3b9
+    move v0, v2
+    :cond_3ba
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_3db
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_3db
+    move v2, v0
+    move v0, v1
+    :goto_3c5
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_3da
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_3d7
+    const/16 v4, 0x21
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_3d7
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_3c5
+    :cond_3da
+    move v0, v2
+    :cond_3db
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_3fc
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_3fc
+    move v2, v0
+    move v0, v1
+    :goto_3e6
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_3fb
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_3f8
+    const/16 v4, 0x22
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_3f8
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_3e6
+    :cond_3fb
+    move v0, v2
+    :cond_3fc
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_41d
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_41d
+    move v2, v0
+    move v0, v1
+    :goto_407
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_41c
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_419
+    const/16 v4, 0x23
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_419
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_407
+    :cond_41c
+    move v0, v2
+    :cond_41d
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_43e
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_43e
+    move v2, v0
+    move v0, v1
+    :goto_428
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_43d
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_43a
+    const/16 v4, 0x24
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_43a
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_428
+    :cond_43d
+    move v0, v2
+    :cond_43e
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_45f
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_45f
+    move v2, v0
+    move v0, v1
+    :goto_449
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_45e
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_45b
+    const/16 v4, 0x25
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_45b
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_449
+    :cond_45e
+    move v0, v2
+    :cond_45f
+    iget-object v2, p0, LTest;->T:LTest;
+    if-eqz v2, :cond_46c
+    const/16 v2, 0x26
+    iget-object v3, p0, LTest;->T:LTest;
+    invoke-static {v2, v3}, LTest;->d(ILTest;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_46c
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_48d
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_48d
+    move v2, v0
+    move v0, v1
+    :goto_477
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_48c
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_489
+    const/16 v4, 0x27
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_489
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_477
+    :cond_48c
+    move v0, v2
+    :cond_48d
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_4ae
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_4ae
+    move v2, v0
+    move v0, v1
+    :goto_498
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_4ad
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_4aa
+    const/16 v4, 0x28
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_4aa
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_498
+    :cond_4ad
+    move v0, v2
+    :cond_4ae
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_4cf
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_4cf
+    move v2, v0
+    move v0, v1
+    :goto_4b9
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_4ce
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_4cb
+    const/16 v4, 0x29
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_4cb
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_4b9
+    :cond_4ce
+    move v0, v2
+    :cond_4cf
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_4f0
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_4f0
+    move v2, v0
+    move v0, v1
+    :goto_4da
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_4ef
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_4ec
+    const/16 v4, 0x2a
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_4ec
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_4da
+    :cond_4ef
+    move v0, v2
+    :cond_4f0
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_511
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_511
+    move v2, v0
+    move v0, v1
+    :goto_4fb
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_510
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_50d
+    const/16 v4, 0x2b
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_50d
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_4fb
+    :cond_510
+    move v0, v2
+    :cond_511
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_532
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_532
+    move v2, v0
+    move v0, v1
+    :goto_51c
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_531
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_52e
+    const/16 v4, 0x2c
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_52e
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_51c
+    :cond_531
+    move v0, v2
+    :cond_532
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_553
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_553
+    move v2, v0
+    move v0, v1
+    :goto_53d
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    array-length v3, v3
+    if-ge v0, v3, :cond_552
+    iget-object v3, p0, LTestObject;->a:[LTest;
+    aget-object v3, v3, v0
+    if-eqz v3, :cond_54f
+    const/16 v4, 0x2d
+    invoke-static {v4, v3}, LTest;->d(ILTest;)I
+    move-result v3
+    add-int/2addr v2, v3
+    :cond_54f
+    add-int/lit8 v0, v0, 0x1
+    goto :goto_53d
+    :cond_552
+    move v0, v2
+    :cond_553
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    if-eqz v2, :cond_571
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-lez v2, :cond_571
+    :goto_55c
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    array-length v2, v2
+    if-ge v1, v2, :cond_571
+    iget-object v2, p0, LTestObject;->a:[LTest;
+    aget-object v2, v2, v1
+    if-eqz v2, :cond_56e
+    const/16 v3, 0x2e
+    invoke-static {v3, v2}, LTest;->d(ILTest;)I
+    move-result v2
+    add-int/2addr v0, v2
+    :cond_56e
+    add-int/lit8 v1, v1, 0x1
+    goto :goto_55c
+    :cond_571
+    iget-object v1, p0, LTest;->V:LTest;
+    if-eqz v1, :cond_57e
+    const/16 v1, 0x64
+    iget-object v2, p0, LTest;->V:LTest;
+    invoke-static {v1, v2}, LTest;->d(ILTest;)I
+    move-result v1
+    add-int/2addr v0, v1
+    :cond_57e
+    return v0
+.end method
+
+.method public final method2()I
+    .registers 1
+    .prologue
+    const/4 v0, 0x1
+    return v0
+.end method
diff --git a/src/test/smali/controlflow/ControlFlow.smali b/src/test/smali/controlflow/ControlFlow.smali
new file mode 100644
index 0000000..35881fe
--- /dev/null
+++ b/src/test/smali/controlflow/ControlFlow.smali
@@ -0,0 +1,225 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static constantEqTrue()I
+    .locals 1
+
+    const v0, 0
+    if-eqz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantEqFalse()I
+    .locals 1
+
+    const v0, 1
+    if-eqz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantNeTrue()I
+    .locals 1
+
+    const v0, 1
+    if-nez v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantNeFalse()I
+    .locals 1
+
+    const v0, 0
+    if-nez v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantLtzTrue()I
+    .locals 1
+
+    const v0, -1
+    if-ltz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantLtzFalse()I
+    .locals 1
+
+    const v0, 0
+    if-ltz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantLezTrue()I
+    .locals 1
+
+    const v0, 0
+    if-lez v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantLezFalse()I
+    .locals 1
+
+    const v0, 1
+    if-lez v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantGtzTrue()I
+    .locals 1
+
+    const v0, 1
+    if-gtz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantGtzFalse()I
+    .locals 1
+
+    const v0, 0
+    if-gtz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantGezTrue()I
+    .locals 1
+
+    const v0, 0
+    if-gez v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static constantGezFalse()I
+    .locals 1
+
+    const v0, -1
+    if-gez v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method static cmpConstantLong()I
+    .locals 4
+
+    const-wide v0, 0
+    const-wide v2, 0
+    cmp-long v0, v0, v2
+    if-eqz v0, :equals
+    const v0, 1
+    return v0
+    :equals
+    const v0, 2
+    return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 3
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-static {}, LTest;->constantEqTrue()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantEqFalse()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantNeTrue()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantNeFalse()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantLtzTrue()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantLtzFalse()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantLezTrue()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantLezFalse()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantGtzTrue()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantGtzFalse()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantGezTrue()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->constantGezFalse()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    invoke-static {}, LTest;->cmpConstantLong()I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    return-void
+.end method
diff --git a/src/test/smali/fibonacci/Fibonacci.smali b/src/test/smali/fibonacci/Fibonacci.smali
new file mode 100644
index 0000000..3345ec0
--- /dev/null
+++ b/src/test/smali/fibonacci/Fibonacci.smali
@@ -0,0 +1,129 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static fibonacci(I)I
+    .locals 2
+
+    if-eqz p0, :return
+    const/4 v0, 0x1
+    if-ne p0, v0, :calc
+
+    :return
+    return p0
+
+    :calc
+    add-int/lit8 v0, p0, -0x1
+    invoke-static {v0}, LTest;->fibonacci(I)I
+    move-result v0
+    add-int/lit8 v1, p0, -0x2
+    invoke-static {v1}, LTest;->fibonacci(I)I
+    move-result v1
+    add-int p0, v0, v1
+    goto :return
+.end method
+
+.method static fibonacciLong(J)J
+    .locals 4
+
+    const-wide/16 v2, 0x1
+    const-wide/16 v0, 0x0
+    cmp-long v0, p0, v0
+    if-eqz v0, :return
+    cmp-long v0, p0, v2
+    if-nez v0, :calc
+
+    :return
+    return-wide p0
+
+    :calc
+    sub-long v0, p0, v2
+    invoke-static {v0, v1}, LTest;->fibonacciLong(J)J
+    move-result-wide v0
+    const-wide/16 v2, 0x2
+    sub-long v2, p0, v2
+    invoke-static {v2, v3}, LTest;->fibonacciLong(J)J
+    move-result-wide v2
+    add-long p0, v0, v2
+    goto :return
+.end method
+
+.method static fibonacciJack(I)I
+    .locals 2
+
+    if-eqz p0, :return
+    const/4 v0, 0x1
+    if-ne p0, v0, :calc
+
+    :return
+    return p0
+
+    :calc
+    add-int/lit8 v0, p0, -0x1
+    invoke-static {v0}, LTest;->fibonacciJack(I)I
+    move-result v0
+    add-int/lit8 v1, p0, -0x2
+    invoke-static {v1}, LTest;->fibonacciJack(I)I
+    move-result v1
+    add-int/2addr v0, v1
+    return v0
+.end method
+
+.method static fibonacciLongJack(J)J
+    .locals 4
+
+    const-wide/16 v2, 0x1
+    const-wide/16 v0, 0x0
+    cmp-long v0, p0, v0
+    if-eqz v0, :return
+    cmp-long v0, p0, v2
+    if-nez v0, :calc
+
+    :return
+    return-wide p0
+
+    :calc
+    sub-long v0, p0, v2
+    invoke-static {v0, v1}, LTest;->fibonacciLongJack(J)J
+    move-result-wide v0
+    const-wide/16 v2, 0x2
+    sub-long v2, p0, v2
+    invoke-static {v2, v3}, LTest;->fibonacciLongJack(J)J
+    move-result-wide v2
+    add-long/2addr v0, v2
+    return-wide v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .registers 5
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    const/16 v1, 0xa
+    invoke-static {v1}, LTest;->fibonacci(I)I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    const-wide/16 v2, 0xa
+    invoke-static {v2, v3}, LTest;->fibonacciLong(J)J
+    move-result-wide v2
+    invoke-virtual {v0, v2, v3}, Ljava/io/PrintStream;->println(J)V
+
+    const/16 v1, 0xa
+    invoke-static {v1}, LTest;->fibonacciJack(I)I
+    move-result v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    const-wide/16 v2, 0xa
+    invoke-static {v2, v3}, LTest;->fibonacciLongJack(J)J
+    move-result-wide v2
+    invoke-virtual {v0, v2, v3}, Ljava/io/PrintStream;->println(J)V
+
+    return-void
+.end method
diff --git a/src/test/smali/fill-array-data/FillArrayData.smali b/src/test/smali/fill-array-data/FillArrayData.smali
new file mode 100644
index 0000000..81a5834
--- /dev/null
+++ b/src/test/smali/fill-array-data/FillArrayData.smali
@@ -0,0 +1,61 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Simple fill array data
+.method public static test1()[I
+  .registers 2
+
+  const/4 v1, 3
+  new-array v0, v1, [I
+  fill-array-data v0, :array_data
+  return-object v0
+
+  :array_data
+  .array-data 4
+    1 2 3
+  .end array-data
+.end method
+
+# Fill array data after data
+.method public static test2()[I
+  .registers 2
+
+  goto :start
+
+  :array_data
+  .array-data 4
+    4 5 6
+  .end array-data
+
+  :start
+  const/4 v1, 3
+  new-array v0, v1, [I
+  fill-array-data v0, :array_data
+  return-object v0
+
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 2
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-static {}, LTest;->test1()[I
+    move-result-object v1
+    invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    invoke-static {}, LTest;->test2()[I
+    move-result-object v1
+    invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    return-void
+.end method
diff --git a/src/test/smali/filled-new-array/FilledNewArray.smali b/src/test/smali/filled-new-array/FilledNewArray.smali
new file mode 100644
index 0000000..2550e3f
--- /dev/null
+++ b/src/test/smali/filled-new-array/FilledNewArray.smali
@@ -0,0 +1,93 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Filled new array from arguments.
+.method public static test1(III)[I
+  .locals 1
+
+  filled-new-array {p0, p1, p2}, [I
+  move-result-object v0
+  return-object v0
+.end method
+
+# Filled new array from constants.
+.method public static test2()[I
+  .locals 3
+
+  const/4 v0, 4
+  const/4 v1, 5
+  const/4 v2, 6
+  filled-new-array {v0, v1, v2}, [I
+  move-result-object v0
+  return-object v0
+.end method
+
+# Filled new array range from arguments.
+.method public static test3(IIIIII)[I
+  .locals 1
+
+  filled-new-array/range {p0 .. p5}, [I
+  move-result-object v0
+  return-object v0
+.end method
+
+# Filled new array range from constants.
+.method public static test4()[I
+  .locals 6
+
+  const/4 v0, 6
+  const/4 v1, 5
+  const/4 v2, 4
+  const/4 v3, 3
+  const/4 v4, 2
+  const/4 v5, 1
+  filled-new-array/range {v0  .. v5}, [I
+  move-result-object v0
+  return-object v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 7
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    const/4 v1, 1
+    const/4 v2, 2
+    const/4 v3, 3
+    invoke-static {v1, v2, v3}, LTest;->test1(III)[I
+    move-result-object v1
+    invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    invoke-static {}, LTest;->test2()[I
+    move-result-object v1
+    invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 1
+    const/4 v2, 2
+    const/4 v3, 3
+    const/4 v4, 4
+    const/4 v5, 5
+    const/4 v6, 6
+    invoke-static/range {v1 .. v6}, LTest;->test3(IIIIII)[I
+    move-result-object v1
+    invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    invoke-static {}, LTest;->test4()[I
+    move-result-object v1
+    invoke-static {v1}, Ljava/util/Arrays;->toString([I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    return-void
+.end method
diff --git a/src/test/smali/infinite-loop/InfiniteLoop1.smali b/src/test/smali/infinite-loop/InfiniteLoop1.smali
new file mode 100644
index 0000000..a93923b
--- /dev/null
+++ b/src/test/smali/infinite-loop/InfiniteLoop1.smali
@@ -0,0 +1,53 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method public static loop1()V
+  .locals 0
+  goto :a
+
+  :a
+  goto :b
+
+  :b
+  goto :c
+
+  :c
+  goto :d
+
+  :d
+  goto :b
+
+  return-void
+.end method
+
+.method public static loop2()V
+  .locals 2
+  sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+  const/4 v1, 0x1
+  invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(I)V
+  goto :a
+
+  :a
+  goto :b
+
+  :b
+  goto :c
+
+  :c
+  goto :d
+
+  :d
+  goto :b
+
+  return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 0
+    return-void
+.end method
diff --git a/src/test/smali/merge-blocks-regression/Test.java b/src/test/smali/merge-blocks-regression/Test.java
new file mode 100644
index 0000000..ca61ff4
--- /dev/null
+++ b/src/test/smali/merge-blocks-regression/Test.java
@@ -0,0 +1,36 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+  public Object b;
+  public Test d;
+  public Test e;
+  public Test g;
+  public Test f;
+
+  public void a() { throw new RuntimeException("Test.a()"); }
+  public void a(long p1) { throw new RuntimeException("Test.a(long)"); }
+  public void a(long p1, boolean p2) { throw new RuntimeException("Test.a(long, boolean)"); }
+  public Test a(java.io.DataInputStream p1) { throw new RuntimeException("Test.a(input-stream)"); }
+  public Test a(byte[] p1, Test p2) { throw new RuntimeException("Test.a(B[], Test)"); }
+  public java.io.File b() { throw new RuntimeException("Test.b()"); }
+  public Test bW_() { throw new RuntimeException("Test.bW_()"); }
+  public long c() { throw new RuntimeException("Test.d()"); }
+  public void c(boolean p1) { throw new RuntimeException("Test.c(boolean)"); }
+  public void c(java.nio.ByteBuffer p1) { throw new RuntimeException("Test.c(byte-buf)"); }
+  public byte[] cB() { throw new RuntimeException("Test.cB()"); }
+  public long d() { throw new RuntimeException("Test.d()"); }
+  public void d(boolean p1) { throw new RuntimeException("Test.d(boolean)"); }
+  public Test e() { throw new RuntimeException("Test.e()"); }
+  public void eV() { throw new RuntimeException("Test.eV()"); }
+
+  public static void main(String[] args) {
+    try {
+      new TestObject().f();
+    } catch (Exception e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/merge-blocks-regression/TestObject.java b/src/test/smali/merge-blocks-regression/TestObject.java
new file mode 100644
index 0000000..59b46f6
--- /dev/null
+++ b/src/test/smali/merge-blocks-regression/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public void f() {
+  }
+}
diff --git a/src/test/smali/merge-blocks-regression/TestObject.smali b/src/test/smali/merge-blocks-regression/TestObject.smali
new file mode 100644
index 0000000..5a9ddec
--- /dev/null
+++ b/src/test/smali/merge-blocks-regression/TestObject.smali
@@ -0,0 +1,193 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field public c:LTest;
+.field public b:LTest;
+.field public d:LTest;
+.field public e:LTest;
+.field public h:LTest;
+.field public i:LTest;
+.field public j:LTest;
+
+.method public final f()V
+    .registers 15
+    const/16 v11, 0xd
+    const/4 v2, 0x0
+    const/4 v1, 0x1
+    iget-object v3, p0, LTestObject;->c:LTest;
+    iget-object v0, p0, LTestObject;->b:LTest;
+    invoke-virtual {v0}, LTest;->bW_()LTest;
+    move-result-object v0
+    iget-object v4, p0, LTestObject;->b:LTest;
+    invoke-virtual {v4}, LTest;->e()LTest;
+    move-result-object v4
+    new-instance v5, Ljava/io/File;
+    invoke-virtual {v0}, LTest;->b()Ljava/io/File;
+    move-result-object v0
+    const-string v6, "nlp_state"
+    invoke-direct {v5, v0, v6}, Ljava/io/File;-><init>(Ljava/io/File;Ljava/lang/String;)V
+    invoke-virtual {v4}, LTest;->d()J
+    move-result-wide v6
+    invoke-virtual {v4}, LTest;->c()J
+    move-result-wide v8
+    :try_start_25
+    new-instance v0, Ljava/io/FileInputStream;
+    invoke-direct {v0, v5}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V
+    new-instance v4, Ljava/io/BufferedInputStream;
+    invoke-direct {v4, v0}, Ljava/io/BufferedInputStream;-><init>(Ljava/io/InputStream;)V
+    iget-object v0, v3, LTest;->g:LTest;
+    invoke-virtual {v0}, LTest;->cB()[B
+    :try_end_34
+    .catch Ljava/io/FileNotFoundException; {:try_start_25 .. :try_end_34} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_25 .. :try_end_34} :catch_c8
+    .catch Ljava/io/IOException; {:try_start_25 .. :try_end_34} :catch_d8
+    move-result-object v0
+    :try_start_35
+    new-instance v5, Ljava/io/DataInputStream;
+    invoke-direct {v5, v4}, Ljava/io/DataInputStream;-><init>(Ljava/io/InputStream;)V
+    invoke-virtual {v5}, Ljava/io/DataInputStream;->readUnsignedShort()I
+    move-result v10
+    if-ne v10, v11, :cond_c4
+    if-ne v10, v11, :cond_b2
+    iget-object v10, v3, LTest;->f:LTest;
+    if-nez v10, :cond_4d
+    const/4 v10, 0x0
+    invoke-static {v0, v10}, LTest;->a([BLTest;)LTest;
+    move-result-object v0
+    iput-object v0, v3, LTest;->f:LTest;
+    :cond_4d
+    iget-object v0, v3, LTest;->f:LTest;
+    invoke-virtual {v0, v5}, LTest;->a(Ljava/io/DataInputStream;)LTest;
+    move-result-object v0
+    iget-object v0, v0, LTest;->b:Ljava/lang/Object;
+    check-cast v0, [B
+    invoke-static {v0}, Ljava/nio/ByteBuffer;->wrap([B)Ljava/nio/ByteBuffer;
+    :try_end_5a
+    .catch Ljava/io/IOException; {:try_start_35 .. :try_end_5a} :catch_ba
+    .catch Ljava/io/FileNotFoundException; {:try_start_35 .. :try_end_5a} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_35 .. :try_end_5a} :catch_c8
+    move-result-object v5
+    :try_start_5b
+    invoke-virtual {v5}, Ljava/nio/ByteBuffer;->getLong()J
+    move-result-wide v10
+    invoke-virtual {v5}, Ljava/nio/ByteBuffer;->getLong()J
+    move-result-wide v12
+    add-long/2addr v10, v12
+    sub-long v6, v10, v6
+    invoke-static {v8, v9, v6, v7}, Ljava/lang/Math;->min(JJ)J
+    move-result-wide v6
+    invoke-virtual {v5}, Ljava/nio/ByteBuffer;->get()B
+    move-result v0
+    if-ne v0, v1, :cond_cd
+    move v0, v1
+    :goto_71
+    invoke-virtual {v3, v6, v7, v0}, LTest;->a(JZ)V
+    :try_end_74
+    .catch Ljava/lang/IllegalArgumentException; {:try_start_5b .. :try_end_74} :catch_cf
+    .catch Ljava/nio/BufferUnderflowException; {:try_start_5b .. :try_end_74} :catch_dd
+    .catch Ljava/io/IOException; {:try_start_5b .. :try_end_74} :catch_ba
+    .catch Ljava/io/FileNotFoundException; {:try_start_5b .. :try_end_74} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_5b .. :try_end_74} :catch_c8
+    :try_start_74
+    iget-object v0, v3, LTest;->d:LTest;
+    invoke-virtual {v0, v5}, LTest;->c(Ljava/nio/ByteBuffer;)V
+    iget-object v0, v3, LTest;->e:LTest;
+    invoke-virtual {v0, v5}, LTest;->c(Ljava/nio/ByteBuffer;)V
+    :try_end_7e
+    .catch Ljava/io/IOException; {:try_start_74 .. :try_end_7e} :catch_ba
+    .catch Ljava/io/FileNotFoundException; {:try_start_74 .. :try_end_7e} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_74 .. :try_end_7e} :catch_c8
+    :goto_7e
+    :try_start_7e
+    invoke-virtual {v4}, Ljava/io/BufferedInputStream;->close()V
+    :try_end_81
+    .catch Ljava/io/FileNotFoundException; {:try_start_7e .. :try_end_81} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_7e .. :try_end_81} :catch_c8
+    .catch Ljava/io/IOException; {:try_start_7e .. :try_end_81} :catch_d8
+    :goto_81
+    iget-object v0, p0, LTestObject;->i:LTest;
+    invoke-virtual {v0, v1}, LTest;->c(Z)V
+    iget-object v0, p0, LTestObject;->i:LTest;
+    invoke-virtual {v0, v2}, LTest;->d(Z)V
+    iget-object v0, p0, LTestObject;->d:LTest;
+    if-eqz v0, :cond_94
+    iget-object v0, p0, LTestObject;->d:LTest;
+    invoke-virtual {v0}, LTest;->a()V
+    :cond_94
+    iget-object v0, p0, LTestObject;->e:LTest;
+    if-eqz v0, :cond_9d
+    iget-object v0, p0, LTestObject;->e:LTest;
+    invoke-virtual {v0}, LTest;->eV()V
+    :cond_9d
+    iget-object v0, p0, LTestObject;->h:LTest;
+    iget-object v1, p0, LTestObject;->b:LTest;
+    invoke-virtual {v1}, LTest;->e()LTest;
+    move-result-object v1
+    invoke-virtual {v1}, LTest;->c()J
+    move-result-wide v2
+    invoke-virtual {v0, v2, v3}, LTest;->a(J)V
+    iget-object v0, p0, LTestObject;->j:LTest;
+    invoke-virtual {v0}, LTest;->a()V
+    return-void
+    :cond_b2
+    :try_start_b2
+    new-instance v0, Ljava/io/IOException;
+    const-string v4, "Incompatible version."
+    invoke-direct {v0, v4}, Ljava/io/IOException;-><init>(Ljava/lang/String;)V
+    throw v0
+    :try_end_ba
+    .catch Ljava/io/IOException; {:try_start_b2 .. :try_end_ba} :catch_ba
+    .catch Ljava/io/FileNotFoundException; {:try_start_b2 .. :try_end_ba} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_b2 .. :try_end_ba} :catch_c8
+    :catch_ba
+    move-exception v0
+    :try_start_bb
+    invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+    throw v0
+    :try_end_bf
+    .catch Ljava/io/FileNotFoundException; {:try_start_bb .. :try_end_bf} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_bb .. :try_end_bf} :catch_c8
+    .catch Ljava/io/IOException; {:try_start_bb .. :try_end_bf} :catch_d8
+    :catch_bf
+    move-exception v0
+    invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+    goto :goto_81
+    :cond_c4
+    :try_start_c4
+    invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+    :try_end_c7
+    .catch Ljava/io/IOException; {:try_start_c4 .. :try_end_c7} :catch_ba
+    .catch Ljava/io/FileNotFoundException; {:try_start_c4 .. :try_end_c7} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_c4 .. :try_end_c7} :catch_c8
+    goto :goto_7e
+    :catch_c8
+    move-exception v0
+    invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+    goto :goto_81
+    :cond_cd
+    move v0, v2
+    goto :goto_71
+    :catch_cf
+    move-exception v0
+    :goto_d0
+    :try_start_d0
+    new-instance v4, Ljava/io/IOException;
+    const-string v5, "Byte buffer read failed."
+    invoke-direct {v4, v5, v0}, Ljava/io/IOException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
+    throw v4
+    :try_end_d8
+    .catch Ljava/io/IOException; {:try_start_d0 .. :try_end_d8} :catch_ba
+    .catch Ljava/io/FileNotFoundException; {:try_start_d0 .. :try_end_d8} :catch_bf
+    .catch Ljava/lang/SecurityException; {:try_start_d0 .. :try_end_d8} :catch_c8
+    :catch_d8
+    move-exception v0
+    invoke-virtual {v3, v8, v9}, LTest;->a(J)V
+    goto :goto_81
+    :catch_dd
+    move-exception v0
+    goto :goto_d0
+.end method
diff --git a/src/test/smali/multiple-returns/MultipleReturns.smali b/src/test/smali/multiple-returns/MultipleReturns.smali
new file mode 100644
index 0000000..145736b
--- /dev/null
+++ b/src/test/smali/multiple-returns/MultipleReturns.smali
@@ -0,0 +1,126 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.field static private result:Ljava/lang/String;
+
+.method public static test1(Z)Ljava/lang/String;
+    .locals 1
+    .param p0
+
+    if-eqz p0, :false_branch
+
+    const-string v0, "T"
+    return-object v0
+
+    :false_branch
+    const-string v0, "F"
+    return-object v0
+.end method
+
+.method public static test2(Z)V
+    .locals 1
+    .param p0
+
+    if-eqz p0, :false_branch
+
+    const-string v0, "t"
+    sput-object v0, LTest;->result:Ljava/lang/String;
+    return-void
+
+    :false_branch
+    const-string v0, "f"
+    sput-object v0, LTest;->result:Ljava/lang/String;
+    return-void
+.end method
+
+.method public static test3(Z)J
+    .locals 2
+    .param p0
+
+    if-eqz p0, :false_branch
+
+    const-wide v0, 0x1
+    return-wide v0
+
+    :false_branch
+    const-wide/high16 v0, 0x4000000000000000L
+    return-wide v0
+.end method
+
+.method public static test4(Z)Z
+    .locals 0
+    .param p0
+
+    if-eqz p0, :false_branch
+
+    return p0
+
+    :false_branch
+    return p0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 3
+    .param p0
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    const/4 v1, 0x1
+    invoke-static {v1}, LTest;->test1(Z)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test1(Z)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x1
+    invoke-static {v1}, LTest;->test2(Z)V
+    sget-object v1, LTest;->result:Ljava/lang/String;
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test2(Z)V
+    sget-object v1, LTest;->result:Ljava/lang/String;
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    invoke-virtual {v0}, Ljava/io/PrintStream;->println()V
+
+    const/4 v1, 0x1
+    invoke-static {v1}, LTest;->test3(Z)J
+    move-result-wide v1
+    invoke-static {v1, v2}, Ljava/lang/Long;->toString(J)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test3(Z)J
+    move-result-wide v1
+    invoke-static {v1, v2}, Ljava/lang/Long;->toString(J)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+    const/4 v1, 0x1
+    invoke-static {v1}, LTest;->test4(Z)Z
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Boolean;->toString(Z)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test4(Z)Z
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Boolean;->toString(Z)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+    return-void
+.end method
+
+
diff --git a/src/test/smali/new-instance-and-init/Test.java b/src/test/smali/new-instance-and-init/Test.java
new file mode 100644
index 0000000..a47e69d
--- /dev/null
+++ b/src/test/smali/new-instance-and-init/Test.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+  public Test(int i) {
+    System.out.println("Test(" + i + ")");
+  }
+
+  public static void main(String[] args) {
+    TestObject.allocate(0);
+    TestObject.allocate(4);
+    TestObject.allocate(10);
+  }
+}
diff --git a/src/test/smali/new-instance-and-init/TestObject.java b/src/test/smali/new-instance-and-init/TestObject.java
new file mode 100644
index 0000000..003f09d
--- /dev/null
+++ b/src/test/smali/new-instance-and-init/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public static void allocate(int i) {
+  }
+}
diff --git a/src/test/smali/new-instance-and-init/TestObject.smali b/src/test/smali/new-instance-and-init/TestObject.smali
new file mode 100644
index 0000000..665de1b
--- /dev/null
+++ b/src/test/smali/new-instance-and-init/TestObject.smali
@@ -0,0 +1,26 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.method public static allocate(I)V
+    .registers 4
+    new-instance v2, LTest;
+    if-nez v3, :cond_9
+    const/4 v1, 0x0
+    invoke-direct {v2, v1}, LTest;-><init>(I)V
+    :goto_8
+    return-void
+    :cond_9
+    const/16 v0, 0xa
+    if-ge v3, v0, :cond_14
+    const/4 v1, 0x0
+    invoke-direct {v2, v1}, LTest;-><init>(I)V
+    goto :goto_8
+    :cond_14
+    const/4 v1, 0x0
+    invoke-direct {v2, v1}, LTest;-><init>(I)V
+    goto :goto_8
+.end method
\ No newline at end of file
diff --git a/src/test/smali/overlapping-long-registers/Overlapping.smali b/src/test/smali/overlapping-long-registers/Overlapping.smali
new file mode 100644
index 0000000..167eb1b
--- /dev/null
+++ b/src/test/smali/overlapping-long-registers/Overlapping.smali
@@ -0,0 +1,53 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Some versions of dalvik had a bug where you cannot use the second half of an input long
+# as the first part of an output long. This smali code explicitly has that issue so the
+# generated dex file can be used to test art/dalvik versions.
+#
+# The issue was that if you have
+#
+#   add-long v3, v0, v2
+#
+# dalvik would add v0 and v2 and store the result in v3 before adding v1 and v3 (now clobbered).
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method static add(JJ)J
+    .locals 5
+    move-wide v0, p0
+    move-wide v2, p2
+    add-long v3, v0, v2
+    return-wide v3
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 10
+
+    sget-object v5, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    const-wide/high16 v0, 0x4000000000000000L
+    const-wide/high16 v2, 0x4100000000000000L
+    invoke-static {v0, v1, v2, v3}, LTest;->add(JJ)J
+    move-result-wide v6
+    invoke-virtual {v5, v6, v7}, Ljava/io/PrintStream;->println(J)V
+
+    # Adding loop in an attempt to get the jit to process the add method.
+    const v8, 1000000
+    const v9, 1
+    :loop
+    if-eqz v8, :exit
+    const-wide v0, 0x4000000000040000L
+    const-wide v2, 0x4100000000041000L
+    invoke-static {v0, v1, v2, v3}, LTest;->add(JJ)J
+    move-result-wide v6
+    sub-int v8, v8, v9
+    goto :loop
+
+    :exit
+    invoke-virtual {v5, v6, v7}, Ljava/io/PrintStream;->println(J)V
+    return-void
+.end method
diff --git a/src/test/smali/packed-switch/PackedSwitch.smali b/src/test/smali/packed-switch/PackedSwitch.smali
new file mode 100644
index 0000000..30ab926
--- /dev/null
+++ b/src/test/smali/packed-switch/PackedSwitch.smali
@@ -0,0 +1,106 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Empty packed switch
+.method public static test1(I)I
+  .registers 1
+  packed-switch v0, :packed_switch_data
+  const/4 v0, 0x1
+  return v0
+
+  :packed_switch_data
+  .packed-switch 0x0
+  .end packed-switch
+.end method
+
+# Empty packed switch after data
+.method public static test2(I)I
+  .registers 1
+
+  goto :packed_switch
+
+  :packed_switch_data
+  .packed-switch 0x0
+  .end packed-switch
+
+  :packed_switch
+  packed-switch v0, :packed_switch_data
+  const/4 v0, 0x2
+  return v0
+.end method
+
+# Packed switch after data
+.method public static test3(I)I
+  .registers 1
+
+  goto :packed_switch
+
+  :case_0
+  const/4 v0, 0x3
+  goto :return
+
+  :packed_switch_data
+  .packed-switch 0x0
+    :case_0
+    :case_1
+  .end packed-switch
+
+  :packed_switch
+  packed-switch v0, :packed_switch_data
+  const/4 v0, 0x5
+  goto :return
+
+  :case_1
+  const/4 v0, 0x4
+
+  :return
+  return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 2
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test1(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test2(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test3(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x1
+    invoke-static {v1}, LTest;->test3(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x2
+    invoke-static {v1}, LTest;->test3(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    return-void
+.end method
diff --git a/src/test/smali/phi-removal-regression/Test.java b/src/test/smali/phi-removal-regression/Test.java
new file mode 100644
index 0000000..7accd02
--- /dev/null
+++ b/src/test/smali/phi-removal-regression/Test.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+  boolean returnBoolean() {
+    System.out.println("returnBoolean");
+    return true;
+  }
+
+  boolean returnTheOtherBoolean(Test a) {
+    System.out.println("returnTheOtherBoolean");
+    return a.returnBoolean();
+  }
+
+  public static void main(String[] args) {
+    new TestObject().a(new Test(), 42);
+  }
+}
diff --git a/src/test/smali/phi-removal-regression/TestObject.java b/src/test/smali/phi-removal-regression/TestObject.java
new file mode 100644
index 0000000..8a93611
--- /dev/null
+++ b/src/test/smali/phi-removal-regression/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public boolean a(Test a, int i) {
+    return true;
+  }
+}
diff --git a/src/test/smali/phi-removal-regression/TestObject.smali b/src/test/smali/phi-removal-regression/TestObject.smali
new file mode 100644
index 0000000..2b5f9e0
--- /dev/null
+++ b/src/test/smali/phi-removal-regression/TestObject.smali
@@ -0,0 +1,58 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field final list:Ljava/util/ArrayList;
+.field private final s:Z
+
+.method public final a(LTest;I)Z
+    .registers 9
+    .prologue
+    const/4 v2, 0x1
+    const/4 v1, 0x0
+    invoke-virtual {p1}, LTest;->returnBoolean()Z
+    move-result v0
+    if-eqz v0, :cond_a
+    move v0, v1
+    :goto_9
+    return v0
+    :cond_a
+    and-int/lit8 v0, p2, 0x2
+    if-nez v0, :cond_14
+    iget-boolean v0, p0, LTestObject;->b:Z
+    if-eqz v0, :cond_14
+    move v0, v2
+    goto :goto_9
+    :cond_14
+    iget-object v0, p0, LTestObject;->list:Ljava/util/ArrayList;
+    invoke-virtual {v0}, Ljava/util/ArrayList;->size()I
+    move-result v4
+    move v3, v1
+    :goto_1b
+    if-ge v3, v4, :cond_3b
+    iget-object v0, p0, LTestObject;->list:Ljava/util/ArrayList;
+    invoke-virtual {v0, v3}, Ljava/util/ArrayList;->get(I)Ljava/lang/Object;
+    move-result-object v0
+    check-cast v0, LTest;
+    and-int/lit8 v5, p2, 0x1
+    if-eqz v5, :cond_2f
+    invoke-virtual {v0}, LTest;->returnBoolean()Z
+    move-result v5
+    if-nez v5, :cond_37
+    :cond_2f
+    invoke-virtual {v0, p1}, LTest;->returnTheOtherBoolean(LTest;)Z
+    move-result v0
+    if-eqz v0, :cond_37
+    move v0, v2
+    goto :goto_9
+    :cond_37
+    add-int/lit8 v0, v3, 0x1
+    move v3, v0
+    goto :goto_1b
+    :cond_3b
+    move v0, v1
+    goto :goto_9
+.end method
diff --git a/src/test/smali/regression/33336471/33336471.smali b/src/test/smali/regression/33336471/33336471.smali
new file mode 100644
index 0000000..87e9cf6
--- /dev/null
+++ b/src/test/smali/regression/33336471/33336471.smali
@@ -0,0 +1,204 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# This method is not called by the test, and is only for reference of dex code failing on
+# art from Android 5.1.1 with mixed int/float constants.
+# When run dex2oat prints the warning:
+#
+# dex2oat W  7568  7571 art/compiler/dex/vreg_analysis.cc:367]
+# void Test.intAndFloatZeroConstantsNotWorking() op at block 6 has both fp and core/ref uses for
+# same def.
+.method static intAndFloatZeroConstantsNotWorking()V
+    .locals 8
+
+    const-string        v6, "START"
+    sget-object         v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual      {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    const/high16        v0, 0x3f800000  # 1.0
+    const/4             v1, 0x00  # 0 / 0.0
+    const/4             v3, 2
+    move                v4, v1
+
+  :label_a
+    invoke-virtual      {v7, v4}, Ljava/io/PrintStream;->println(I)V
+    invoke-virtual      {v7, v3}, Ljava/io/PrintStream;->println(I)V
+    if-ge               v4, v3, :label_b
+    const-string        v6, "LOOP"
+    invoke-virtual      {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+    add-int/lit8        v4, v4, 0x01  # 1
+    goto                :label_a
+
+  :label_b
+    const/4             v5, 0x01
+    new-array           v5, v5, [F
+    const/4             v3, 0x00
+    aget                v4, v5, v3
+    cmpl-float          v1, v4, v1
+    if-nez              v1, :label_c
+    cmpl-float          v0, v4, v0
+    if-eqz              v0, :label_c
+    const-string        v0, "DONE"
+    invoke-virtual      {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+    goto                :label_d
+
+  :label_c
+    const-string        v0, "FLOAT COMPARISON FAILED"
+    invoke-virtual      {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+  :label_d
+    return-void
+.end method
+
+.method static intAndFloatZeroConstants()V
+    .locals 8
+
+    const-string        v6, "START"
+    sget-object         v7, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual      {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    const/high16        v0, 0x3f800000  # 1.0
+    const/4             v1, 0x00  # 0
+    const/4             v2, 0x00  # 0.0
+    const/4             v3, 2
+    move                v4, v1
+
+  :label_a
+    invoke-virtual      {v7, v4}, Ljava/io/PrintStream;->println(I)V
+    invoke-virtual      {v7, v3}, Ljava/io/PrintStream;->println(I)V
+    if-ge               v4, v3, :label_b
+    const-string        v6, "LOOP"
+    invoke-virtual      {v7, v6}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+    add-int/lit8        v4, v4, 0x01  # 1
+    goto                :label_a
+
+  :label_b
+    const/4             v5, 0x01
+    new-array           v5, v5, [F
+    const/4             v3, 0x00
+    aget                v4, v5, v3
+    cmpl-float          v1, v4, v2
+    if-nez              v1, :label_c
+    cmpl-float          v0, v4, v0
+    if-eqz              v0, :label_c
+    const-string        v0, "DONE"
+    invoke-virtual      {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+    goto                :label_d
+
+  :label_c
+    const-string        v0, "FLOAT COMPARISON FAILED"
+    invoke-virtual      {v7, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+  :label_d
+    return-void
+.end method
+
+# This method is not called by the test, and is only for reference of dex code failing on
+# art from Android 5.1.1 with mixed long/double constants.
+# This code does actually work, but dex2oat still prints the warning:
+#
+# dex2oat W  7568  7571 art/compiler/dex/vreg_analysis.cc:367]
+# void Test.longAndDoubleZeroConstantsNotWorking() op at block 6 has both fp and core/ref uses
+#for same def.
+.method static longAndDoubleZeroConstantsNotWorking()V
+    .locals 14
+
+    const-string        v12, "START"
+    sget-object         v13, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual      {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    const-wide/high16   v0, 0x3f80000000000000L  # 1.0 0->0 1->2 2->4 3->6 4->8 5->10+11 6->12 7->13
+    const-wide/16       v2, 0x00L  # 0 / 0.0
+    const-wide/16       v6, 2
+    move-wide           v8, v2
+
+  :label_a
+    invoke-virtual      {v13, v8, v9}, Ljava/io/PrintStream;->println(J)V
+    invoke-virtual      {v13, v6, v7}, Ljava/io/PrintStream;->println(J)V
+    cmp-long            v12, v8, v6
+    if-gez              v12, :label_b
+    const-string        v12, "LOOP"
+    invoke-virtual      {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+    const-wide/16       v10, 0x01
+    add-long            v8, v8, v10
+    goto                :label_a
+
+  :label_b
+    const/4             v10, 0x01
+    new-array           v10, v10, [D
+    const/4             v6, 0x00
+    aget-wide           v8, v10, v6
+    cmpl-double         v2, v8, v2
+    if-nez              v2, :label_c
+    cmpl-double         v0, v8, v0
+    if-eqz              v0, :label_c
+    const-string        v0, "DONE"
+    invoke-virtual      {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+    goto                :label_d
+
+  :label_c
+    const-string        v0, "FLOAT COMPARISON FAILED"
+    invoke-virtual      {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+  :label_d
+    return-void
+.end method
+
+.method static longAndDoubleZeroConstants()V
+    .locals 14
+
+    const-string        v12, "START"
+    sget-object         v13, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual      {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    const-wide/high16   v0, 0x3f80000000000000L
+    const-wide/16       v2, 0x00L  # 0
+    const-wide/16       v4, 0x00L  # 0.0
+    const-wide/16       v6, 2
+    move-wide           v8, v2
+
+  :label_a
+    invoke-virtual      {v13, v8, v9}, Ljava/io/PrintStream;->println(J)V
+    invoke-virtual      {v13, v6, v7}, Ljava/io/PrintStream;->println(J)V
+    cmp-long            v12, v8, v6
+    if-gez              v12, :label_b
+    const-string        v12, "LOOP"
+    invoke-virtual      {v13, v12}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+    const-wide/16       v10, 0x01
+    add-long            v8, v8, v10
+    goto                :label_a
+
+  :label_b
+    const/4             v10, 0x01
+    new-array           v10, v10, [D
+    const/4             v6, 0x00
+    aget-wide           v8, v10, v6
+    cmpl-double         v2, v8, v4
+    if-nez              v2, :label_c
+    cmpl-double         v0, v8, v0
+    if-eqz              v0, :label_c
+    const-string        v0, "DONE"
+    invoke-virtual      {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+    goto                :label_d
+
+  :label_c
+    const-string        v0, "DOUBLE COMPARISON FAILED"
+    invoke-virtual      {v13, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+  :label_d
+    return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 0
+
+    invoke-static {}, LTest;->intAndFloatZeroConstants()V
+    invoke-static {}, LTest;->longAndDoubleZeroConstants()V
+
+    return-void
+.end method
diff --git a/src/test/smali/regression/33846227/33846227.smali b/src/test/smali/regression/33846227/33846227.smali
new file mode 100644
index 0000000..c8553a7
--- /dev/null
+++ b/src/test/smali/regression/33846227/33846227.smali
@@ -0,0 +1,36 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Before Lollipop (Art 5.1.1) Art/Dalvik failed on verification if an empty sparse switch payload
+# was the last instruction in a method. This was originally reported as 19827056, and fixed in
+# https://android.googlesource.com/platform/art/+/9ccd151d0d27a729f88af9d00285afe4d147981a
+
+# This test is copied from
+# https://android.googlesource.com/platform/art/+/9ccd151d0d27a729f88af9d00285afe4d147981a
+.class public LTest;
+.super Ljava/lang/Object;
+
+.method public static run()V
+    .registers 2
+
+    :start
+    const v0, 0
+
+    sparse-switch v0, :SparseSwitch
+
+    return-void
+
+    :SparseSwitch
+    .sparse-switch
+    .end sparse-switch
+
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 0
+
+    invoke-static {}, LTest;->run()V
+
+    return-void
+.end method
diff --git a/src/test/smali/self-is-catch-block/SelfIsCatchBlock.smali b/src/test/smali/self-is-catch-block/SelfIsCatchBlock.smali
new file mode 100644
index 0000000..04f0cf1
--- /dev/null
+++ b/src/test/smali/self-is-catch-block/SelfIsCatchBlock.smali
@@ -0,0 +1,43 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method private static throwOnPositive(I)V
+    .registers 2
+    if-lez v1, :cond_nothrow
+    new-instance v0, Ljava/lang/RuntimeException;
+    invoke-direct {v0}, Ljava/lang/RuntimeException;-><init>()V
+    throw v0
+    :cond_nothrow
+    return-void
+.end method
+
+# Tests the flow of values in the pathological case that the block is its own catch handler.
+# This tests that the register allocator does not insert moves at the end of the throwing
+# block since in the case of a throw the block does not actually complete.
+.method static loopWhileThrow(I)I
+    .registers 4
+    :catchall_0
+    move v0, p0
+    add-int/lit8 p0, p0, -0x1
+    :try_start_6
+    invoke-static {v0}, LTest;->throwOnPositive(I)V
+    :try_end_9
+    .catchall {:try_start_6 .. :try_end_9} :catchall_0
+    return p0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .registers 2
+    const v0, 0x64
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
+    invoke-static {v0}, LTest;->loopWhileThrow(I)I
+    move-result v0
+    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V
+    return-void
+.end method
diff --git a/src/test/smali/sparse-switch/SparseSwitch.smali b/src/test/smali/sparse-switch/SparseSwitch.smali
new file mode 100644
index 0000000..908220e
--- /dev/null
+++ b/src/test/smali/sparse-switch/SparseSwitch.smali
@@ -0,0 +1,106 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+# Empty sparse switch
+.method public static test1(I)I
+  .registers 1
+  sparse-switch v0, :sparse_switch_data
+  const/4 v0, 0x1
+  return v0
+
+  :sparse_switch_data
+  .sparse-switch
+  .end sparse-switch
+.end method
+
+# Empty sparse switch after data
+.method public static test2(I)I
+  .registers 1
+
+  goto :sparse_switch
+
+  :sparse_switch_data
+  .sparse-switch
+  .end sparse-switch
+
+  :sparse_switch
+  sparse-switch v0, :sparse_switch_data
+  const/4 v0, 0x2
+  return v0
+.end method
+
+# Sparse switch after data
+.method public static test3(I)I
+  .registers 1
+
+  goto :sparse_switch
+
+  :case_2
+  const/4 v0, 0x3
+  goto :return
+
+  :sparse_switch_data
+  .sparse-switch
+    0x2 -> :case_2
+    0x4 -> :case_4
+  .end sparse-switch
+
+  :sparse_switch
+  sparse-switch v0, :sparse_switch_data
+  const/4 v0, 0x5
+  goto :return
+
+  :case_4
+  const/4 v0, 0x4
+
+  :return
+  return v0
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 2
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test1(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x0
+    invoke-static {v1}, LTest;->test2(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x2
+    invoke-static {v1}, LTest;->test3(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x4
+    invoke-static {v1}, LTest;->test3(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    const/4 v1, 0x6
+    invoke-static {v1}, LTest;->test3(I)I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    return-void
+.end method
diff --git a/src/test/smali/try-catch/TryCatch.smali b/src/test/smali/try-catch/TryCatch.smali
new file mode 100644
index 0000000..2becd5f
--- /dev/null
+++ b/src/test/smali/try-catch/TryCatch.smali
@@ -0,0 +1,73 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class LTest;
+.super Ljava/lang/Object;
+
+# Fall through to catch block.
+.method public test1()V
+    .registers 1
+    :try_start
+    monitor-enter p0
+    monitor-exit  p0
+    :try_end
+    return-void
+    .catchall {:try_start .. :try_end} :try_end
+.end method
+
+.method public static test2()I
+    .locals 1
+    const v0, 0
+    :try_start
+    const v0, 1
+    goto :return
+    :try_end
+    .catch Ljava/lang/Exception; {:try_start .. :try_end} :return
+    .catch Ljava/lang/Throwable; {:try_start .. :try_end} :error
+    :error
+    move-exception v0
+    const v0, 2
+    :return
+    return v0
+.end method
+
+# Dead catch block.
+.method public test3()I
+    .locals 1
+    const v0, 0
+    return v0
+    :start
+    nop
+    :end
+    .catchall {:start .. :end} :catch
+    nop
+    :catch
+    nop
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 0
+    return-void
+.end method
+
+.method public test4(I)V
+    .locals 1
+    const/4 v0, 0
+    if-nez p0, :not_zero
+    const/4 v0, 1
+    goto :try_end
+    :not_zero
+    const/4 v0, 2
+    :try_start
+    invoke-static {}, Ltest/X;->f()V
+    const/4 v0, 3
+    :try_end
+    return-void
+    .catchall {:try_start .. :try_end} :try_end
+.end method
+
+.method public f()V
+    .locals 0
+    return-void
+.end method
diff --git a/src/test/smali/type-confusion-regression/Test.java b/src/test/smali/type-confusion-regression/Test.java
new file mode 100644
index 0000000..ef41f6b
--- /dev/null
+++ b/src/test/smali/type-confusion-regression/Test.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Test extends Throwable {
+  public long[] al = { 1, 2, 3 };
+  public int[] ai = null;
+  public boolean b = false;
+  public List a = new ArrayList();
+  public long l = 1 << 53;
+  public int i = 32;
+  public double d = 0.123;
+  public Test h = null;
+
+  public Test(int i) {
+    throw new RuntimeException("Test(i)");
+  }
+
+  public Test(int i, int j) {
+    throw new RuntimeException("Test.<init>(II)");
+  }
+
+  public Test(String s) {
+    throw new RuntimeException("Test.<init>(Ljava/lang/String;)");
+  }
+
+  public Test() {
+    throw new RuntimeException("Test.<init>()");
+  }
+
+  public static int a(Test t) {
+    throw new RuntimeException("Test.a(Test)");
+  }
+
+  public Test e() {
+    throw new RuntimeException("Test.e()");
+  }
+
+  public static void main(String[] args) {
+    try {
+      TestObject.a(new Test(), new Test(), new Test(), new Test());
+    } catch (RuntimeException e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/type-confusion-regression/TestObject.java b/src/test/smali/type-confusion-regression/TestObject.java
new file mode 100644
index 0000000..ee66c35
--- /dev/null
+++ b/src/test/smali/type-confusion-regression/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public static void a(Test a, Test b, Test c, Test d) {
+  }
+}
diff --git a/src/test/smali/type-confusion-regression/TestObject.smali b/src/test/smali/type-confusion-regression/TestObject.smali
new file mode 100644
index 0000000..832cab9
--- /dev/null
+++ b/src/test/smali/type-confusion-regression/TestObject.smali
@@ -0,0 +1,385 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.method public static a(LTest;LTest;LTest;LTest;)V
+    .registers 40
+    .prologue
+    invoke-virtual/range {p2 .. p2}, LTest;->e()LTest;
+    move-result-object v2
+    iget-wide v0, v2, LTest;->l:J
+    move-wide/from16 v22, v0
+    invoke-virtual/range {p2 .. p2}, LTest;->e()LTest;
+    move-result-object v2
+    iget-wide v2, v2, LTest;->l:J
+    const-wide/16 v4, 0x3e8
+    mul-long/2addr v2, v4
+    const-wide/16 v4, 0x3e8
+    mul-long/2addr v2, v4
+    div-long v2, v2, v22
+    move-object/from16 v0, p0
+    iput-wide v2, v0, LTest;->l:J
+    invoke-virtual/range {p1 .. p1}, LTest;->e()LTest;
+    move-result-object v2
+    iget-wide v4, v2, LTest;->d:D
+    double-to-int v3, v4
+    move-object/from16 v0, p0
+    iput v3, v0, LTest;->i:I
+    iget-wide v4, v2, LTest;->d:D
+    double-to-int v3, v4
+    move-object/from16 v0, p0
+    iput v3, v0, LTest;->i:I
+    iget-object v2, v2, LTest;->h:LTest;
+    invoke-static {v2}, LTest;->a(LTest;)I
+    move-result v2
+    move-object/from16 v0, p0
+    iput v2, v0, LTest;->i:I
+    const/4 v3, 0x0
+    invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+    move-result-object v2
+    if-eqz v2, :cond_59
+    invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+    move-result-object v2
+    iget-object v2, v2, LTest;->a:Ljava/util/List;
+    invoke-interface {v2}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+    move-result-object v4
+    :cond_47
+    invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z
+    move-result v2
+    if-eqz v2, :cond_59
+    invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+    move-result-object v2
+    check-cast v2, LTest;
+    iget v2, v2, LTest;->i:I
+    if-eqz v2, :cond_47
+    const/4 v2, 0x1
+    move v3, v2
+    :cond_59
+    move-object/from16 v0, p0
+    iput-boolean v3, v0, LTest;->b:Z
+    invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+    move-result-object v2
+    iget-object v0, v2, LTest;->a:Ljava/util/List;
+    move-object/from16 v17, v0
+    const/4 v2, 0x0
+    invoke-interface/range {v17 .. v17}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+    move-result-object v5
+    move v4, v2
+    :goto_6b
+    invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z
+    move-result v2
+    if-eqz v2, :cond_8c
+    invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+    move-result-object v2
+    check-cast v2, LTest;
+    iget-wide v6, v2, LTest;->l:J
+    const-wide/16 v8, 0x0
+    cmp-long v2, v6, v8
+    if-gez v2, :cond_87
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_87
+    int-to-long v8, v4
+    add-long/2addr v6, v8
+    long-to-int v2, v6
+    move v4, v2
+    goto :goto_6b
+    :cond_8c
+    if-gtz v4, :cond_96
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_96
+    invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+    move-result-object v5
+    const/4 v2, 0x0
+    if-eqz v5, :cond_260
+    iget-object v2, v5, LTest;->al:[J
+    if-eqz v2, :cond_a4
+    array-length v5, v2
+    if-nez v5, :cond_ac
+    :cond_a4
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_ac
+    array-length v5, v2
+    add-int/lit8 v5, v5, -0x1
+    aget-wide v6, v2, v5
+    int-to-long v8, v4
+    cmp-long v5, v6, v8
+    if-lez v5, :cond_be
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_be
+    move-object v5, v2
+    :goto_bf
+    const/4 v2, 0x0
+    const/4 v6, 0x0
+    invoke-virtual/range {p3 .. p3}, LTest;->e()LTest;
+    move-result-object v7
+    if-eqz v7, :cond_f2
+    iget-object v7, v7, LTest;->a:Ljava/util/List;
+    invoke-interface {v7}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+    move-result-object v8
+    move v6, v2
+    :goto_ce
+    invoke-interface {v8}, Ljava/util/Iterator;->hasNext()Z
+    move-result v2
+    if-eqz v2, :cond_f0
+    invoke-interface {v8}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+    move-result-object v2
+    check-cast v2, LTest;
+    iget v2, v2, LTest;->i:I
+    int-to-long v10, v2
+    const-wide/16 v12, 0x0
+    cmp-long v2, v10, v12
+    if-gez v2, :cond_eb
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_eb
+    int-to-long v12, v6
+    add-long/2addr v10, v12
+    long-to-int v2, v10
+    move v6, v2
+    goto :goto_ce
+    :cond_f0
+    move v2, v6
+    move-object v6, v7
+    :cond_f2
+    if-eqz v2, :cond_fe
+    if-eq v2, v4, :cond_fe
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_fe
+    if-eqz v5, :cond_149
+    new-instance v2, LTest;
+    array-length v7, v5
+    invoke-direct {v2, v4, v7}, LTest;-><init>(II)V
+    move-object v4, v2
+    :goto_107
+    iget-object v0, v4, LTest;->al:[J
+    move-object/from16 v24, v0
+    iget-object v0, v4, LTest;->ai:[I
+    move-object/from16 v25, v0
+    const/16 v16, 0x0
+    const/4 v7, -0x1
+    const-wide/16 v14, 0x0
+    if-eqz v6, :cond_150
+    invoke-interface {v6}, Ljava/util/List;->size()I
+    move-result v2
+    if-lez v2, :cond_150
+    invoke-interface {v6}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+    move-result-object v2
+    move-object v6, v2
+    :goto_121
+    const-wide/16 v12, 0x0
+    const-wide/16 v10, 0x0
+    const-wide/16 v8, 0x0
+    invoke-interface/range {v17 .. v17}, Ljava/util/List;->iterator()Ljava/util/Iterator;
+    move-result-object v26
+    :cond_12b
+    invoke-interface/range {v26 .. v26}, Ljava/util/Iterator;->hasNext()Z
+    move-result v2
+    if-eqz v2, :cond_22d
+    invoke-interface/range {v26 .. v26}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+    move-result-object v2
+    check-cast v2, LTest;
+    iget-wide v0, v2, LTest;->l:J
+    move-wide/from16 v28, v0
+    const-wide/16 v18, 0x0
+    cmp-long v17, v28, v18
+    if-gez v17, :cond_153
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_149
+    new-instance v2, LTest;
+    invoke-direct {v2, v4}, LTest;-><init>(I)V
+    move-object v4, v2
+    goto :goto_107
+    :cond_150
+    const/4 v2, 0x0
+    move-object v6, v2
+    goto :goto_121
+    :cond_153
+    iget-wide v0, v2, LTest;->l:J
+    move-wide/from16 v18, v0
+    move-wide/from16 v20, v18
+    :goto_159
+    const-wide/16 v18, 0x0
+    cmp-long v2, v20, v18
+    if-lez v2, :cond_12b
+    if-eqz v6, :cond_192
+    move-wide/from16 v18, v12
+    :goto_163
+    const-wide/16 v12, 0x0
+    cmp-long v2, v18, v12
+    if-gtz v2, :cond_178
+    invoke-interface {v6}, Ljava/util/Iterator;->next()Ljava/lang/Object;
+    move-result-object v2
+    check-cast v2, LTest;
+    iget v10, v2, LTest;->i:I
+    int-to-long v0, v10
+    move-wide/from16 v18, v0
+    iget v2, v2, LTest;->i:I
+    int-to-long v10, v2
+    goto :goto_163
+    :cond_178
+    if-nez v16, :cond_17b
+    move-wide v8, v10
+    :cond_17b
+    add-long v12, v14, v10
+    sub-long/2addr v12, v8
+    move-wide/from16 v34, v12
+    move-wide v12, v10
+    move-wide v10, v8
+    move-wide/from16 v8, v34
+    :goto_184
+    const-wide/16 v30, 0x0
+    cmp-long v2, v8, v30
+    if-gez v2, :cond_198
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_192
+    move-wide/from16 v18, v12
+    move-wide v12, v10
+    move-wide v10, v8
+    move-wide v8, v14
+    goto :goto_184
+    :cond_198
+    const-wide/16 v30, 0x3e8
+    mul-long v8, v8, v30
+    const-wide/16 v30, 0x3e8
+    mul-long v8, v8, v30
+    div-long v8, v8, v22
+    move/from16 v2, v16
+    :goto_1a4
+    if-lez v2, :cond_1cb
+    add-int/lit8 v17, v2, -0x1
+    aget-wide v30, v24, v17
+    cmp-long v17, v30, v8
+    if-lez v17, :cond_1cb
+    add-int/lit8 v17, v2, -0x1
+    aget-wide v30, v24, v17
+    aput-wide v30, v24, v2
+    if-eqz v25, :cond_1c8
+    if-ltz v7, :cond_1c8
+    add-int/lit8 v17, v2, -0x1
+    aget v27, v25, v7
+    move/from16 v0, v17
+    move/from16 v1, v27
+    if-ne v0, v1, :cond_1c8
+    aget v17, v25, v7
+    add-int/lit8 v17, v17, 0x1
+    aput v17, v25, v7
+    :cond_1c8
+    add-int/lit8 v2, v2, -0x1
+    goto :goto_1a4
+    :cond_1cb
+    aput-wide v8, v24, v2
+    if-lez v2, :cond_1ea
+    add-int/lit8 v17, v2, -0x1
+    aget-wide v30, v24, v17
+    cmp-long v8, v30, v8
+    if-nez v8, :cond_1ea
+    const/4 v3, 0x1
+    if-ne v2, v3, :cond_1e2
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_1e2
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_1ea
+    if-eqz v5, :cond_216
+    add-int/lit8 v8, v7, 0x1
+    array-length v9, v5
+    if-ge v8, v9, :cond_216
+    move/from16 v0, v16
+    int-to-long v8, v0
+    add-int/lit8 v17, v7, 0x1
+    aget-wide v30, v5, v17
+    const-wide/16 v32, 0x1
+    sub-long v30, v30, v32
+    cmp-long v8, v8, v30
+    if-nez v8, :cond_216
+    add-int/lit8 v7, v7, 0x1
+    aput v2, v25, v7
+    if-lez v7, :cond_216
+    add-int/lit8 v2, v7, -0x1
+    aget v2, v25, v2
+    aget v8, v25, v7
+    if-lt v2, v8, :cond_216
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_216
+    add-int/lit8 v2, v16, 0x1
+    add-long v16, v14, v28
+    const-wide/16 v8, 0x1
+    sub-long v14, v18, v8
+    const-wide/16 v8, 0x1
+    sub-long v8, v20, v8
+    move-wide/from16 v20, v8
+    move-wide v8, v10
+    move-wide v10, v12
+    move-wide v12, v14
+    move-wide/from16 v14, v16
+    move/from16 v16, v2
+    goto/16 :goto_159
+    :cond_22d
+    iget-object v2, v4, LTest;->al:[J
+    move-object/from16 v0, p0
+    iput-object v2, v0, LTest;->al:[J
+    iget-object v2, v4, LTest;->ai:[I
+    if-eqz v3, :cond_241
+    if-nez v2, :cond_241
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_241
+    if-eqz v2, :cond_25b
+    array-length v3, v2
+    if-gtz v3, :cond_24e
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_24e
+    const/4 v3, 0x0
+    aget v3, v2, v3
+    if-eqz v3, :cond_25b
+    new-instance v2, LTest;
+    const-string v3, "string"
+    invoke-direct {v2, v3}, LTest;-><init>(Ljava/lang/String;)V
+    throw v2
+    :cond_25b
+    move-object/from16 v0, p0
+    iput-object v2, v0, LTest;->ai:[I
+    return-void
+    :cond_260
+    move-object v5, v2
+    goto/16 :goto_bf
+.end method
\ No newline at end of file
diff --git a/src/test/smali/type-confusion-regression2/Test.java b/src/test/smali/type-confusion-regression2/Test.java
new file mode 100644
index 0000000..4caa81b
--- /dev/null
+++ b/src/test/smali/type-confusion-regression2/Test.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+  public static void main(String[] args) {
+    try {
+      new TestObject().b();
+    } catch (Exception e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/type-confusion-regression2/TestObject.java b/src/test/smali/type-confusion-regression2/TestObject.java
new file mode 100644
index 0000000..677b429
--- /dev/null
+++ b/src/test/smali/type-confusion-regression2/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public void b() {
+  }
+}
diff --git a/src/test/smali/type-confusion-regression2/TestObject.smali b/src/test/smali/type-confusion-regression2/TestObject.smali
new file mode 100644
index 0000000..8bac9e0
--- /dev/null
+++ b/src/test/smali/type-confusion-regression2/TestObject.smali
@@ -0,0 +1,187 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field private static final l:[I
+
+.field public a:I
+.field public b:I
+.field public c:[I
+.field public d:[D
+.field public e:I
+.field public f:I
+.field public g:[D
+.field public h:[D
+.field public i:[D
+.field public j:I
+.field private k:[D
+
+.method public final b()V
+    .registers 29
+    move-object/from16 v0, p0
+    iget v2, v0, LTestObject;->a:I
+    const/4 v3, 0x1
+    if-ne v2, v3, :cond_8
+    :cond_7
+    return-void
+    :cond_8
+    move-object/from16 v0, p0
+    iget v2, v0, LTestObject;->a:I
+    mul-int/lit8 v12, v2, 0x2
+    const/4 v5, 0x0
+    move-object/from16 v0, p0
+    iget v2, v0, LTestObject;->a:I
+    const/4 v3, 0x0
+    const/4 v4, 0x0
+    :goto_15
+    add-int/lit8 v6, v4, 0x1
+    const/4 v4, 0x4
+    if-gt v6, v4, :cond_55
+    sget-object v4, LTestObject;->l:[I
+    add-int/lit8 v5, v6, -0x1
+    aget v4, v4, v5
+    move v5, v4
+    move v4, v3
+    :goto_22
+    div-int v3, v2, v5
+    mul-int v7, v5, v3
+    sub-int v7, v2, v7
+    if-nez v7, :cond_f9
+    add-int/lit8 v2, v4, 0x1
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->g:[D
+    add-int/lit8 v7, v2, 0x1
+    add-int/2addr v7, v12
+    int-to-double v8, v5
+    aput-wide v8, v4, v7
+    const/4 v4, 0x2
+    if-ne v5, v4, :cond_64
+    const/4 v4, 0x1
+    if-eq v2, v4, :cond_64
+    const/4 v4, 0x2
+    :goto_3d
+    if-gt v4, v2, :cond_5a
+    sub-int v7, v2, v4
+    add-int/lit8 v7, v7, 0x2
+    add-int/2addr v7, v12
+    move-object/from16 v0, p0
+    iget-object v8, v0, LTestObject;->g:[D
+    add-int/lit8 v9, v7, 0x1
+    move-object/from16 v0, p0
+    iget-object v10, v0, LTestObject;->g:[D
+    aget-wide v10, v10, v7
+    aput-wide v10, v8, v9
+    add-int/lit8 v4, v4, 0x1
+    goto :goto_3d
+    :cond_55
+    add-int/lit8 v4, v5, 0x2
+    move v5, v4
+    move v4, v3
+    goto :goto_22
+    :cond_5a
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->g:[D
+    add-int/lit8 v7, v12, 0x2
+    const-wide/high16 v8, 0x4000000000000000L
+    aput-wide v8, v4, v7
+    :cond_64
+    const/4 v4, 0x1
+    if-ne v3, v4, :cond_f5
+    move-object/from16 v0, p0
+    iget-object v3, v0, LTestObject;->g:[D
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->a:I
+    int-to-double v4, v4
+    aput-wide v4, v3, v12
+    move-object/from16 v0, p0
+    iget-object v3, v0, LTestObject;->g:[D
+    add-int/lit8 v4, v12, 0x1
+    int-to-double v6, v2
+    aput-wide v6, v3, v4
+    const-wide v4, 0x401921fb54442d18L
+    move-object/from16 v0, p0
+    iget v3, v0, LTestObject;->a:I
+    int-to-double v6, v3
+    div-double v14, v4, v6
+    const/4 v6, 0x0
+    add-int/lit8 v13, v2, -0x1
+    const/4 v2, 0x1
+    if-eqz v13, :cond_7
+    const/4 v3, 0x1
+    move v9, v2
+    move v11, v3
+    :goto_90
+    if-gt v11, v13, :cond_7
+    move-object/from16 v0, p0
+    iget-object v2, v0, LTestObject;->g:[D
+    add-int/lit8 v3, v11, 0x1
+    add-int/2addr v3, v12
+    aget-wide v2, v2, v3
+    double-to-int v3, v2
+    const/4 v2, 0x0
+    mul-int v10, v9, v3
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->a:I
+    div-int v16, v4, v10
+    add-int/lit8 v17, v3, -0x1
+    const/4 v3, 0x1
+    move v8, v3
+    :goto_a9
+    move/from16 v0, v17
+    if-gt v8, v0, :cond_f0
+    add-int v7, v2, v9
+    int-to-double v2, v7
+    mul-double v18, v2, v14
+    const-wide/16 v4, 0x0
+    const/4 v2, 0x3
+    move v3, v6
+    :goto_b6
+    move/from16 v0, v16
+    if-gt v2, v0, :cond_e9
+    add-int/lit8 v3, v3, 0x2
+    const-wide/high16 v20, 0x3ff0000000000000L
+    add-double v4, v4, v20
+    mul-double v20, v4, v18
+    move-object/from16 v0, p0
+    iget v0, v0, LTestObject;->a:I
+    move/from16 v22, v0
+    add-int v22, v22, v3
+    move-object/from16 v0, p0
+    iget-object v0, v0, LTestObject;->g:[D
+    move-object/from16 v23, v0
+    add-int/lit8 v24, v22, -0x2
+    invoke-static/range {v20 .. v21}, Ljava/lang/Math;->cos(D)D
+    move-result-wide v26
+    aput-wide v26, v23, v24
+    move-object/from16 v0, p0
+    iget-object v0, v0, LTestObject;->g:[D
+    move-object/from16 v23, v0
+    add-int/lit8 v22, v22, -0x1
+    invoke-static/range {v20 .. v21}, Ljava/lang/Math;->sin(D)D
+    move-result-wide v20
+    aput-wide v20, v23, v22
+    add-int/lit8 v2, v2, 0x2
+    goto :goto_b6
+    :cond_e9
+    add-int v6, v6, v16
+    add-int/lit8 v2, v8, 0x1
+    move v8, v2
+    move v2, v7
+    goto :goto_a9
+    :cond_f0
+    add-int/lit8 v2, v11, 0x1
+    move v9, v10
+    move v11, v2
+    goto :goto_90
+    :cond_f5
+    move v4, v2
+    move v2, v3
+    goto/16 :goto_22
+    :cond_f9
+    move v3, v4
+    move v4, v6
+    goto/16 :goto_15
+.end method
\ No newline at end of file
diff --git a/src/test/smali/type-confusion-regression3/Test.java b/src/test/smali/type-confusion-regression3/Test.java
new file mode 100644
index 0000000..0f1f0f2
--- /dev/null
+++ b/src/test/smali/type-confusion-regression3/Test.java
@@ -0,0 +1,74 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test extends Throwable {
+  public byte[] a;
+
+  public Test(String s) {
+    throw new RuntimeException("Test(Ljava/lang/String;");
+  }
+
+  public Test(int a, Test b) {
+    throw new RuntimeException("Test(ILTest;)");
+  }
+
+  public Test() {
+  }
+
+  public long c() {
+    throw new RuntimeException("Test.c()");
+  }
+
+  public Object valueAt(int i) {
+    throw new RuntimeException("Test.valueAt(I)");
+  }
+
+  public void b(int i) {
+    throw new RuntimeException("Test.b(I)");
+  }
+
+  public void b(byte[] a, int b, int c) {
+    throw new RuntimeException("Test.b([BII)");
+  }
+
+  public void a() {
+    throw new RuntimeException("Test.a()");
+  }
+
+  public void a(Test a) {
+    throw new RuntimeException("Test.a(LTest;)");
+  }
+
+  public void a(Test a, int i) {
+    throw new RuntimeException("Test.a(LTest;I)");
+  }
+
+  public boolean a(byte[] a, int b, int c, boolean d) {
+    throw new RuntimeException("Test.a");
+  }
+
+  public void a(long a, int b, int c, int d, byte[] e) {
+    throw new RuntimeException("Test.a(JIII[B)");
+  }
+
+  public void c(int i) {
+    throw new RuntimeException("Test.c(I)");
+  }
+
+  public int n() {
+    throw new RuntimeException("Test.n()");
+  }
+
+  public int size() {
+    throw new RuntimeException("Test.size");
+  }
+
+  public static void main(String[] args) {
+    try {
+      new TestObject().a(new Test(), new Test());
+    } catch (Exception e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/type-confusion-regression3/TestObject.java b/src/test/smali/type-confusion-regression3/TestObject.java
new file mode 100644
index 0000000..f20168c
--- /dev/null
+++ b/src/test/smali/type-confusion-regression3/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public int a(Test a, Test b) {
+    return 0;
+  }
+}
diff --git a/src/test/smali/type-confusion-regression3/TestObject.smali b/src/test/smali/type-confusion-regression3/TestObject.smali
new file mode 100644
index 0000000..ce33419
--- /dev/null
+++ b/src/test/smali/type-confusion-regression3/TestObject.smali
@@ -0,0 +1,918 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field private i:I
+.field private o:LTest;
+
+.method public final a(LTest;LTest;)I
+    .registers 28
+    .prologue
+    :cond_0
+    :goto_0
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    packed-switch v4, :pswitch_data_60a
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    const/4 v5, 0x3
+    if-ne v4, v5, :cond_4ac
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    if-nez v4, :cond_431
+    move-object/from16 v0, p0
+    iget-object v11, v0, LTestObject;->o:LTest;
+    const/4 v5, 0x0
+    const-wide v8, 0x7fffffffffffffffL
+    invoke-virtual {v11}, LTest;->size()I
+    move-result v12
+    const/4 v4, 0x0
+    move v10, v4
+    :goto_24
+    if-ge v10, v12, :cond_3e8
+    invoke-virtual {v11, v10}, LTest;->valueAt(I)Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, LTest;
+    iget v6, v4, LTest;->e:I
+    iget-object v7, v4, LTest;->a:LTest;
+    iget v7, v7, LTest;->d:I
+    if-eq v6, v7, :cond_5fe
+    iget-object v6, v4, LTest;->a:LTest;
+    iget-wide v6, v6, LTest;->b:J
+    cmp-long v13, v6, v8
+    if-gez v13, :cond_5fe
+    move-wide/from16 v23, v6
+    move-object v6, v4
+    move-wide/from16 v4, v23
+    :goto_41
+    add-int/lit8 v7, v10, 0x1
+    move v10, v7
+    move-wide v8, v4
+    move-object v5, v6
+    goto :goto_24
+    :pswitch_47
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    if-nez v4, :cond_8a
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:[B
+    const/4 v5, 0x0
+    const/16 v6, 0x8
+    const/4 v7, 0x1
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v4, v5, v6, v7}, LTest;->a([BIIZ)Z
+    move-result v4
+    if-nez v4, :cond_64
+    const/4 v4, 0x0
+    :goto_60
+    if-nez v4, :cond_0
+    const/4 v4, -0x1
+    :goto_63
+    return v4
+    :cond_64
+    const/16 v4, 0x8
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    const/4 v5, 0x0
+    invoke-virtual {v4, v5}, LTest;->c(I)V
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4}, LTest;->h()J
+    move-result-wide v4
+    move-object/from16 v0, p0
+    iput-wide v4, v0, LTestObject;->n:J
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4}, LTest;->j()I
+    move-result v4
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    :cond_8a
+    move-object/from16 v0, p0
+    iget-wide v4, v0, LTestObject;->n:J
+    const-wide/16 v6, 0x1
+    cmp-long v4, v4, v6
+    if-nez v4, :cond_b9
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:[B
+    const/16 v5, 0x8
+    const/16 v6, 0x8
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v4, v5, v6}, LTest;->b([BII)V
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    add-int/lit8 v4, v4, 0x8
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4}, LTest;->p()J
+    move-result-wide v4
+    move-object/from16 v0, p0
+    iput-wide v4, v0, LTestObject;->n:J
+    :cond_b9
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v4
+    move-object/from16 v0, p0
+    iget v6, v0, LTestObject;->i:I
+    int-to-long v6, v6
+    sub-long v6, v4, v6
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    sget v5, LTest;->J:I
+    if-ne v4, v5, :cond_ec
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4}, LTest;->size()I
+    move-result v8
+    const/4 v4, 0x0
+    move v5, v4
+    :goto_d6
+    if-ge v5, v8, :cond_ec
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4, v5}, LTest;->valueAt(I)Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iput-wide v6, v4, LTest;->c:J
+    iput-wide v6, v4, LTest;->b:J
+    add-int/lit8 v4, v5, 0x1
+    move v5, v4
+    goto :goto_d6
+    :cond_ec
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    sget v5, LTest;->h:I
+    if-ne v4, v5, :cond_11e
+    const/4 v4, 0x0
+    move-object/from16 v0, p0
+    iput-object v4, v0, LTestObject;->o:LTest;
+    move-object/from16 v0, p0
+    iget-wide v4, v0, LTestObject;->n:J
+    add-long/2addr v4, v6
+    move-object/from16 v0, p0
+    iput-wide v4, v0, LTestObject;->q:J
+    move-object/from16 v0, p0
+    iget-boolean v4, v0, LTestObject;->w:Z
+    if-nez v4, :cond_116
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    sget-object v5, LTest;->f:LTest;
+    invoke-virtual {v4, v5}, LTest;->a(LTest;)V
+    const/4 v4, 0x1
+    move-object/from16 v0, p0
+    iput-boolean v4, v0, LTestObject;->w:Z
+    :cond_116
+    const/4 v4, 0x2
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    :goto_11b
+    const/4 v4, 0x1
+    goto/16 :goto_60
+    :cond_11e
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    sget v5, LTest;->A:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->C:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->D:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->E:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->F:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->J:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->K:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->L:I
+    if-eq v4, v5, :cond_146
+    sget v5, LTest;->O:I
+    if-ne v4, v5, :cond_178
+    :cond_146
+    const/4 v4, 0x1
+    :goto_147
+    if-eqz v4, :cond_17e
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v4
+    move-object/from16 v0, p0
+    iget-wide v6, v0, LTestObject;->n:J
+    add-long/2addr v4, v6
+    const-wide/16 v6, 0x8
+    sub-long/2addr v4, v6
+    move-object/from16 v0, p0
+    iget-object v6, v0, LTestObject;->k:Ljava/util/Stack;
+    new-instance v7, LTest;
+    move-object/from16 v0, p0
+    iget v8, v0, LTestObject;->i:I
+    invoke-direct {v7, v8, v4, v5}, LTest;-><init>(IJ)V
+    invoke-virtual {v6, v7}, Ljava/util/Stack;->add(Ljava/lang/Object;)Z
+    move-object/from16 v0, p0
+    iget-wide v6, v0, LTestObject;->n:J
+    move-object/from16 v0, p0
+    iget v8, v0, LTestObject;->i:I
+    int-to-long v8, v8
+    cmp-long v6, v6, v8
+    if-nez v6, :cond_17a
+    move-object/from16 v0, p0
+    invoke-direct {v0, v4, v5}, LTestObject;->a(J)V
+    goto :goto_11b
+    :cond_178
+    const/4 v4, 0x0
+    goto :goto_147
+    :cond_17a
+    invoke-direct/range {p0 .. p0}, LTestObject;->a()V
+    goto :goto_11b
+    :cond_17e
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    sget v5, LTest;->R:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->Q:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->B:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->z:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->S:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->v:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->w:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->N:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->x:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->y:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->T:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->ab:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->ac:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->ag:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->ad:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->ae:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->af:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->P:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->M:I
+    if-eq v4, v5, :cond_1d2
+    sget v5, LTest;->aD:I
+    if-ne v4, v5, :cond_1e5
+    :cond_1d2
+    const/4 v4, 0x1
+    :goto_1d3
+    if-eqz v4, :cond_222
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    const/16 v5, 0x8
+    if-eq v4, v5, :cond_1e7
+    new-instance v4, LTest;
+    const-string v5, "a"
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_1e5
+    const/4 v4, 0x0
+    goto :goto_1d3
+    :cond_1e7
+    move-object/from16 v0, p0
+    iget-wide v4, v0, LTestObject;->n:J
+    const-wide/32 v6, 0x7fffffff
+    cmp-long v4, v4, v6
+    if-lez v4, :cond_1fa
+    new-instance v4, LTest;
+    const-string v5, "a"
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_1fa
+    new-instance v4, LTest;
+    move-object/from16 v0, p0
+    iget-wide v6, v0, LTestObject;->n:J
+    long-to-int v5, v6
+    invoke-direct {v4, v5}, LTest;-><init>(I)V
+    move-object/from16 v0, p0
+    iput-object v4, v0, LTestObject;->o:LTest;
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:[B
+    const/4 v5, 0x0
+    move-object/from16 v0, p0
+    iget-object v6, v0, LTestObject;->o:LTest;
+    iget-object v6, v6, LTest;->a:[B
+    const/4 v7, 0x0
+    const/16 v8, 0x8
+    invoke-static {v4, v5, v6, v7, v8}, Ljava/lang/System;->arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V
+    const/4 v4, 0x1
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    goto/16 :goto_11b
+    :cond_222
+    move-object/from16 v0, p0
+    iget-wide v4, v0, LTestObject;->n:J
+    const-wide/32 v6, 0x7fffffff
+    cmp-long v4, v4, v6
+    if-lez v4, :cond_235
+    new-instance v4, LTest;
+    const-string v5, "a"
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_235
+    const/4 v4, 0x0
+    move-object/from16 v0, p0
+    iput-object v4, v0, LTestObject;->o:LTest;
+    const/4 v4, 0x1
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    goto/16 :goto_11b
+    :pswitch_241
+    move-object/from16 v0, p0
+    iget-wide v4, v0, LTestObject;->n:J
+    long-to-int v4, v4
+    move-object/from16 v0, p0
+    iget v5, v0, LTestObject;->i:I
+    sub-int/2addr v4, v5
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->o:LTest;
+    if-eqz v5, :cond_368
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->o:LTest;
+    iget-object v5, v5, LTest;->a:[B
+    const/16 v6, 0x8
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v5, v6, v4}, LTest;->b([BII)V
+    new-instance v5, LTest;
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget-object v6, v0, LTestObject;->o:LTest;
+    invoke-direct {v5, v4, v6}, LTest;-><init>(ILTest;)V
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v10
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->k:Ljava/util/Stack;
+    invoke-virtual {v4}, Ljava/util/Stack;->isEmpty()Z
+    move-result v4
+    if-nez v4, :cond_291
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->k:Ljava/util/Stack;
+    invoke-virtual {v4}, Ljava/util/Stack;->peek()Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, LTest;
+    invoke-virtual {v4, v5}, LTest;->a(LTest;)V
+    :cond_286
+    :goto_286
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v4
+    move-object/from16 v0, p0
+    invoke-direct {v0, v4, v5}, LTestObject;->a(J)V
+    goto/16 :goto_0
+    :cond_291
+    iget v4, v5, LTest;->aL:I
+    sget v6, LTest;->z:I
+    if-ne v4, v6, :cond_359
+    iget-object v0, v5, LTest;->aM:LTest;
+    move-object/from16 v16, v0
+    const/16 v4, 0x8
+    move-object/from16 v0, v16
+    invoke-virtual {v0, v4}, LTest;->c(I)V
+    invoke-virtual/range {v16 .. v16}, LTest;->j()I
+    move-result v4
+    invoke-static {v4}, LTest;->a(I)I
+    move-result v4
+    const/4 v5, 0x4
+    move-object/from16 v0, v16
+    invoke-virtual {v0, v5}, LTest;->d(I)V
+    invoke-virtual/range {v16 .. v16}, LTest;->h()J
+    move-result-wide v8
+    if-nez v4, :cond_304
+    invoke-virtual/range {v16 .. v16}, LTest;->h()J
+    move-result-wide v6
+    invoke-virtual/range {v16 .. v16}, LTest;->h()J
+    move-result-wide v4
+    add-long/2addr v4, v10
+    move-wide v10, v4
+    move-wide v4, v6
+    :goto_2c1
+    const/4 v6, 0x2
+    move-object/from16 v0, v16
+    invoke-virtual {v0, v6}, LTest;->d(I)V
+    invoke-virtual/range {v16 .. v16}, LTest;->e()I
+    move-result v17
+    move/from16 v0, v17
+    new-array v0, v0, [I
+    move-object/from16 v18, v0
+    move/from16 v0, v17
+    new-array v0, v0, [J
+    move-object/from16 v19, v0
+    move/from16 v0, v17
+    new-array v0, v0, [J
+    move-object/from16 v20, v0
+    move/from16 v0, v17
+    new-array v0, v0, [J
+    move-object/from16 v21, v0
+    const-wide/32 v6, 0xf4240
+    invoke-static/range {v4 .. v9}, LTest;->a(JJJ)J
+    move-result-wide v12
+    const/4 v6, 0x0
+    move-wide v14, v10
+    move v10, v6
+    move-wide v6, v4
+    move-wide v4, v12
+    :goto_2ef
+    move/from16 v0, v17
+    if-ge v10, v0, :cond_33e
+    invoke-virtual/range {v16 .. v16}, LTest;->j()I
+    move-result v11
+    const/high16 v12, -0x80000000
+    and-int/2addr v12, v11
+    if-eqz v12, :cond_310
+    new-instance v4, LTest;
+    const-string v5, "a"
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_304
+    invoke-virtual/range {v16 .. v16}, LTest;->p()J
+    move-result-wide v6
+    invoke-virtual/range {v16 .. v16}, LTest;->p()J
+    move-result-wide v4
+    add-long/2addr v4, v10
+    move-wide v10, v4
+    move-wide v4, v6
+    goto :goto_2c1
+    :cond_310
+    invoke-virtual/range {v16 .. v16}, LTest;->h()J
+    move-result-wide v12
+    const v22, 0x7fffffff
+    and-int v11, v11, v22
+    aput v11, v18, v10
+    aput-wide v14, v19, v10
+    aput-wide v4, v21, v10
+    add-long v4, v6, v12
+    const-wide/32 v6, 0xf4240
+    invoke-static/range {v4 .. v9}, LTest;->a(JJJ)J
+    move-result-wide v12
+    aget-wide v6, v21, v10
+    sub-long v6, v12, v6
+    aput-wide v6, v20, v10
+    const/4 v6, 0x4
+    move-object/from16 v0, v16
+    invoke-virtual {v0, v6}, LTest;->d(I)V
+    aget v6, v18, v10
+    int-to-long v6, v6
+    add-long/2addr v14, v6
+    add-int/lit8 v6, v10, 0x1
+    move v10, v6
+    move-wide v6, v4
+    move-wide v4, v12
+    goto :goto_2ef
+    :cond_33e
+    new-instance v4, LTest;
+    move-object/from16 v0, v18
+    move-object/from16 v1, v19
+    move-object/from16 v2, v20
+    move-object/from16 v3, v21
+    invoke-direct {v4, v0, v1, v2, v3}, LTest;-><init>([I[J[J[J)V
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->o:LTest;
+    invoke-virtual {v5, v4}, LTest;->a(LTest;)V
+    const/4 v4, 0x1
+    move-object/from16 v0, p0
+    iput-boolean v4, v0, LTestObject;->w:Z
+    goto/16 :goto_286
+    :cond_359
+    iget v4, v5, LTest;->aL:I
+    sget v6, LTest;->aD:I
+    if-ne v4, v6, :cond_286
+    iget-object v4, v5, LTest;->aM:LTest;
+    move-object/from16 v0, p0
+    invoke-virtual {v0, v4}, LTestObject;->a(LTest;)V
+    goto/16 :goto_286
+    :cond_368
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v4}, LTest;->b(I)V
+    goto/16 :goto_286
+    :pswitch_36f
+    const/4 v5, 0x0
+    const-wide v6, 0x7fffffffffffffffL
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4}, LTest;->size()I
+    move-result v9
+    const/4 v4, 0x0
+    move v8, v4
+    :goto_37f
+    if-ge v8, v9, :cond_3b1
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4, v8}, LTest;->valueAt(I)Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-boolean v10, v4, LTest;->m:Z
+    if-eqz v10, :cond_602
+    iget-wide v10, v4, LTest;->c:J
+    cmp-long v10, v10, v6
+    if-gez v10, :cond_602
+    iget-wide v6, v4, LTest;->c:J
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    invoke-virtual {v4, v8}, LTest;->valueAt(I)Ljava/lang/Object;
+    move-result-object v4
+    check-cast v4, LTest;
+    move-wide/from16 v23, v6
+    move-object v6, v4
+    move-wide/from16 v4, v23
+    :goto_3a8
+    add-int/lit8 v7, v8, 0x1
+    move v8, v7
+    move-wide/from16 v23, v4
+    move-object v5, v6
+    move-wide/from16 v6, v23
+    goto :goto_37f
+    :cond_3b1
+    if-nez v5, :cond_3ba
+    const/4 v4, 0x3
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    goto/16 :goto_0
+    :cond_3ba
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v8
+    sub-long/2addr v6, v8
+    long-to-int v4, v6
+    if-gez v4, :cond_3ca
+    new-instance v4, LTest;
+    const-string v5, "a"
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_3ca
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v4}, LTest;->b(I)V
+    iget-object v4, v5, LTest;->a:LTest;
+    iget-object v5, v4, LTest;->l:LTest;
+    iget-object v5, v5, LTest;->a:[B
+    const/4 v6, 0x0
+    iget v7, v4, LTest;->k:I
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v5, v6, v7}, LTest;->b([BII)V
+    iget-object v5, v4, LTest;->l:LTest;
+    const/4 v6, 0x0
+    invoke-virtual {v5, v6}, LTest;->c(I)V
+    const/4 v5, 0x0
+    iput-boolean v5, v4, LTest;->m:Z
+    goto/16 :goto_0
+    :cond_3e8
+    move-object/from16 v0, p0
+    iput-object v5, v0, LTestObject;->o:LTest;
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    if-nez v4, :cond_414
+    move-object/from16 v0, p0
+    iget-wide v4, v0, LTestObject;->q:J
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v6
+    sub-long/2addr v4, v6
+    long-to-int v4, v4
+    if-gez v4, :cond_406
+    new-instance v4, LTest;
+    const-string v5, ""
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_406
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v4}, LTest;->b(I)V
+    invoke-direct/range {p0 .. p0}, LTestObject;->a()V
+    const/4 v4, 0x0
+    :goto_40f
+    if-eqz v4, :cond_0
+    const/4 v4, 0x0
+    goto/16 :goto_63
+    :cond_414
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-wide v4, v4, LTest;->b:J
+    invoke-virtual/range {p1 .. p1}, LTest;->c()J
+    move-result-wide v6
+    sub-long/2addr v4, v6
+    long-to-int v4, v4
+    if-gez v4, :cond_42c
+    new-instance v4, LTest;
+    const-string v5, ""
+    invoke-direct {v4, v5}, LTest;-><init>(Ljava/lang/String;)V
+    throw v4
+    :cond_42c
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v4}, LTest;->b(I)V
+    :cond_431
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-object v4, v4, LTest;->e:[I
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->o:LTest;
+    iget v5, v5, LTest;->e:I
+    aget v4, v4, v5
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:LTest;
+    iget-boolean v4, v4, LTest;->i:Z
+    if-eqz v4, :cond_553
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->o:LTest;
+    iget-object v6, v5, LTest;->a:LTest;
+    iget-object v7, v6, LTest;->l:LTest;
+    iget-object v4, v6, LTest;->a:LTest;
+    iget v4, v4, LTest;->a:I
+    iget-object v8, v6, LTest;->n:LTest;
+    if-eqz v8, :cond_534
+    iget-object v4, v6, LTest;->n:LTest;
+    :goto_461
+    iget v8, v4, LTest;->a:I
+    iget-object v4, v6, LTest;->j:[Z
+    iget v6, v5, LTest;->e:I
+    aget-boolean v6, v4, v6
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v9, v4, LTest;->a:[B
+    const/4 v10, 0x0
+    if-eqz v6, :cond_53c
+    const/16 v4, 0x80
+    :goto_474
+    or-int/2addr v4, v8
+    int-to-byte v4, v4
+    aput-byte v4, v9, v10
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    const/4 v9, 0x0
+    invoke-virtual {v4, v9}, LTest;->c(I)V
+    iget-object v4, v5, LTest;->b:LTest;
+    move-object/from16 v0, p0
+    iget-object v5, v0, LTestObject;->o:LTest;
+    const/4 v9, 0x1
+    invoke-virtual {v4, v5, v9}, LTest;->a(LTest;I)V
+    invoke-virtual {v4, v7, v8}, LTest;->a(LTest;I)V
+    if-nez v6, :cond_53f
+    add-int/lit8 v4, v8, 0x1
+    :goto_491
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v5, v0, LTestObject;->i:I
+    add-int/2addr v4, v5
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    :goto_4a2
+    const/4 v4, 0x4
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    const/4 v4, 0x0
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    :cond_4ac
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v12, v4, LTest;->a:LTest;
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v9, v4, LTest;->c:LTest;
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v5, v4, LTest;->b:LTest;
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget v8, v4, LTest;->e:I
+    iget v4, v9, LTest;->n:I
+    const/4 v6, -0x1
+    if-eq v4, v6, :cond_57a
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget-object v4, v4, LTest;->a:[B
+    const/4 v6, 0x0
+    const/4 v7, 0x0
+    aput-byte v7, v4, v6
+    const/4 v6, 0x1
+    const/4 v7, 0x0
+    aput-byte v7, v4, v6
+    const/4 v6, 0x2
+    const/4 v7, 0x0
+    aput-byte v7, v4, v6
+    iget v4, v9, LTest;->n:I
+    iget v6, v9, LTest;->n:I
+    rsub-int/lit8 v6, v6, 0x4
+    :goto_4e1
+    move-object/from16 v0, p0
+    iget v7, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v10, v0, LTestObject;->i:I
+    if-ge v7, v10, :cond_59e
+    move-object/from16 v0, p0
+    iget v7, v0, LTestObject;->i:I
+    if-nez v7, :cond_55a
+    move-object/from16 v0, p0
+    iget-object v7, v0, LTestObject;->o:LTest;
+    iget-object v7, v7, LTest;->a:[B
+    move-object/from16 v0, p1
+    invoke-virtual {v0, v7, v6, v4}, LTest;->b([BII)V
+    move-object/from16 v0, p0
+    iget-object v7, v0, LTestObject;->o:LTest;
+    const/4 v10, 0x0
+    invoke-virtual {v7, v10}, LTest;->c(I)V
+    move-object/from16 v0, p0
+    iget-object v7, v0, LTestObject;->o:LTest;
+    invoke-virtual {v7}, LTest;->n()I
+    move-result v7
+    move-object/from16 v0, p0
+    iput v7, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget-object v7, v0, LTestObject;->o:LTest;
+    const/4 v10, 0x0
+    invoke-virtual {v7, v10}, LTest;->c(I)V
+    move-object/from16 v0, p0
+    iget-object v7, v0, LTestObject;->o:LTest;
+    const/4 v10, 0x4
+    invoke-virtual {v5, v7, v10}, LTest;->a(LTest;I)V
+    move-object/from16 v0, p0
+    iget v7, v0, LTestObject;->i:I
+    add-int/lit8 v7, v7, 0x4
+    move-object/from16 v0, p0
+    iput v7, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v7, v0, LTestObject;->i:I
+    add-int/2addr v7, v6
+    move-object/from16 v0, p0
+    iput v7, v0, LTestObject;->i:I
+    goto :goto_4e1
+    :cond_534
+    iget-object v8, v5, LTest;->c:LTest;
+    iget-object v8, v8, LTest;->k:[LTest;
+    aget-object v4, v8, v4
+    goto/16 :goto_461
+    :cond_53c
+    const/4 v4, 0x0
+    goto/16 :goto_474
+    :cond_53f
+    invoke-virtual {v7}, LTest;->e()I
+    move-result v5
+    const/4 v6, -0x2
+    invoke-virtual {v7, v6}, LTest;->d(I)V
+    mul-int/lit8 v5, v5, 0x6
+    add-int/lit8 v5, v5, 0x2
+    invoke-virtual {v4, v7, v5}, LTest;->a(LTest;I)V
+    add-int/lit8 v4, v8, 0x1
+    add-int/2addr v4, v5
+    goto/16 :goto_491
+    :cond_553
+    const/4 v4, 0x0
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    goto/16 :goto_4a2
+    :cond_55a
+    move-object/from16 v0, p0
+    iget v7, v0, LTestObject;->i:I
+    const/4 v10, 0x0
+    move-object/from16 v0, p1
+    invoke-virtual {v5, v0, v7, v10}, LTest;->a(LTest;IZ)I
+    move-result v7
+    move-object/from16 v0, p0
+    iget v10, v0, LTestObject;->i:I
+    add-int/2addr v10, v7
+    move-object/from16 v0, p0
+    iput v10, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v10, v0, LTestObject;->i:I
+    sub-int v7, v10, v7
+    move-object/from16 v0, p0
+    iput v7, v0, LTestObject;->i:I
+    goto/16 :goto_4e1
+    :cond_57a
+    :goto_57a
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v6, v0, LTestObject;->i:I
+    if-ge v4, v6, :cond_59e
+    move-object/from16 v0, p0
+    iget v4, v0, LTestObject;->i:I
+    move-object/from16 v0, p0
+    iget v6, v0, LTestObject;->i:I
+    sub-int/2addr v4, v6
+    const/4 v6, 0x0
+    move-object/from16 v0, p1
+    invoke-virtual {v5, v0, v4, v6}, LTest;->a(LTest;IZ)I
+    move-result v4
+    move-object/from16 v0, p0
+    iget v6, v0, LTestObject;->i:I
+    add-int/2addr v4, v6
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    goto :goto_57a
+    :cond_59e
+    iget-object v4, v12, LTest;->g:[J
+    aget-wide v6, v4, v8
+    iget-object v4, v12, LTest;->f:[I
+    aget v4, v4, v8
+    int-to-long v10, v4
+    add-long/2addr v6, v10
+    const-wide/16 v10, 0x3e8
+    mul-long/2addr v6, v10
+    iget-boolean v4, v12, LTest;->i:Z
+    if-eqz v4, :cond_5f3
+    const/4 v4, 0x2
+    :goto_5b0
+    iget-object v10, v12, LTest;->h:[Z
+    aget-boolean v8, v10, v8
+    if-eqz v8, :cond_5f5
+    const/4 v8, 0x1
+    :goto_5b7
+    or-int/2addr v8, v4
+    iget-object v4, v12, LTest;->a:LTest;
+    iget v4, v4, LTest;->a:I
+    const/4 v11, 0x0
+    iget-boolean v10, v12, LTest;->i:Z
+    if-eqz v10, :cond_5ca
+    iget-object v10, v12, LTest;->n:LTest;
+    if-eqz v10, :cond_5f7
+    iget-object v4, v12, LTest;->n:LTest;
+    iget-object v4, v4, LTest;->b:[B
+    :goto_5c9
+    move-object v11, v4
+    :cond_5ca
+    move-object/from16 v0, p0
+    iget v9, v0, LTestObject;->i:I
+    const/4 v10, 0x0
+    invoke-virtual/range {v5 .. v11}, LTest;->a(JIII[B)V
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget v5, v4, LTest;->e:I
+    add-int/lit8 v5, v5, 0x1
+    iput v5, v4, LTest;->e:I
+    move-object/from16 v0, p0
+    iget-object v4, v0, LTestObject;->o:LTest;
+    iget v4, v4, LTest;->e:I
+    iget v5, v12, LTest;->d:I
+    if-ne v4, v5, :cond_5eb
+    const/4 v4, 0x0
+    move-object/from16 v0, p0
+    iput-object v4, v0, LTestObject;->o:LTest;
+    :cond_5eb
+    const/4 v4, 0x3
+    move-object/from16 v0, p0
+    iput v4, v0, LTestObject;->i:I
+    const/4 v4, 0x1
+    goto/16 :goto_40f
+    :cond_5f3
+    const/4 v4, 0x0
+    goto :goto_5b0
+    :cond_5f5
+    const/4 v8, 0x0
+    goto :goto_5b7
+    :cond_5f7
+    iget-object v9, v9, LTest;->k:[LTest;
+    aget-object v4, v9, v4
+    iget-object v4, v4, LTest;->b:[B
+    goto :goto_5c9
+    :cond_5fe
+    move-object v6, v5
+    move-wide v4, v8
+    goto/16 :goto_41
+    :cond_602
+    move-wide/from16 v23, v6
+    move-object v6, v5
+    move-wide/from16 v4, v23
+    goto/16 :goto_3a8
+    nop
+    :pswitch_data_60a
+    .packed-switch 0x0
+        :pswitch_47
+        :pswitch_241
+        :pswitch_36f
+    .end packed-switch
+.end method
+
+.method private final a(J)V
+    .registers 3
+    .prologue
+    return-void
+.end method
+
diff --git a/src/test/smali/type-confusion-regression4/Test.java b/src/test/smali/type-confusion-regression4/Test.java
new file mode 100644
index 0000000..252d6c8
--- /dev/null
+++ b/src/test/smali/type-confusion-regression4/Test.java
@@ -0,0 +1,40 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+  public boolean b = false;
+  public Test a = null;
+
+  public Test() {
+  }
+
+  public Test(int i, Test a) {
+    throw new RuntimeException("Test(ILTest;)");
+  }
+
+  public int nextIndex() {
+    throw new RuntimeException("nextIndex()");
+  }
+
+  public int previousIndex() {
+    throw new RuntimeException("previousIndex()");
+  }
+
+  public boolean hasNext() {
+    throw new RuntimeException("hasNext()");
+  }
+
+  public boolean hasPrevious() {
+    throw new RuntimeException("hasPrevious()");
+  }
+
+  public static void main(String[] args) {
+    try {
+      new TestObject().a(new Test(), new Test());
+    } catch (Exception e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/type-confusion-regression4/TestObject.java b/src/test/smali/type-confusion-regression4/TestObject.java
new file mode 100644
index 0000000..340e820
--- /dev/null
+++ b/src/test/smali/type-confusion-regression4/TestObject.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public final Test a(Test t0, Test t1) {
+    return null;
+  }
+}
diff --git a/src/test/smali/type-confusion-regression4/TestObject.smali b/src/test/smali/type-confusion-regression4/TestObject.smali
new file mode 100644
index 0000000..c190f81
--- /dev/null
+++ b/src/test/smali/type-confusion-regression4/TestObject.smali
@@ -0,0 +1,82 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.method public final a(LTest;LTest;)LTest;
+    .registers 7
+    .prologue
+    const/4 v0, 0x0
+    if-eqz p1, :cond_23
+    iget-boolean v1, p1, LTest;->b:Z
+    if-eqz v1, :cond_35
+    iget-object v1, p0, LTest;->f:LTest;
+    iget-object v1, v1, LTest;->b:LTest;
+    invoke-virtual {v1}, LTest;->hasPrevious()Z
+    move-result v1
+    if-eqz v1, :cond_35
+    new-instance p2, LTest;
+    const/4 v1, 0x1
+    iget-object v2, p0, LTest;->f:LTest;
+    iget-object v3, v2, LTest;->b:LTest;
+    invoke-virtual {v3}, LTest;->hasPrevious()Z
+    move-result v3
+    if-nez v3, :cond_24
+    :goto_1e
+    iget-object v0, v0, LTest;->a:LTest;
+    invoke-direct {p2, v1, v0}, LTest;-><init>(ILTest;)V
+    :cond_23
+    :goto_23
+    return-object p2
+    :cond_24
+    iget-object v0, v2, LTest;->b:LTest;
+    iget-object v0, v0, LTest;->a:Ljava/util/ArrayList;
+    iget-object v2, v2, LTest;->b:LTest;
+    invoke-virtual {v2}, LTest;->previousIndex()I
+    move-result v2
+    invoke-interface {v0, v2}, Ljava/util/List;->get(I)Ljava/lang/Object;
+    move-result-object v0
+    check-cast v0, LTest;
+    goto :goto_1e
+    :cond_35
+    iget-boolean v1, p1, LTest;->b:Z
+    if-eqz v1, :cond_67
+    iget-object v1, p0, LTest;->f:LTest;
+    iget-object v1, v1, LTest;->b:LTest;
+    invoke-virtual {v1}, LTest;->hasNext()Z
+    move-result v1
+    if-eqz v1, :cond_67
+    new-instance p2, LTest;
+    const/4 v1, 0x2
+    iget-object v2, p0, LTest;->f:LTest;
+    iget-object v3, v2, LTest;->b:LTest;
+    invoke-virtual {v3}, LTest;->hasNext()Z
+    move-result v3
+    if-nez v3, :cond_56
+    :goto_50
+    iget-object v0, v0, LTest;->a:LTest;
+    invoke-direct {p2, v1, v0}, LTest;-><init>(ILTest;)V
+    goto :goto_23
+    :cond_56
+    iget-object v0, v2, LTest;->b:LTest;
+    iget-object v0, v0, LTest;->a:Ljava/util/ArrayList;
+    iget-object v2, v2, LTest;->b:LTest;
+    invoke-virtual {v2}, LTest;->nextIndex()I
+    move-result v2
+    invoke-interface {v0, v2}, Ljava/util/List;->get(I)Ljava/lang/Object;
+    move-result-object v0
+    check-cast v0, LTest;
+    goto :goto_50
+    :cond_67
+    iget-object v0, p1, LTest;->a:LTest;
+    if-eqz v0, :cond_23
+    new-instance p2, LTest;
+    const/4 v0, 0x0
+    new-instance v1, LTest;
+    iget-object v2, p1, LTest;->a:LTest;
+    invoke-direct {v1, v2}, LTest;-><init>(LTest;)V
+    invoke-direct {p2, v0, v1}, LTest;-><init>(ILTest;)V
+    goto :goto_23
+.end method
\ No newline at end of file
diff --git a/src/test/smali/type-confusion-regression5/Test.java b/src/test/smali/type-confusion-regression5/Test.java
new file mode 100644
index 0000000..e450d6b
--- /dev/null
+++ b/src/test/smali/type-confusion-regression5/Test.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class Test {
+
+  public int getId() {
+    throw new RuntimeException("getId()I");
+  }
+
+  public boolean a() {
+    throw new RuntimeException("a()Z");
+  }
+
+  public void a(Test t0) {
+    throw new RuntimeException("a(LTest;)V");
+  }
+
+  public void a(Test t0, Test t1) {
+    throw new RuntimeException("a(LTest;LTest;)V");
+  }
+
+  public static boolean b(Test t0, Test t1) {
+    throw new RuntimeException("b()Z");
+  }
+
+  public Test c() {
+    throw new RuntimeException("c()LTest;");
+  }
+
+  public static boolean c(Test t) {
+    throw new RuntimeException("c(LTest;)Z");
+  }
+
+  public void g() {
+    throw new RuntimeException("g()V");
+  }
+
+  public boolean pageScroll(int i) {
+    throw new RuntimeException("pageScroll(I)Z");
+  }
+
+  public static void main(String[] args) {
+    try {
+      new TestObject().onClick(new Test());
+    } catch (Exception e) {
+      System.out.println(e);
+    }
+  }
+}
diff --git a/src/test/smali/type-confusion-regression5/TestObject.java b/src/test/smali/type-confusion-regression5/TestObject.java
new file mode 100644
index 0000000..a3cc1f9
--- /dev/null
+++ b/src/test/smali/type-confusion-regression5/TestObject.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+public class TestObject {
+  public final void onClick(Test t) {
+  }
+}
diff --git a/src/test/smali/type-confusion-regression5/TestObject.smali b/src/test/smali/type-confusion-regression5/TestObject.smali
new file mode 100644
index 0000000..9ca1697
--- /dev/null
+++ b/src/test/smali/type-confusion-regression5/TestObject.smali
@@ -0,0 +1,171 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public final LTestObject;
+.super Ljava/lang/Object;
+
+.field private c:LTest;
+
+.method public onClick(LTest;)V
+    .registers 10
+    const/4             v2, 0x00  # 0
+    invoke-virtual      { v9 }, LTest;->getId()I
+    move-result         v0
+    const               v1, 0x7f0f01bf  # 2131689919
+    if-ne               v0, v1, :label_247
+    invoke-virtual      { v8 }, LTestObject;->getActivity()LTest;
+    move-result-object  v0
+    invoke-static       { v0, v9 }, LTest;->b(LTest;LTest;)Z
+    iget-object         v0, v8, LTestObject;->c:LTest;
+    invoke-virtual      { v0 }, LTest;->a()Z
+    move-result         v0
+    if-eqz              v0, :label_231
+    iget-object         v0, v8, LTestObject;->d:Landroid/widget/Button;
+    const/4             v1, 0x00  # 0
+    invoke-virtual      { v0, v1 }, Landroid/widget/Button;->setEnabled(Z)V
+    iget-object         v0, v8, LTestObject;->b:Landroid/widget/LinearLayout;
+    if-nez              v0, :label_72
+    const-string        v0, "a"
+    const/4             v1, 0x05  # 5
+    invoke-static       { v0, v1 }, Landroid/util/Log;->isLoggable(Ljava/lang/String;I)Z
+    move-result         v0
+    if-eqz              v0, :label_51
+    const-string        v0, "b"
+    const-string        v1, "c"
+    invoke-static       { v0, v1 }, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I
+  :label_51
+    invoke-static       { v2 }, LTest;->c(LTest;)Z
+    move-result         v0
+    if-eqz              v0, :label_66
+    iget-object         v0, v8, LTestObject;->a:LTest;
+    sget-object         v1, Ljok;->d:LTest;
+    sget-object         v3, Ljol;->c:LTest;
+    invoke-virtual      { v0, v1, v3 }, LTest;->a(LTest;LTest;)V
+  :label_66
+    iget-object         v0, v8, LTestObject;->g:LTest;
+    invoke-virtual      { v0, v2 }, LTest;->a(LTest;)V
+  :label_71
+    return-void
+  :label_72
+    new-instance        v5, Ljava/util/ArrayList;
+    invoke-direct       { v5 }, Ljava/util/ArrayList;-><init>()V
+    iget-object         v0, v8, LTestObject;->b:Landroid/widget/LinearLayout;
+    invoke-direct       { v8, v0 }, LTestObject;->a(Landroid/widget/LinearLayout;)Ljava/util/List;
+    move-result-object  v0
+    invoke-interface    { v0 }, Ljava/util/List;->iterator()Ljava/util/Iterator;
+    move-result-object  v6
+    move-object         v1, v2
+    move-object         v4, v2
+  :label_89
+    invoke-interface    { v6 }, Ljava/util/Iterator;->hasNext()Z
+    move-result         v0
+    if-eqz              v0, :label_151
+    invoke-interface    { v6 }, Ljava/util/Iterator;->next()Ljava/lang/Object;
+    move-result-object  v0
+    check-cast          v0, LTest;
+    invoke-virtual      { v0 }, LTest;->c()LTest;
+    move-result-object  v3
+    if-eqz              v3, :label_148
+    instance-of         v7, v0, LTest;
+    if-eqz              v7, :label_127
+    if-eqz              v1, :label_116
+    invoke-virtual      { v5, v1 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+  :label_116
+    invoke-virtual      { v5, v3 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+    if-eqz              v4, :label_124
+    invoke-virtual      { v5, v4 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+  :label_124
+    move-object         v1, v2
+    move-object         v4, v2
+    goto                :label_89
+  :label_127
+    instance-of         v7, v0, LTest;
+    if-eqz              v7, :label_133
+    move-object         v1, v3
+    goto                :label_89
+  :label_133
+    instance-of         v0, v0, LTest;
+    if-eqz              v0, :label_139
+    move-object         v4, v3
+    goto                :label_89
+  :label_139
+    if-eqz              v4, :label_145
+    invoke-virtual      { v5, v4 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+    move-object         v4, v2
+  :label_145
+    invoke-virtual      { v5, v3 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+  :label_148
+    move-object         v3, v4
+    move-object         v4, v3
+    goto                :label_89
+  :label_151
+    if-eqz              v4, :label_156
+    invoke-virtual      { v5, v4 }, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
+  :label_156
+    new-instance        v1, LTest;
+    invoke-direct       { v1 }, LTest;-><init>()V
+    const-string        v0, "d"
+    iput-object         v0, v1, LTest;->b:Ljava/lang/String;
+    iget-object         v0, v1, LTest;->c:Ljava/util/Set;
+    const/4             v3, 0x06  # 6
+    invoke-static       { v3 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
+    move-result-object  v3
+    invoke-interface    { v0, v3 }, Ljava/util/Set;->add(Ljava/lang/Object;)Z
+    new-instance        v3, LTest;
+    invoke-direct       { v3 }, LTest;-><init>()V
+    iput-object         v5, v3, LTest;->a:Ljava/util/List;
+    iget-object         v0, v3, LTest;->b:Ljava/util/Set;
+    const/4             v4, 0x02  # 2
+    invoke-static       { v4 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
+    move-result-object  v4
+    invoke-interface    { v0, v4 }, Ljava/util/Set;->add(Ljava/lang/Object;)Z
+    new-instance        v0, LTest;
+    iget-object         v4, v3, LTest;->b:Ljava/util/Set;
+    iget-object         v3, v3, LTest;->a:Ljava/util/List;
+    invoke-direct       { v0, v4, v3 }, LTest;-><init>(Ljava/util/Set;Ljava/util/List;)V
+    check-cast          v0, LTest;
+    iput-object         v0, v1, LTest;->a:LTest;
+    iget-object         v0, v1, LTest;->c:Ljava/util/Set;
+    const/4             v3, 0x04  # 4
+    invoke-static       { v3 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
+    move-result-object  v3
+    invoke-interface    { v0, v3 }, Ljava/util/Set;->add(Ljava/lang/Object;)Z
+    new-instance        v0, LTest;
+    iget-object         v3, v1, LTest;->c:Ljava/util/Set;
+    iget-object         v4, v1, LTest;->a:LTest;
+    iget-object         v1, v1, LTest;->b:Ljava/lang/String;
+    invoke-direct       { v0, v3, v2, v4, v1 }, LTest;-><init>(Ljava/util/Set;LTest;LTest;Ljava/lang/String;)V
+    check-cast          v0, LTest;
+    move-object         v2, v0
+    goto/16             :label_51
+  :label_231
+    iget-object         v0, v8, LTestObject;->c:LTest;
+    const/16            v1, 0x0082  # 130
+    invoke-virtual      { v0, v1 }, LTest;->pageScroll(I)Z
+    iget-object         v0, v8, LTestObject;->a:LTest;
+    sget-object         v1, Ljok;->k:LTest;
+    invoke-virtual      { v0, v1 }, LTest;->a(LTest;)V
+    goto/16             :label_71
+  :label_247
+    const               v1, 0x7f0f0204  # 2131689988
+    if-ne               v0, v1, :label_71
+    iget-object         v0, v8, LTestObject;->a:LTest;
+    sget-object         v1, Ljok;->q:LTest;
+    invoke-virtual      { v0, v1 }, LTest;->a(LTest;)V
+    iget-object         v0, v8, LTestObject;->g:LTest;
+    invoke-virtual      { v0 }, LTest;->g()V
+    goto/16             :label_71
+.end method
+
+.method public getActivity()LTest;
+  .registers 1
+  const/4               v0, 0x00
+  return-object         v0
+.end method
+
+.method private a(Landroid/widget/LinearLayout;)Ljava/util/List;
+  .registers 2
+  const/4               v0, 0x00
+  return-object         v0
+.end method
diff --git a/src/test/smali/unreachable-code-1/UnreachableCode1.smali b/src/test/smali/unreachable-code-1/UnreachableCode1.smali
new file mode 100644
index 0000000..a7ba2f1
--- /dev/null
+++ b/src/test/smali/unreachable-code-1/UnreachableCode1.smali
@@ -0,0 +1,87 @@
+# Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+.class public LTest;
+
+.super Ljava/lang/Object;
+
+.method public static test1()I
+  .registers 1
+  goto :return
+
+  :dummy
+  const/4 v0, 0x1
+
+  :return
+  const/4 v0, 0x7
+  return v0
+
+.end method
+
+.method public static test2()I
+  .registers 1
+  goto :return
+
+  :dummy1
+  const/4 v0, 0x1
+
+  :dummy2
+  const/4 v0, 0x2
+
+  :return
+  const/4 v0, 0x7
+  return v0
+
+.end method
+
+.method public static test3()I
+  .registers 1
+  goto :return
+
+  :dummy1
+  const/4 v0, 0x1
+  goto :dummy3
+
+  :dummy2
+  const/4 v0, 0x2
+  goto :return
+
+  :dummy3
+  const/4 v0, 0x3
+  goto :return
+
+  :dummy4
+  const/4 v0, 0x4
+
+  :return
+  const/4 v0, 0x7
+  return v0
+
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 2
+
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-static {}, LTest;->test1()I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    invoke-static {}, LTest;->test2()I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    invoke-static {}, LTest;->test3()I
+    move-result v1
+    invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;
+    move-result-object v1
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/Object;)V
+
+    return-void
+.end method