src/main/java/erjang/beam/repr/Operands.java
public class Operands {

    public static abstract class Operand {
		public SourceOperand asSource() {
			throw new IllegalArgumentException("Not a source: "+this);
		}
		public DestinationOperand asDestination() {
			throw new IllegalArgumentException("Not a destination: "+this);
		}
		public Label asLabel() {
			throw new IllegalArgumentException("Not a label: "+this);
		}
		public Literal asLiteral() {
			throw new IllegalArgumentException("Not a literal: "+this);
		}
		public Atom asAtom() {
			throw new IllegalArgumentException("Not an atom: "+this);
		}
		public CodeInt asCodeInt() {
			throw new IllegalArgumentException("Not a code integer: "+this);
		}
		public SelectList asSelectList() {
			throw new IllegalArgumentException("Not a select list: "+this);
		}
		public AllocList asAllocList() {
			throw new IllegalArgumentException("Not an alloc list: "+this);
		}
		public YReg asYReg() {
			throw new IllegalArgumentException("Not a Y register: "+this);
		}

		public TableLiteral testTableLiteral() { return null; }
		public Atom testAtom() { return null; }
		public Int testInt() { return null; }
		public BigInt testBigInt() { return null; }
		public Float testFloat() { return null; }
		public SelectList testSelectList() { return null; }
		public AllocList testAllocList() { return null; }
		public DestinationOperand testDestination() {return null;}
		public XReg testXReg() { return null; }
		public YReg testYReg() { return null; }
		public FReg testFReg() { return null; }

		public abstract EObject toSymbolic();
    }

	public static ESeq toSymbolicList(Operand[] args) {
		ESeq res = ERT.NIL;
		for (int i = args.length-1; i >= 0; i--) {
			res = res.cons(args[i].toSymbolic());
		}
		return res;
	}

    public static abstract class SourceOperand extends Operand {
		@Override
		public SourceOperand asSource() {return this;}
    }
    public static abstract class DestinationOperand extends SourceOperand {
		@Override
		public DestinationOperand asDestination() {return this;}
		@Override
		public DestinationOperand testDestination() {return this;}
    }
    public static abstract class Literal extends SourceOperand {
		@Override
		public Literal asLiteral() {return this;}
		public abstract EObject literalValue();
    }

    public static Literal makeInt(byte[] d) {
		BigInteger tmp = new BigInteger(d);
		return (d.length<=4)
			? new Int(tmp.intValue())
			: new BigInt(tmp);
    }

    /** CodeInt is present in jump-table-by-arity (select_arity). */
    public static class CodeInt extends Operand {
		public final int value;
		public CodeInt(int value) {this.value=value;}

		@Override
		public CodeInt asCodeInt() {return this;}

		public EObject toSymbolic() {
			return new ESmall(value);
		}
    }

    public static class Int extends Literal {
		public final int value;
		public Int(int value) {this.value=value;}

		public boolean equals(int val) { return value == val; }
		
		@Override
		public Int testInt() {return this;}

		@Override
		public EObject literalValue() {return new ESmall(value);}

		public EObject toSymbolic() {
			return ETuple.make(INTEGER_ATOM, new ESmall(value));
		}
    }

    public static class BigInt extends Literal {
		public final BigInteger value;
		public BigInt(BigInteger value) {this.value=value;}

		@Override
		public BigInt testBigInt() {return this;}

		@Override
		public EObject literalValue() {return new EBig(value);}

		public EObject toSymbolic() {
			return ETuple.make(INTEGER_ATOM, new EBig(value));
		}
    }

    public static class Float extends Literal {
		public final double value;
		public Float(double value) {this.value=value;}

		@Override
		public Float testFloat() {return this;}

		@Override
		public EObject literalValue() {return new EDouble(value);}

		public EObject toSymbolic() {
			return ETuple.make(FLOAT_ATOM, new EDouble(value));
		}
    }

    public static final Nil Nil = new Nil();
    public static class Nil extends Literal {
		private Nil() {}

		@Override
		public EObject literalValue() {return ERT.NIL;}

		public EObject toSymbolic() {return NIL_ATOM;}
    }

    public static class SelectList extends Operand {
		Operand[] list;
		public SelectList(Operand[] list) {this.list=list;}

		@Override
		public SelectList asSelectList() {return this;}

		public int size() {return list.length / 2;}
		public Operand getValue(int i) {return list[2*i];}
		public Label getLabel(int i)   {return (Label)list[2*i+1];}
		
		// when used in MapQuery/MapUpdate insns
        public int size(boolean keys_only) {
            if (keys_only) return list.length;
            return list.length / 2;
        }
		public SourceOperand getKey(int i, boolean keys_only) {
		    if (keys_only) return (SourceOperand)list[i];
		    return (SourceOperand)list[2*i];
		}
		public DestinationOperand getKeyDest(int i)   {return (DestinationOperand)list[2*i+1];}
		public SourceOperand getKeySrc(int i)   {return (SourceOperand)list[2*i+1];}

		public EObject toSymbolic() {
			EObject[] elems = new EObject[list.length];
			for (int i=0; i<list.length; i++) {
				elems[i] = list[i].toSymbolic();
			}
			return ETuple.make(LIST_ATOM, ESeq.fromArray(elems));
		}
    }

    public static class AllocList extends Operand {
		static final int WORDS  = 0;
		static final int FLOATS = 1;

		int[] list;
		public AllocList(int[] list) {this.list=list;}
		public AllocList(int words) {
			list=new int[2];
			list[0] = WORDS;
			list[1] = words;
		}

		@Override
		public AllocList asAllocList() {return this;}
		public EObject toSymbolic() {
			int len = list.length/2;

			if (len==1 && list[0] == WORDS)
				return new ESmall(list[1]); // Just words.

			EObject[] elems = new EObject[len];
			for (int i=0; i<len; i++) {
				elems[i] = ETuple.make(kindToSymbolic(list[2*i]),
									   new ESmall(list[2*i+1]));
			}
			return ETuple.make(ALLOC_ATOM, ESeq.fromArray(elems));
		}
		protected EAtom kindToSymbolic(int kind) {
			switch (kind) {
			case WORDS:  return WORDS_ATOM;
			case FLOATS: return FLOATS_ATOM;
			default:
				throw new IllegalArgumentException("Unknown alloc kind: "+kind);
			} // switch
		}
    }


    public static class Atom extends Literal {
		private EAtom value;
		public Atom(EAtom value) {this.value=value;}

		@Override
		public Atom asAtom() {return this;}

		@Override
		public Atom testAtom() {return this;}

		@Override
		public EObject literalValue() {return value;}

		public EAtom getEAtom() {return value;}
		public EObject toSymbolic() {
			return ETuple.make(ATOM_ATOM, value);
		}
		
		@Override
		public String toString() {
			return "atom:" + value.toString();
		}
    }

    public static class BitString extends Literal {
		public  final EBitString value;
		public BitString(EBitString value) {
			this.value = value;
		}

		@Override
		public EObject literalValue() {return value;}

		public long bitLength() {return value.bitSize();}

		@Override
		public EObject toSymbolic() {
			return value; // OBS: beam_disasm round up to nearest byte.
		}
    }

    public static class ByteString extends Literal {
		public final EString value;
		public ByteString(EString value) {
			this.value = value;
		}

		@Override
		public EObject literalValue() {return value;}

		public int byteLength() {return value.length();}

		@Override
		public EObject toSymbolic() {
			return ETuple.make(STRING_ATOM, value);
		}
    }

    public static class TableLiteral extends Literal {
		public final EObject value;
		public TableLiteral(EObject value) {this.value=value;}

		@Override
		public EObject literalValue() {return value;}

		@Override
		public TableLiteral testTableLiteral() {return this;}

		public EObject toSymbolic() {
			return ETuple.make(LITERAL_ATOM, value);
		}
    }

    public static class Label extends Operand {
		public final int nr;
		public Label(int nr) {this.nr=nr;}
		@Override
		public Label asLabel() {return this;}

		public EObject toSymbolic() {
			return ETuple.make(F_ATOM, new ESmall(nr));
		}
		
		@Override
		public int hashCode() {
			return nr;
		}
		
		@Override
		public boolean equals(Object obj) {
			if (obj instanceof Label) {
				Label lab = (Label) obj;
				return lab.nr == nr;
			} else {
				return false;
			}
		}
    }

    public static class XReg extends DestinationOperand {
		public final int nr;
		public XReg(int nr) {this.nr=nr;}

		@Override
		public XReg testXReg() {return this;}

		private static ArrayList<XReg> cache = new ArrayList<XReg>();
		public static XReg get(int nr) {
			while (cache.size() <= nr) {
				cache.add(new XReg(cache.size()));
			}
			return cache.get(nr);
		}

		public EObject toSymbolic() {
			return ETuple.make(X_ATOM, new ESmall(nr));
		}
    }

    public static class YReg extends DestinationOperand {
		public final int nr;
		public YReg(int nr) {this.nr=nr;}

		@Override
		public YReg asYReg() {return this;}
		@Override
		public YReg testYReg() {return this;}

		private static ArrayList<YReg> cache = new ArrayList<YReg>();
		public static YReg get(int nr) {
			while (cache.size() <= nr) {
				cache.add(new YReg(cache.size()));
			}
			return cache.get(nr);
		}

		public EObject toSymbolic() {
			return ETuple.make(Y_ATOM, new ESmall(nr));
		}
    }

    public static class FReg extends DestinationOperand {
		public final int nr;
		public FReg(int nr) {this.nr=nr;}

		@Override
		public FReg testFReg() {return this;}

		private static ArrayList<FReg> cache = new ArrayList<FReg>();
		public static FReg get(int nr) {
			while (cache.size() <= nr) {
				cache.add(new FReg(cache.size()));
			}
			return cache.get(nr);
		}

		public EObject toSymbolic() {
			return ETuple.make(FR_ATOM, new ESmall(nr));
		}
    }

}
src/main/java/erjang/beam/repr/Operands.java
  public static class BitString extends Literal {
public  final EBitString value;
public BitString(EBitString value) {
	this.value = value;
}

@Override
public EObject literalValue() {return value;}

public long bitLength() {return value.bitSize();}

@Override
public EObject toSymbolic() {
	return value; // OBS: beam_disasm round up to nearest byte.
}
  }
src/main/java/erjang/beam/BeamOpcode.java
public enum BeamOpcode {

	label			(0x01),
	func_info		(0x02),
	int_code_end		(0x03),
	call			(0x04),
	call_last		(0x05),
	call_only		(0x06),
	call_ext		(0x07),
	call_ext_last		(0x08),
	bif0			(0x09),
	bif1			(0x0a),
	bif2			(0x0b),
	allocate		(0x0c),
	allocate_heap		(0x0d),
	allocate_zero		(0x0e),
	allocate_heap_zero	(0x0f),
	test_heap		(0x10),
	init			(0x11),
	deallocate		(0x12),
	K_return		(0x13),
	send                    (0x14),
	remove_message		(0x15),
	timeout                 (0x16),
	loop_rec		(0x17),
	loop_rec_end		(0x18),
	wait			(0x19),
	wait_timeout		(0x1a),
	is_lt			(0x27),
	is_ge			(0x28),
	is_eq			(0x29),
	is_ne			(0x2a),
	is_eq_exact		(0x2b),
	is_ne_exact		(0x2c),
	is_integer		(0x2d),
	is_float		(0x2e),
	is_number		(0x2f),
	is_atom			(0x30),
	is_pid			(0x31),
	is_reference		(0x32),
	is_port			(0x33),
	is_nil			(0x34),
	is_binary		(0x35),
	is_list			(0x37),
	is_nonempty_list	(0x38),
	is_tuple		(0x39),
	test_arity		(0x3a),
	select_val		(0x3b),
	select_tuple_arity	(0x3c),
	jump			(0x3d),
	K_catch			(0x3e),
	catch_end		(0x3f),
	move			(0x40),
	get_list		(0x41),
	get_tuple_element	(0x42),
	set_tuple_element	(0x43),
	put_string		(0x44),
	put_list                (0x45),
	put_tuple		(0x46),
	put			(0x47),
	badmatch		(0x48),
	if_end			(0x49),
	case_end		(0x4a),
	call_fun		(0x4b),
	is_function		(0x4d),
	call_ext_only		(0x4e),
	bs_put_integer		(0x59),
	bs_put_binary		(0x5a),
	bs_put_float		(0x5b),
	bs_put_string		(0x5c),
	fclearerror		(0x5e),
	fcheckerror		(0x5f),
	fmove			(0x60),
	fconv			(0x61),
	fadd			(0x62),
	fsub			(0x63),
	fmul			(0x64),
	fdiv			(0x65),
	fnegate			(0x66),
	make_fun2		(0x67),
	K_try			(0x68),
	try_end			(0x69),
	try_case		(0x6a),
	try_case_end		(0x6b),
	raise			(0x6c),
	bs_init2		(0x6d),
	bs_bits_to_bytes	(0x6e),
	bs_add			(0x6f),
	apply			(0x70),
	apply_last		(0x71),
	is_boolean		(0x72),
	is_function2		(0x73),
	bs_start_match2		(0x74),
	bs_get_integer2		(0x75),
	bs_get_float2		(0x76),
	bs_get_binary2		(0x77),
	bs_skip_bits2		(0x78),
	bs_test_tail2		(0x79),
	bs_save2		(0x7a),
	bs_restore2		(0x7b),
	gc_bif1			(0x7c),
	gc_bif2			(0x7d),
	is_bitstr		(0x81),
	bs_context_to_binary    (0x82),
	bs_test_unit		(0x83),
	bs_match_string		(0x84),
	bs_init_writable	(0x85),
	bs_append		(0x86),
	bs_private_append	(0x87),
	trim                    (0x88),
	bs_init_bits		(0x89),
	bs_get_utf8		(0x8a),
	bs_skip_utf8		(0x8b),
	bs_get_utf16		(0x8c),
	bs_skip_utf16		(0x8d),
	bs_get_utf32		(0x8e),
	bs_skip_utf32		(0x8f),
	bs_utf8_size		(0x90),
	bs_put_utf8		(0x91),
	bs_utf16_size		(0x92),
	bs_put_utf16		(0x93),
	bs_put_utf32		(0x94),
	on_load				(0x95),
	
	recv_mark           (0x96),
	recv_set            (0x97),
	gc_bif3             (0x98),
        line                (0x99),

        put_map_assoc       (0x9a),
        put_map_exact       (0x9b),
        is_map              (0x9c),
        has_map_fields      (0x9d),
        get_map_elements    (0x9e),

	// Opcode groups
	
	test,
	bif,
	gc_bif,
	arithfbif,

	// Synthetic (Erjang-internal) opcodes
	i_call_fun_last            (0xF0),
	
	// illegal op-code
	NONE
	;

	public final EAtom symbol = EAtom.intern(name().startsWith("K_")
						 ? name().substring(2)
						 : name());

	public final int encoding;
	BeamOpcode() {this.encoding = -1;}
	BeamOpcode(int encoding) {this.encoding = encoding;}

	static BeamOpcode[] decodeMap;
	static {
		decodeMap = new BeamOpcode[256];
		for (BeamOpcode x : EnumSet.allOf(BeamOpcode.class))
			if (x.encoding >= 0) decodeMap[x.encoding] = x;
	}
	public static BeamOpcode decode(int opcode) {return decodeMap[opcode];}


	static EAtom TRY = EAtom.intern("try");
	static EAtom CATCH = EAtom.intern("catch");
	static EAtom RETURN = EAtom.intern("return");
	
	static public BeamOpcode get(EAtom sym) {
		String name = sym.getName();
		if (sym == TRY) return K_try;
		if (sym == CATCH) return K_catch;
		if (sym == RETURN) return K_return;
		
		return valueOf(name);
	}


}
src/main/java/erjang/beam/repr/Operands.java
  public static class TableLiteral extends Literal {
public final EObject value;
public TableLiteral(EObject value) {this.value=value;}

@Override
public EObject literalValue() {return value;}

@Override
public TableLiteral testTableLiteral() {return this;}

public EObject toSymbolic() {
	return ETuple.make(LITERAL_ATOM, value);
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static abstract class Operand {
public SourceOperand asSource() {
	throw new IllegalArgumentException("Not a source: "+this);
}
public DestinationOperand asDestination() {
	throw new IllegalArgumentException("Not a destination: "+this);
}
public Label asLabel() {
	throw new IllegalArgumentException("Not a label: "+this);
}
public Literal asLiteral() {
	throw new IllegalArgumentException("Not a literal: "+this);
}
public Atom asAtom() {
	throw new IllegalArgumentException("Not an atom: "+this);
}
public CodeInt asCodeInt() {
	throw new IllegalArgumentException("Not a code integer: "+this);
}
public SelectList asSelectList() {
	throw new IllegalArgumentException("Not a select list: "+this);
}
public AllocList asAllocList() {
	throw new IllegalArgumentException("Not an alloc list: "+this);
}
public YReg asYReg() {
	throw new IllegalArgumentException("Not a Y register: "+this);
}

public TableLiteral testTableLiteral() { return null; }
public Atom testAtom() { return null; }
public Int testInt() { return null; }
public BigInt testBigInt() { return null; }
public Float testFloat() { return null; }
public SelectList testSelectList() { return null; }
public AllocList testAllocList() { return null; }
public DestinationOperand testDestination() {return null;}
public XReg testXReg() { return null; }
public YReg testYReg() { return null; }
public FReg testFReg() { return null; }

public abstract EObject toSymbolic();
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class SelectList extends Operand {
Operand[] list;
public SelectList(Operand[] list) {this.list=list;}

@Override
public SelectList asSelectList() {return this;}

public int size() {return list.length / 2;}
public Operand getValue(int i) {return list[2*i];}
public Label getLabel(int i)   {return (Label)list[2*i+1];}
		
// when used in MapQuery/MapUpdate insns
      public int size(boolean keys_only) {
          if (keys_only) return list.length;
          return list.length / 2;
      }
public SourceOperand getKey(int i, boolean keys_only) {
    if (keys_only) return (SourceOperand)list[i];
    return (SourceOperand)list[2*i];
}
public DestinationOperand getKeyDest(int i)   {return (DestinationOperand)list[2*i+1];}
public SourceOperand getKeySrc(int i)   {return (SourceOperand)list[2*i+1];}

public EObject toSymbolic() {
	EObject[] elems = new EObject[list.length];
	for (int i=0; i<list.length; i++) {
		elems[i] = list[i].toSymbolic();
	}
	return ETuple.make(LIST_ATOM, ESeq.fromArray(elems));
}
  }
src/main/java/erjang/EAtom.java
public final class EAtom extends EObject implements CharSequence {

	public EAtom testAtom() {
		return this;
	}

	@Override
	public boolean match(ETermPattern matcher, EMatchContext r) {
		return matcher.match(this, r);
	}

	@Override
	public ETermPattern compileMatch(Set<Integer> out) {
		return EPattern.compilePattern(this, out);
	}



	public EAtom testBoolean() {
		if ( this==ERT.TRUE || this == ERT.FALSE) return this;
		return null;
	}

	private final String value;
	public final int hash;

	@Override
	int compare_same(EObject rhs) {
		return compareTo((EAtom) rhs);
	}
	
	private static ConcurrentHashMap<String, EAtom> interns = new ConcurrentHashMap<String, EAtom>();

	private EAtom(String name) {
		this.value = name;
		this.hash = name.hashCode();
	}

	@Override
	int cmp_order() {
		return CMP_ORDER_ATOM;
	}
	
	static Pattern ATOM = Pattern.compile("[a-z_]([a-z]|[A-Z]|@|_|[0-9])*");

	@Override
	public String toString() {
		if (ATOM.matcher(value).matches()) {
			return value;
		} else {
			return "'" + encode_escapes(value) + "'";
		}
	}

	/**
	 * @param value2
	 * @return
	 */
	private String encode_escapes(CharSequence seq) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < seq.length(); i++) {
			char ch = seq.charAt(i);
			if ((ch >= 0x20 && ch <= 0x7f) && ch != '\'' && ch != 0x7f) {
				sb.append(ch);
			} else {
				switch (ch) {
				case '\t':
					sb.append("\\t");
					break;
				case '\n':
					sb.append("\\n");
					break;
				case '\r':
					sb.append("\\r");
					break;
				case '\f':
					sb.append("\\f");
					break;
				case '\b':
					sb.append("\\b");
					break;
				case '\'':
					sb.append("\\'");
					break;
				default:
					sb.append("\\");
					sb.append('x');
					if (ch < 0x10) sb.append('0');
					sb.append(Integer.toHexString(ch).toUpperCase());
				}
			}
		}
		return sb.toString();
	}

	public boolean isAtom(String name) {
		return interns.contains(name);
	}

	@Override
	public boolean equals(Object obj) {
		return this == obj;
	}

	@Override
	public int hashCode() {
		return hash;
	}

	// used by NIF API
	public byte[] latin1_bytes() {
		return value.getBytes(StandardCharsets.ISO_8859_1);
	}

	// used by NIF API
	public int latin1_length() {
		return value.length();
	}

	public static EAtom intern(String name) {
		
		
		
		EAtom res = interns.get(name);
		if (res == null) {

			boolean changed = false;
			char[] data = name.toCharArray();
			for (int i = 0; i < data.length; i++) 
			{
				if ((data[i] & 0xff00) != 0) changed = true;
				data[i] = (char) (data[i] & 0xff);
			}

			if (changed)
				name = new String(data);
			
			EAtom new_val = new EAtom(name);
			do {
				res = interns.putIfAbsent(name, new_val);
			} while (res == null);
		}

		return res;
	}

	public static EAtom existing_atom(String name) {
		EAtom res = interns.get(name);
		if (res == null) {
			throw ERT.badarg(EString.fromString(name));
		}

		return res;
	}

	public static EAtom existing_atom_or_null(String name) {
		return interns.get(name);
	}

	@Override
	public char charAt(int index) {
		return value.charAt(index);
	}

	@Override
	public int length() {
		return value.length();
	}

	@Override
	public CharSequence subSequence(int start, int end) {
		return value.subSequence(start, end);
	}

	public String getName() {
		return value;
	}

	private static final Type EATOM_TYPE = Type.getType(EAtom.class);
	private static final Type STRING_TYPE = Type.getType(String.class);

	@Override
	public org.objectweb.asm.Type emit_const(MethodVisitor fa) {

		Type type = EATOM_TYPE;

		fa.visitLdcInsn(value);
		fa.visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(),
				"intern", "(" + STRING_TYPE.getDescriptor() + ")"
						+ type.getDescriptor());

		return type;
	}

	/**
	 * @param other
	 * @return
	 */
	public int compareTo(EAtom other) {
		if (this == other) return 0;
		return value.compareTo(other.value);
	}
	
	/* (non-Javadoc)
	 * @see erjang.EObject#equalsExactly(erjang.EObject)
	 */
	@Override
	public boolean equalsExactly(EObject rhs) {
		return rhs == this;
	}

	/**
	 * @param buf
	 * @return
	 */
	public static EAtom intern(byte[] buf) {
		char[] data = new char[buf.length];
		for (int i = 0; i < data.length; i++) {
			data[i] = (char) (buf[i] & 0xff);
		}
		String str = new String(data);
		return intern(str);
	}

	public static EAtom read(EInputStream ei) throws IOException {
		return ei.read_atom();
	}

	@Override
	public void encode(EOutputStream eos) {
		eos.write_atom(this.value);
	}
}
src/main/java/erjang/beam/repr/Operands.java
  public static class AllocList extends Operand {
static final int WORDS  = 0;
static final int FLOATS = 1;

int[] list;
public AllocList(int[] list) {this.list=list;}
public AllocList(int words) {
	list=new int[2];
	list[0] = WORDS;
	list[1] = words;
}

@Override
public AllocList asAllocList() {return this;}
public EObject toSymbolic() {
	int len = list.length/2;

	if (len==1 && list[0] == WORDS)
		return new ESmall(list[1]); // Just words.

	EObject[] elems = new EObject[len];
	for (int i=0; i<len; i++) {
		elems[i] = ETuple.make(kindToSymbolic(list[2*i]),
							   new ESmall(list[2*i+1]));
	}
	return ETuple.make(ALLOC_ATOM, ESeq.fromArray(elems));
}
protected EAtom kindToSymbolic(int kind) {
	switch (kind) {
	case WORDS:  return WORDS_ATOM;
	case FLOATS: return FLOATS_ATOM;
	default:
		throw new IllegalArgumentException("Unknown alloc kind: "+kind);
	} // switch
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class XReg extends DestinationOperand {
public final int nr;
public XReg(int nr) {this.nr=nr;}

@Override
public XReg testXReg() {return this;}

private static ArrayList<XReg> cache = new ArrayList<XReg>();
public static XReg get(int nr) {
	while (cache.size() <= nr) {
		cache.add(new XReg(cache.size()));
	}
	return cache.get(nr);
}

public EObject toSymbolic() {
	return ETuple.make(X_ATOM, new ESmall(nr));
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class ByteString extends Literal {
public final EString value;
public ByteString(EString value) {
	this.value = value;
}

@Override
public EObject literalValue() {return value;}

public int byteLength() {return value.length();}

@Override
public EObject toSymbolic() {
	return ETuple.make(STRING_ATOM, value);
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static abstract class DestinationOperand extends SourceOperand {
@Override
public DestinationOperand asDestination() {return this;}
@Override
public DestinationOperand testDestination() {return this;}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class Atom extends Literal {
private EAtom value;
public Atom(EAtom value) {this.value=value;}

@Override
public Atom asAtom() {return this;}

@Override
public Atom testAtom() {return this;}

@Override
public EObject literalValue() {return value;}

public EAtom getEAtom() {return value;}
public EObject toSymbolic() {
	return ETuple.make(ATOM_ATOM, value);
}
		
@Override
public String toString() {
	return "atom:" + value.toString();
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class FReg extends DestinationOperand {
public final int nr;
public FReg(int nr) {this.nr=nr;}

@Override
public FReg testFReg() {return this;}

private static ArrayList<FReg> cache = new ArrayList<FReg>();
public static FReg get(int nr) {
	while (cache.size() <= nr) {
		cache.add(new FReg(cache.size()));
	}
	return cache.get(nr);
}

public EObject toSymbolic() {
	return ETuple.make(FR_ATOM, new ESmall(nr));
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static abstract class SourceOperand extends Operand {
@Override
public SourceOperand asSource() {return this;}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static abstract class Literal extends SourceOperand {
@Override
public Literal asLiteral() {return this;}
public abstract EObject literalValue();
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class YReg extends DestinationOperand {
public final int nr;
public YReg(int nr) {this.nr=nr;}

@Override
public YReg asYReg() {return this;}
@Override
public YReg testYReg() {return this;}

private static ArrayList<YReg> cache = new ArrayList<YReg>();
public static YReg get(int nr) {
	while (cache.size() <= nr) {
		cache.add(new YReg(cache.size()));
	}
	return cache.get(nr);
}

public EObject toSymbolic() {
	return ETuple.make(Y_ATOM, new ESmall(nr));
}
  }
src/main/java/erjang/beam/repr/Operands.java
  public static class Label extends Operand {
public final int nr;
public Label(int nr) {this.nr=nr;}
@Override
public Label asLabel() {return this;}

public EObject toSymbolic() {
	return ETuple.make(F_ATOM, new ESmall(nr));
}
		
@Override
public int hashCode() {
	return nr;
}
		
@Override
public boolean equals(Object obj) {
	if (obj instanceof Label) {
		Label lab = (Label) obj;
		return lab.nr == nr;
	} else {
		return false;
	}
}
  }
src/main/java/erjang/beam/repr/Insn.java
public class Insn implements BeamInstruction {

	protected final BeamOpcode opcode;
	public Insn(BeamOpcode opcode) {this.opcode = opcode;}
	public BeamOpcode opcode() {return opcode;}

	public String toString() {return toSymbolic().toString();}
	public EObject toSymbolic() {
	    return opcode.symbol;
	}
	public final ETuple toSymbolicTuple() {
		EObject symInsn0 = toSymbolic();
		ETuple symInsn = (symInsn0 instanceof ETuple)?
			((ETuple)symInsn0) : ETuple.make(symInsn0);
		return symInsn;
	}

	private static EObject NOFAIL_REPR = ETuple.make(F_ATOM, new ESmall(0));
	private static EObject START_REPR = ETuple.make(ATOM_ATOM, START_ATOM);
	static EObject labelToSymbolic(Label label) {
		return label==null? NOFAIL_REPR : label.toSymbolic();
	}
	static EObject labelToSymbolic_nf(Label label) {
		return label==null? NOFAIL_ATOM : label.toSymbolic();
	}

	static EObject bsFieldFlagsToSymbolic(int flags) {
		return ETuple.make(FIELD_FLAGS_ATOM, new ESmall(flags));
	}
	/*============================================================
	 *                     Instruction formats
	 * Class names encode the format - the parameter types - as follows:
	 * I - Integer
	 * A - Atom
	 * Bi - Bitstring
	 * By - Bytestring
	 * S - Source operand: register or literal
	 * D - Destination operand: register
	 * L - Label
	 * K - Optional label (fail label)
	 * E - External function
	 * F - Anonymous function object
	 * W - Allocation size
	 */

	public static class I extends Insn { // E.g. 'deallocate'
		public final int i1;
		public I(BeamOpcode opcode, int i1) {
			super(opcode);
			this.i1 = i1;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol, new ESmall(i1));
		}
	}

	public static class S extends Insn { // E.g. 'put'
		public final SourceOperand src;
		public S(BeamOpcode opcode, SourceOperand src) {
			super(opcode);
			this.src = src;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol, src.toSymbolic());
		}
	}

	public static class D extends Insn { // E.g. 'init'
		public final DestinationOperand dest;
		public D(BeamOpcode opcode, DestinationOperand dest) {
			super(opcode);
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol, dest.toSymbolic());
		}
	}

	public static class L extends Insn { // E.g. 'jump'
		public final Label label;
		public L(BeamOpcode opcode, Label label) {
			super(opcode);
			this.label = label;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol, label.toSymbolic());
		}
	}

	public static class F extends Insn { // E.g. 'make_fun2'
		public final int anon_fun_no;
		public final AnonFun anon_fun;
		public F(BeamOpcode opcode, int anon_fun_no, AnonFun anon_fun) {
			super(opcode);
			this.anon_fun_no = anon_fun_no;
			this.anon_fun = anon_fun;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   anon_fun.toSymbolic(),
							   new ESmall(anon_fun_no),
							   new ESmall(anon_fun.old_uniq),
							   new ESmall(anon_fun.free_vars));
		}
	}

	public static class Y extends Insn { // E.g. 'catch'
		public final YReg y;
		public Y(BeamOpcode opcode, YReg y) {
			super(opcode);
			this.y = y;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol, y.toSymbolic());
		}
	}

	public static class By extends Insn { // E.g. 'bs_put_string'
		public final ByteString bin;
		public By(BeamOpcode opcode, ByteString bin) {
			super(opcode);
			this.bin = bin;
		}

		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(bin.byteLength()),
							   bin.toSymbolic());
		}
	}

	public static class DI extends Insn { // E.g. 'bs_save2'
		public final DestinationOperand dest;
		public final int i2;
		public final boolean is_saverestore;
		public DI(BeamOpcode opcode, DestinationOperand dest, int i2, boolean is_saverestore) {
			super(opcode);
			this.dest = dest;
			this.i2 = i2;
			this.is_saverestore = is_saverestore;
		}
		public ETuple toSymbolic() {
			if (is_saverestore)
				return ETuple.make(opcode.symbol,
								   dest.toSymbolic(),
								   (i2==-1 ? START_REPR : new ESmall(i2)));
			else
				return ETuple.make(opcode.symbol,
								   dest.toSymbolic(),
								   new ESmall(i2));
		}
	}

	public static class SD extends Insn { // E.g. 'move'
		public final SourceOperand src;
		public final DestinationOperand dest;
		public SD(BeamOpcode opcode, SourceOperand src, DestinationOperand dest) {
			super(opcode);
			this.src = src;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   src.toSymbolic(),
							   dest.toSymbolic());
		}
	}

	public static class LD extends L { // E.g. 'loop_rec'
		public final DestinationOperand dest;
		public final boolean is_test;
		public LD(BeamOpcode opcode, Label label, DestinationOperand dest) {
			super(opcode, label);
			this.dest = dest;
			this.is_test = false;
		}
		public LD(BeamOpcode opcode, Label label, DestinationOperand dest, boolean is_test) {
			super(opcode, label);
			this.dest = dest;
			this.is_test = is_test;
		}
		public ETuple toSymbolic() {
			if (is_test)
				return ETuple.make(TEST_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(dest.toSymbolic()));
			else
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   dest.toSymbolic());
		}
	}

	public static class YL extends Insn { // E.g. 'catch'
		public final YReg y;
		public final Label label;
		public YL(BeamOpcode opcode, YReg y, Label label) {
			super(opcode);
			this.y = y;
			this.label = label;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   y.toSymbolic(),
							   label.toSymbolic());
		}
	}

	public static class LS extends L { // E.g. 'is_nil'
		public final SourceOperand src;
		public final boolean is_test;
		public LS(BeamOpcode opcode, Label label, SourceOperand src, boolean is_test) {
			super(opcode, label);
			this.src = src;
			this.is_test = is_test;
		}
		public ETuple toSymbolic() {
			if (is_test)
				return ETuple.make(TEST_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(src.toSymbolic()));
			else
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   src.toSymbolic());
		}
	}

	public static class II extends I { // E.g. 'allocate'
		public final int i2;
		public II(BeamOpcode opcode, int i1, int i2) {
			super(opcode, i1);
			this.i2 = i2;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(i1),
							   new ESmall(i2));
		}
	}

	public static class IL extends I { // E.g. 'call'
		public final Label label;
		public final boolean is_call;
		public final FunctionInfo functionAtLabel;

		public IL(BeamOpcode opcode, int i1, Label label,  FunctionInfo fal) {
			super(opcode, i1);
			this.label = label;
			this.is_call = true;
			this.functionAtLabel = fal;
		}

		public ETuple toSymbolic() {
			if (is_call)
				return ETuple.make(opcode.symbol,
								   new ESmall(i1),
								   functionAtLabel.toSymbolic());
			else
				return ETuple.make(opcode.symbol,
								   new ESmall(i1),
								   label.toSymbolic());
		}
	}

	public static class SS extends Insn { // E.g. 'raise'
		public final SourceOperand src1, src2;
		public SS(BeamOpcode opcode, SourceOperand src1, SourceOperand src2) {
			super(opcode);
			this.src1 = src1;
			this.src2 = src2;
		}
		public ETuple toSymbolic() {
			if (opcode == BeamOpcode.raise)
				return ETuple.make(opcode.symbol,
								   new Label(0).toSymbolic(),
								   EList.make(src1.toSymbolic(),
											  src2.toSymbolic()),
								   XReg.get(0).toSymbolic());
			else
				return ETuple.make(opcode.symbol,
								   src1.toSymbolic(),
								   src2.toSymbolic());
		}
	}

	public static class AAI extends Insn { // E.g. 'func_info'
		public final Atom a1,a2;
		public final int i3;
		public AAI(BeamOpcode opcode, Atom a1, Atom a2, int i3) {
			super(opcode);
			this.a1 = a1;
			this.a2 = a2;
			this.i3 = i3;
		}

		public ExtFun getExtFun() {
			return new ExtFun(a1.getEAtom(), a2.getEAtom(), i3);
		}

		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   a1.toSymbolic(),
							   a2.toSymbolic(),
							   new ESmall(i3));
		}
	}

	public static class IE extends I { // E.g. 'call_ext'
		public final ExtFun ext_fun;
		public IE(BeamOpcode opcode, int i1, ExtFun ext_fun) {
			super(opcode, i1);
			this.ext_fun = ext_fun;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(i1),
							   ext_fun.toSymbolic());
		}
	}

	public static class ID extends Insn { // E.g. 'put_tuple'
		public final int i1;
		public final DestinationOperand dest;
		public ID(BeamOpcode opcode, int i1, DestinationOperand dest) {
			super(opcode);
			this.i1 = i1;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(i1),
							   dest.toSymbolic());
		}
	}

	public static class WI extends Insn { // E.g. 'test_heap'
		public final AllocList alist;
		public final int i2;
		public WI(BeamOpcode opcode, AllocList alist, int i2) {
			super(opcode);
			this.alist = alist;
			this.i2 = i2;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   alist.toSymbolic(),
							   new ESmall(i2));
		}
	}

	public static class IWI extends I { // E.g. 'allocate_heap'
		public final int i3;
		public final AllocList al;
		public IWI(BeamOpcode opcode, int i1, AllocList al, int i3) {
			super(opcode, i1);
			this.al = al;
			this.i3 = i3;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(i1),
							   al.toSymbolic(),
							   new ESmall(i3));
		}
	}

	public static class LDI extends LD { // E.g. 'test_arity'
		public final int i;
		public LDI(BeamOpcode opcode, Label label, DestinationOperand dest, int i, boolean is_test) {
			super(opcode, label, dest, is_test);
			this.i = i;
		}
		public ETuple toSymbolic() {
			if (is_test)
				return ETuple.make(TEST_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(dest.toSymbolic(),
											  new ESmall(i)));
			else
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   dest.toSymbolic(),
								   new ESmall(i));
		}
	}

	public static class SID extends Insn { // E.g. 'get_tuple_element'
		public final SourceOperand src;
		public final int i;
		public final DestinationOperand dest;
		public SID(BeamOpcode opcode, SourceOperand src, int i, DestinationOperand dest) {
			super(opcode);
			this.src = src;
			this.i = i;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   src.toSymbolic(),
							   new ESmall(i),
							   dest.toSymbolic());
		}
	}

	public static class LSS extends L { // E.g. 'is_eq_exact'
		public final SourceOperand src1, src2;
		public final boolean is_test;
		public LSS(BeamOpcode opcode, Label label, SourceOperand src1, SourceOperand src2, boolean is_test) {
			super(opcode, label);
			this.src1 = src1;
			this.src2 = src2;
			this.is_test = is_test;
		}
		public ETuple toSymbolic() {
			if (is_test)
				return ETuple.make(TEST_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(src1.toSymbolic(),
											  src2.toSymbolic()));
			else
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   src1.toSymbolic(),
								   src2.toSymbolic());
		}
	}

	public static class LDS extends LD { // E.g. 'is_function2'
		public final SourceOperand src;
		public LDS(BeamOpcode opcode, Label label, DestinationOperand dest, SourceOperand src, boolean is_test) {
			super(opcode, label, dest, is_test);
			this.src = src;
		}

		public ETuple toSymbolic() {
			if (is_test)
				return ETuple.make(TEST_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(dest.toSymbolic(),
											  src.toSymbolic()));
			else
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   dest.toSymbolic(),
								   src.toSymbolic());
		}
	}

	public static class LSD extends Insn { // E.g. 'bs_utf8_size'
		public final Label label;
		public final SourceOperand src;
		public final DestinationOperand dest;
		public LSD(BeamOpcode opcode, Label label, SourceOperand src, DestinationOperand dest) {
			super(opcode);
			this.label = label;
			this.src = src;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   label.toSymbolic(),
							   src.toSymbolic(),
							   dest.toSymbolic());
		}
	}

	public static class SDD extends Insn { // E.g. 'get_list'
		public final SourceOperand src;
		public final DestinationOperand dest1;
		public final DestinationOperand dest2;
		public SDD(BeamOpcode opcode, SourceOperand src, DestinationOperand dest1, DestinationOperand dest2) {
			super(opcode);
			this.src = src;
			this.dest1 = dest1;
			this.dest2 = dest2;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   src.toSymbolic(),
							   dest1.toSymbolic(),
							   dest2.toSymbolic());
		}
	}

	public static class SSD extends Insn { // E.g. 'move'
		public final SourceOperand src1;
		public final SourceOperand src2;
		public final DestinationOperand dest;
		public SSD(BeamOpcode opcode, SourceOperand src1, SourceOperand src2, DestinationOperand dest) {
			super(opcode);
			this.src1 = src1;
			this.src2 = src2;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   src1.toSymbolic(),
							   src2.toSymbolic(),
							   dest.toSymbolic());
		}
	}

	public static class SDI extends Insn { // E.g. 'set_tuple_element'
		public final SourceOperand src;
		public final DestinationOperand dest;
		public final int i;
		public SDI(BeamOpcode opcode, SourceOperand src, DestinationOperand dest, int i) {
			super(opcode);
			this.src = src;
			this.dest = dest;
			this.i = i;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   src.toSymbolic(),
							   dest.toSymbolic(),
							   new ESmall(i));
		}
	}


	public static class ILI extends IL { // E.g. 'call'
		public final int i3;
		public final boolean is_call;
		public ILI(BeamOpcode opcode, int i1, Label label, int i3, FunctionInfo fal) {
			super(opcode, i1, label, fal);
			this.i3 = i3;
			this.is_call = true;
		}

		public ETuple toSymbolic() {
			if (is_call)
				return ETuple.make(opcode.symbol,
								   new ESmall(i1),
								   functionAtLabel.toSymbolic(),
								   new ESmall(i3));
			else
				return ETuple.make(opcode.symbol,
								   new ESmall(i1),
								   label.toSymbolic(),
								   new ESmall(i3));
		}
	}

	public static class IEI extends IE { // E.g. 'call'
		public final int i3;
		public IEI(BeamOpcode opcode, int i1, ExtFun ext_fun, int i3) {
			super(opcode, i1, ext_fun);
			this.i3 = i3;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(i1),
							   ext_fun.toSymbolic(),
							   new ESmall(i3));
		}
	}

	public static class ByD extends Insn { // E.g. 'put_string'
		public final ByteString bin;
		public final DestinationOperand dest;
		public ByD(BeamOpcode opcode, ByteString bin, DestinationOperand dest) {
			super(opcode);
			this.bin = bin;
			this.dest = dest;
		}

		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   new ESmall(bin.byteLength()),
							   bin.toSymbolic(),
							   dest.toSymbolic());
		}
	}

	public static class LSSD extends Insn { // E.g. 'fmul'
		public final Label label;
		public final SourceOperand src1;
		public final SourceOperand src2;
		public final DestinationOperand dest;
		public final boolean is_arithfbif;
		public LSSD(BeamOpcode opcode, Label label,
					SourceOperand src1, SourceOperand src2,
					DestinationOperand dest,
					boolean is_arithfbif)
		{
			super(opcode);
			this.label = label;
			this.src1 = src1;
			this.src2 = src2;
			this.dest = dest;
			this.is_arithfbif = is_arithfbif;
		}
		public ETuple toSymbolic() {
			if (is_arithfbif)
				return ETuple.make(ARITHFBIF_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(src1.toSymbolic(),
											  src2.toSymbolic()),
								   dest.toSymbolic());
			else
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   src1.toSymbolic(),
								   src2.toSymbolic(),
								   dest.toSymbolic());
		}
	}

	public static class LSSID extends Insn { // E.g. 'bs_add'
		public final Label label;
		public final SourceOperand src1;
		public final SourceOperand src2;
		public final int i3;
		public final DestinationOperand dest;
		public LSSID(BeamOpcode opcode, Label label,
					 SourceOperand src1, SourceOperand src2,
					 int i3, DestinationOperand dest)
		{
			super(opcode);
			this.label = label;
			this.src1 = src1;
			this.src2 = src2;
			this.i3 = i3;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   label.toSymbolic(),
							   EList.make(src1.toSymbolic(),
										  src2.toSymbolic(),
										  new ESmall(i3)),
							   dest.toSymbolic());
		}
	}

	public static class LDBi extends LD { // E.g. 'bs_match_string'
		public final BitString bin;
		public LDBi(BeamOpcode opcode, Label label, DestinationOperand dest, BitString bin) {
			super(opcode, label, dest, true);
			this.bin = bin;
		}

		public ETuple toSymbolic() {
			return ETuple.make(TEST_ATOM,
							   opcode.symbol,
							   label.toSymbolic(),
							   EList.make(dest.toSymbolic(),
										  ERT.box(bin.bitLength()),
										  bin.toSymbolic()));
		}
	}

	public static class LDII extends LD { // E.g. 'bs_skip_utf8'
		public final int i3, i4;
		public LDII(BeamOpcode opcode, Label label, DestinationOperand dest, int i3, int i4) {
			super(opcode, label, dest, true);
			this.i3 = i3;
			this.i4 = i4;
		}

		public ETuple toSymbolic() {
			return ETuple.make(TEST_ATOM,
							   opcode.symbol,
							   label.toSymbolic(),
							   EList.make(dest.toSymbolic(),
										  new ESmall(i3),
										  bsFieldFlagsToSymbolic(i4)));
		}
	}


	public static class LSIIS extends Insn { // E.g. 'bs_put_integer'
		public final Label label;
		public final SourceOperand src2, src5;
		public final int i3, i4;
		public final boolean is_bs;
		public LSIIS(BeamOpcode opcode, Label label, SourceOperand src2, int i3, int i4, SourceOperand src5, boolean is_bs) {
			super(opcode);
			this.label = label;
			this.src2 = src2;
			this.i3 = i3;
			this.i4 = i4;
			this.src5 = src5;
			this.is_bs = is_bs;
		}

		public ETuple toSymbolic() {
			if (is_bs)
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   src2.toSymbolic(),
								   new ESmall(i3),
								   bsFieldFlagsToSymbolic(i4),
								   src5.toSymbolic());
			else
				throw new erjang.NotImplemented();
		}
	}

	public static class LIS extends Insn { // E.g. 'bs_put_utf8'
		public final Label label;
		public final int i2;
		public final SourceOperand src;
		public final boolean is_bsput;
		public LIS(BeamOpcode opcode, Label label, int i2, SourceOperand src, boolean is_bsput) {
			super(opcode);
			this.label = label;
			this.i2 = i2;
			this.src = src;
			this.is_bsput = is_bsput;
		}

		public ETuple toSymbolic() {
			if (is_bsput)
				return ETuple.make(opcode.symbol,
								   label.toSymbolic(),
								   bsFieldFlagsToSymbolic(i2),
								   src.toSymbolic());
			else
				throw new erjang.NotImplemented();
		}
	}


	public static class LDIID extends LD { // E.g. 'bs_start_match2'
		public final int i3, i4;
		public final DestinationOperand dest5;
		public final boolean i4_is_bsflags;
		public LDIID(BeamOpcode opcode, Label label, DestinationOperand dest2, int i3, int i4, DestinationOperand dest5, boolean i4_is_bsflags) {
			super(opcode, label, dest2, true);
			this.i3 = i3;
			this.i4 = i4;
			this.dest5 = dest5;
			this.i4_is_bsflags = i4_is_bsflags;
		}

		public ETuple toSymbolic() {
			return ETuple.make(TEST_ATOM,
							   opcode.symbol,
							   label.toSymbolic(),
							   EList.make(dest.toSymbolic(),
										  new ESmall(i3),
										  i4_is_bsflags
										  ? bsFieldFlagsToSymbolic(i4)
										  : new ESmall(i4),
										  dest5.toSymbolic()));
		}
	}

	public static class LSIIID extends Insn { // E.g. 'bs_init2'
		public final Label label;
		public final SourceOperand src2;
		public final int i3, i4,i5;
		public final DestinationOperand dest;
		public final boolean is_bs;
		public LSIIID(BeamOpcode opcode, Label label,
					  SourceOperand src2,
					  int i3, int i4, int i5,
					  DestinationOperand dest,
					  boolean is_bs)
		{
			super(opcode);
			this.label = label;
			this.src2 = src2;
			this.i3 = i3;
			this.i4 = i4;
			this.i5 = i5;
			this.dest = dest;
			this.is_bs = is_bs;
		}

		public ETuple toSymbolic() {
			if (is_bs)
				return ETuple.make(opcode.symbol,
								   labelToSymbolic(label),
								   src2.toSymbolic(),
								   new ESmall(i3),
								   new ESmall(i4),
								   bsFieldFlagsToSymbolic(i5),
								   dest.toSymbolic());
			else
				throw new erjang.NotImplemented();
		}
	}

	public static class LDSII extends LD { // E.g. 'bs_skip_bits2'
		public final SourceOperand src3;
		public final int i4, i5;

		public LDSII(BeamOpcode opcode, Label label,
					 DestinationOperand dest, SourceOperand src3,
					 int i4, int i5)
		{
			super(opcode, label, dest, true);
			this.src3 = src3;
			this.i4 = i4;
			this.i5 = i5;
		}
		public ETuple toSymbolic() {
			return ETuple.make(TEST_ATOM,
							   opcode.symbol,
							   label.toSymbolic(),
							   EList.make(dest.toSymbolic(),
										  src3.toSymbolic(),
										  new ESmall(i4),
										  bsFieldFlagsToSymbolic(i5)));
		}
	}

	public static class LDISIID extends LD { // E.g. 'bs_get_integer2'
		public final int i3;
		public final SourceOperand src4;
		public final int i5, i6;
		public final DestinationOperand dest7;

		public LDISIID(BeamOpcode opcode, Label label,
					   DestinationOperand dest2, int i3,
					   SourceOperand src4, int i5, int i6,
					   DestinationOperand dest7)
		{
			super(opcode, label, dest2, true);
			this.i3 = i3;
			this.src4 = src4;
			this.i5 = i5;
			this.i6 = i6;
			this.dest7 = dest7;
		}
		public ETuple toSymbolic() {
			if (is_test)
				return ETuple.make(TEST_ATOM,
								   opcode.symbol,
								   label.toSymbolic(),
								   EList.make(dest.toSymbolic(),
											  new ESmall(i3),
											  src4.toSymbolic(),
											  new ESmall(i5),
											  bsFieldFlagsToSymbolic(i6),
											  dest7.toSymbolic()));
			else
				throw new erjang.NotImplemented();
		}
	}

	//============================================================

	public static class Bif extends Insn { // 'bif0-2'
		public final Label label;
		public final ExtFun ext_fun;
		public final SourceOperand[] args;
		public final DestinationOperand dest;
		protected Bif(BeamOpcode opcode, Label label, ExtFun ext_fun, SourceOperand[] args, DestinationOperand dest) {
			super(opcode);
			this.label = label;
			this.ext_fun = ext_fun;
			this.dest = dest;
			this.args = args;
		}

		private static SourceOperand[] NO_ARGS = new SourceOperand[] {};
		public Bif(BeamOpcode opcode, Label label, ExtFun ext_fun, DestinationOperand dest) {
			this(opcode, label, ext_fun, NO_ARGS, dest);
		}
		public Bif(BeamOpcode opcode, Label label, ExtFun ext_fun, SourceOperand src, DestinationOperand dest) {
			this(opcode, label, ext_fun, new SourceOperand[] {src}, dest);
		}
		public Bif(BeamOpcode opcode, Label label, ExtFun ext_fun, SourceOperand src1, SourceOperand src2, DestinationOperand dest) {
			this(opcode, label, ext_fun, new SourceOperand[] {src1, src2}, dest);
		}

		public SourceOperand[] argList() {return args;}

		public ETuple toSymbolic() {
			return ETuple.make(BIF_ATOM,
							   ext_fun.fun,
							   labelToSymbolic_nf(label),
							   Operands.toSymbolicList(args),
							   dest.toSymbolic());
		}
	}



	public static class GcBif extends Insn { // 'gc_bif1-2'
		public final Label label;
		public final ExtFun ext_fun;
		public final int i;
		public final SourceOperand[] args;
		public final DestinationOperand dest;
		protected GcBif(BeamOpcode opcode, Label label, ExtFun ext_fun, int i, SourceOperand[] args, DestinationOperand dest) {
			super(opcode);
			this.label = label;
			this.ext_fun = ext_fun;
			this.i = i;
			this.args = args;
			this.dest = dest;
		}

		public GcBif(BeamOpcode opcode, Label label, ExtFun ext_fun, int i, SourceOperand src, DestinationOperand dest) {
			this(opcode, label, ext_fun, i, new SourceOperand[]{src}, dest);
		}
		public GcBif(BeamOpcode opcode, Label label, ExtFun ext_fun, int i, SourceOperand src1, SourceOperand src2, DestinationOperand dest) {
			this(opcode, label, ext_fun, i, new SourceOperand[]{src1,src2}, dest);
		}
		public GcBif(BeamOpcode opcode, Label label, ExtFun ext_fun, int i, SourceOperand src1, SourceOperand src2, SourceOperand src3, DestinationOperand dest) {
			this(opcode, label, ext_fun, i, new SourceOperand[]{src1,src2,src3}, dest);
		}

		public SourceOperand[] argList() {return args;}

		public ETuple toSymbolic() {
			return ETuple.make(GCBIF_ATOM,
							   ext_fun.fun,
							   label.toSymbolic(),
							   new ESmall(i),
							   Operands.toSymbolicList(args),
							   dest.toSymbolic());
		}
	}

	public static class Select extends Insn { // E.g. 'select_val'
		public final SourceOperand src;
		public final Label defaultLabel;
		public final SelectList jumpTable; // TODO: Improve representation.
		public Select(BeamOpcode opcode,
					  SourceOperand src,
					  Label defaultLabel,
					  SelectList jumpTable)
		{
			super(opcode);
			this.src = src;
			this.defaultLabel = defaultLabel;
			this.jumpTable = jumpTable;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   src.toSymbolic(),
							   defaultLabel.toSymbolic(),
							   jumpTable.toSymbolic());
		}
	}


	public static class MapQuery extends Insn { // E.g. 'select_val'
		public final SourceOperand src;
		public final Label defaultLabel;
		public final SelectList kvs; // TODO: Improve representation.
		public MapQuery(BeamOpcode opcode,
					  Label defaultLabel,
					  SourceOperand map,
					  SelectList kvs)
		{
			super(opcode);
			this.src = map;
			this.defaultLabel = defaultLabel;
			this.kvs = kvs;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   defaultLabel.toSymbolic(),
							   src.toSymbolic(),
							   kvs.toSymbolic());
		}
	}

	public static class MapUpdate extends Insn { // E.g. 'select_val'
		public final SourceOperand src;
		public final Label defaultLabel;
		public final SelectList kvs; // TODO: Improve representation.
		public final DestinationOperand dest;
		private int kvCount;
		public MapUpdate(BeamOpcode opcode,
					  Label defaultLabel,
					  SourceOperand map,
					  int kvCount, 
					  SelectList kvs,
					  DestinationOperand dest)
		{
			super(opcode);
			this.src = map;
			this.defaultLabel = defaultLabel;
			this.kvs = kvs;
			this.kvCount = kvCount;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   defaultLabel.toSymbolic(),
							   src.toSymbolic(),
							   dest.toSymbolic(),
							   ESmall.make(kvCount),
							   kvs.toSymbolic());
		}
	}

	public static class BSAppend extends Insn { // E.g. 'bs_append'
		// LSIIISID
		public final Label label;
		public final SourceOperand src2;
		public final int i3, i4, i5, i7;
		public final SourceOperand src6;
		public final DestinationOperand dest8;
		public BSAppend(BeamOpcode opcode, Label label, SourceOperand src2, int i3, int i4, int i5, SourceOperand src6, int i7, DestinationOperand dest8) {
			super(opcode);
			this.label = label;
			this.src2 = src2;
			this.i3 = i3;
			this.i4 = i4;
			this.i5 = i5;
			this.src6 = src6;
			this.i7 = i7;
			this.dest8 = dest8;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   label.toSymbolic(),
							   src2.toSymbolic(),
							   new ESmall(i3),
							   new ESmall(i4),
							   new ESmall(i5),
							   src6.toSymbolic(),
							   bsFieldFlagsToSymbolic(i7),
							   dest8.toSymbolic());
		}
	}

	public static class BSPrivateAppend extends Insn { // E.g. 'bs_private_append'
		//  // LSISID
		public final Label label;
		public final SourceOperand src2, src4;
		public final int i3, i5;
		public final DestinationOperand dest;
		public BSPrivateAppend(BeamOpcode opcode, Label label, SourceOperand src2, int i3, SourceOperand src4, int i5, DestinationOperand dest) {
			super(opcode);
			this.label = label;
			this.src2 = src2;
			this.i3 = i3;
			this.src4 = src4;
			this.i5 = i5;
			this.dest = dest;
		}
		public ETuple toSymbolic() {
			return ETuple.make(opcode.symbol,
							   label.toSymbolic(),
							   src2.toSymbolic(),
							   new ESmall(i3),
							   src4.toSymbolic(),
							   bsFieldFlagsToSymbolic(i5),
							   dest.toSymbolic());
		}
	}
}
src/main/java/erjang/beam/repr/Operands.java
  public static class Int extends Literal {
public final int value;
public Int(int value) {this.value=value;}

public boolean equals(int val) { return value == val; }
		
@Override
public Int testInt() {return this;}

@Override
public EObject literalValue() {return new ESmall(value);}

public EObject toSymbolic() {
	return ETuple.make(INTEGER_ATOM, new ESmall(value));
}
  }
src/main/java/erjang/EInputStream.java
public class EInputStream extends ByteArrayInputStream {

	public static int DECODE_INT_LISTS_AS_STRINGS = 1;

	private final int flags;

	private EAtom[] atom_cache_refs;

	private boolean safeMode;

	/**
	 * @param buf
	 */
	public EInputStream(final byte[] buf) {
		this(buf, 0);
	}

	/**
	 * Create a stream from a buffer containing encoded Erlang terms.
	 * 
	 * @param flags
	 */
	public EInputStream(final byte[] buf, final int flags) {
		super(buf);
		this.flags = flags;
	}

	/**
	 * Create a stream from a buffer containing encoded Erlang terms at the
	 * given offset and length.
	 * 
	 * @param flags
	 */
	public EInputStream(final byte[] buf, final int offset, final int length,
			final int flags) {
		super(buf, offset, length);
		this.flags = flags;
	}
	
	public void updateMessageDigest(MessageDigest digest, int offset, int len) {
		digest.update(this.buf, offset, len);
	}

	/**
	 * Get the current position in the stream.
	 * 
	 * @return the current position in the stream.
	 */
	public int getPos() {
		return super.pos;
	}

	/**
	 * Set the current position in the stream.
	 * 
	 * @param pos
	 *            the position to move to in the stream. If pos indicates a
	 *            position beyond the end of the stream, the position is move to
	 *            the end of the stream instead. If pos is negative, the
	 *            position is moved to the beginning of the stream instead.
	 * 
	 * @return the previous position in the stream.
	 */
	public int setPos(int pos) {
		final int oldpos = super.pos;

		if (pos > super.count) {
			pos = super.count;
		} else if (pos < 0) {
			pos = 0;
		}

		super.pos = pos;

		return oldpos;
	}

	/**
	 * Read an array of bytes from the stream. The method reads at most
	 * buf.length bytes from the input stream.
	 * 
	 * @return the number of bytes read.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int readN(final byte[] buf) throws IOException {
		return this.readN(buf, 0, buf.length);
	}

	/**
	 * Read an array of bytes from the stream. The method reads at most len
	 * bytes from the input stream into offset off of the buffer.
	 * 
	 * @return the number of bytes read.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int readN(final byte[] buf, final int off, final int len)
			throws IOException {
		if (len == 0 && available() == 0) {
			return 0;
		}
		final int i = super.read(buf, off, len);
		if (i < 0) {
			throw new IOException("Cannot read from input stream");
		}
		return i;
	}

	/**
	 * Alias for peek1()
	 */
	public int peek() throws IOException {
		return peek1();
	}

	/**
	 * Look ahead one position in the stream without consuming the byte found
	 * there.
	 * 
	 * @return the next byte in the stream, as an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int peek1() throws IOException {
		int i;
		try {
			i = super.buf[super.pos];
			if (i < 0) {
				i += 256;
			}

			return i;
		} catch (final Exception e) {
			throw new IOException("Cannot read from input stream");
		}
	}

	public int peek1skip_version() throws IOException {
		int i = peek1();
		if (i == EExternal.versionTag) {
			read1();
			i = peek1();
		}
		return i;
	}

	/**
	 * Read a one byte integer from the stream.
	 * 
	 * @return the byte read, as an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int read1() throws IOException {
		int i;
		i = super.read();

		if (i < 0) {
			throw new IOException("Cannot read from input stream");
		}

		return i;
	}

	public int read1skip_version() throws IOException {
		int tag = read1();
		if (tag == EExternal.versionTag) {
			tag = read1();
		}
		return tag;
	}

	public void readFully(byte[] b) throws IOException {
		int pos = 0;
		while (pos < b.length) {
			int read;
			try {
				read = super.read(b, pos, b.length-pos);
			} catch (IndexOutOfBoundsException ioobe) {
				read = 0;
			}
			if (read==0) throw new EOFException("Can't read enough from input stream");
			pos += read;
		}
	}

	/**
	 * Read a two byte big endian integer from the stream.
	 * 
	 * @return the bytes read, converted from big endian to an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int read2BE() throws IOException {
		final byte[] b = new byte[2];
		readFully(b);
		return (b[0] << 8 & 0xff00) + (b[1] & 0xff);
	}

	/**
	 * Read a four byte big endian integer from the stream.
	 * 
	 * @return the bytes read, converted from big endian to an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int read4BE() throws IOException {
		final byte[] b = new byte[4];
		readFully(b);
		return (b[0] << 24 & 0xff000000) + (b[1] << 16 & 0xff0000)
				+ (b[2] << 8 & 0xff00) + (b[3] & 0xff);
	}

	/**
	 * Read a two byte little endian integer from the stream.
	 * 
	 * @return the bytes read, converted from little endian to an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int read2LE() throws IOException {
		final byte[] b = new byte[2];
		readFully(b);
		return (b[1] << 8 & 0xff00) + (b[0] & 0xff);
	}

	/**
	 * Read a four byte little endian integer from the stream.
	 * 
	 * @return the bytes read, converted from little endian to an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public int read4LE() throws IOException {
		final byte[] b = new byte[4];
		readFully(b);
		return (b[3] << 24 & 0xff000000) + (b[2] << 16 & 0xff0000)
				+ (b[1] << 8 & 0xff00) + (b[0] & 0xff);
	}

	/**
	 * Read a little endian integer from the stream.
	 * 
	 * @param n
	 *            the number of bytes to read
	 * 
	 * @return the bytes read, converted from little endian to an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public long readLE(int n) throws IOException {
		final byte[] b = new byte[n];
		try {
			super.read(b);
		} catch (final IOException e) {
			throw new IOException("Cannot read from input stream");
		}
		;
		long v = 0;
		while (n-- > 0) {
			v = v << 8 | (long) b[n] & 0xff;
		}
		return v;
	}

	/**
	 * Read a bigendian integer from the stream.
	 * 
	 * @param n
	 *            the number of bytes to read
	 * 
	 * @return the bytes read, converted from big endian to an integer.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public long readBE(final int n) throws IOException {
		final byte[] b = new byte[n];
		try {
			super.read(b);
		} catch (final IOException e) {
			throw new IOException("Cannot read from input stream");
		}
		;
		long v = 0;
		for (int i = 0; i < n; i++) {
			v = v << 8 | (long) b[i] & 0xff;
		}
		return v;
	}

	/**
	 * Read an Erlang atom from the stream and interpret the value as a boolean.
	 * 
	 * @return true if the atom at the current position in the stream contains
	 *         the value 'true' (ignoring case), false otherwise.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an atom.
	 */
	public boolean read_boolean() throws IOException {
		return read_atom() == ERT.TRUE;
	}

	/**
	 * Read an Erlang atom from the stream.
	 * 
	 * @return a String containing the value of the atom.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an atom.
	 */
	public EAtom read_atom() throws IOException {
		int tag;
		int len;
		byte[] strbuf;
		String atom;

		tag = read1skip_version();

		if (tag == EExternal.atomCacheRef) {
			int index = read1() & 0xff;
			EAtom res = atom_cache_refs[index];
			if (res == null) {
				throw new IOException("no cached atom at "+index);
			}
			return res;
		}
		
		if (tag == EExternal.smallAtomTag) {
			len = read1();
		} else {
			if (tag != EExternal.atomTag) {
				throw new IOException("wrong tag encountered, expected "
						+ EExternal.atomTag + ", got " + tag);
			}
	
			len = read2BE();
		}
		
		strbuf = new byte[len];
		this.readN(strbuf);
		
		char[] in = new char[len];
		for (int i = 0; i < len; i++) {
			in[i] = (char) (strbuf[i] & 0xff);
		}
		
		atom = new String(in);

		if (atom.length() > EExternal.maxAtomLength) {
			atom = atom.substring(0, EExternal.maxAtomLength);
		}

		if (safeMode) {
			return EAtom.existing_atom(atom);
		} else {
			return EAtom.intern(atom);
		}
	}

	/**
	 * Read an Erlang binary from the stream.
	 * 
	 * @return a byte array containing the value of the binary.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a binary.
	 */
	public byte[] read_binary() throws IOException {
		int tag;
		int len;
		byte[] bin;

		tag = read1skip_version();

		if (tag != EExternal.binTag) {
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.binTag + ", got " + tag);
		}

		len = read4BE();

		bin = new byte[len];
		this.readN(bin);

		return bin;
	}

	/**
	 * Read an Erlang bitstr from the stream.
	 * 
	 * @param pad_bits
	 *            an int array whose first element will be set to the number of
	 *            pad bits in the last byte.
	 * 
	 * @return a byte array containing the value of the bitstr.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a bitstr.
	 */
	public byte[] read_bitstr(final int pad_bits[]) throws IOException {
		int tag;
		int len;
		byte[] bin;

		tag = read1skip_version();

		if (tag != EExternal.bitBinTag) {
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.bitBinTag + ", got " + tag);
		}

		len = read4BE();
		bin = new byte[len];
		final int tail_bits = read1();
		if (tail_bits < 0 || 7 < tail_bits) {
			throw new IOException("Wrong tail bit count in bitstr: "
					+ tail_bits);
		}
		if (len == 0 && tail_bits != 0) {
			throw new IOException("Length 0 on bitstr with tail bit count: "
					+ tail_bits);
		}
		this.readN(bin);

		pad_bits[0] = 8 - tail_bits;
		return bin;
	}

	/**
	 * Read an Erlang float from the stream.
	 * 
	 * @return the float value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a float.
	 */
	public float read_float() throws IOException {
		final double d = read_double();
		return (float) d;
	}

	/**
	 * Read an Erlang float from the stream.
	 * 
	 * @return the float value, as a double.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a float.
	 */
	public double read_double() throws IOException {
		int tag;

		// parse the stream
		tag = read1skip_version();

		switch (tag) {
		case EExternal.newFloatTag: {
			return Double.longBitsToDouble(readBE(8));
		}
		case EExternal.floatTag: {
			BigDecimal val;
			int epos;
			int exp;
			final byte[] strbuf = new byte[31];
			String str;

			// get the string
			this.readN(strbuf);
			str = new String(strbuf, IO.ISO_LATIN_1);

			// find the exponent prefix 'e' in the string
			epos = str.indexOf('e', 0);

			if (epos < 0) {
				throw new IOException("Invalid float format: '" + str + "'");
			}

			// remove the sign from the exponent, if positive
			String estr = str.substring(epos + 1).trim();

			if (estr.substring(0, 1).equals("+")) {
				estr = estr.substring(1);
			}

			// now put the mantissa and exponent together
			exp = Integer.valueOf(estr).intValue();
			val = new BigDecimal(str.substring(0, epos)).movePointRight(exp);

			return val.doubleValue();
		}
		default:
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.newFloatTag + ", got " + tag);
		}
	}

	/**
	 * Read one byte from the stream.
	 * 
	 * @return the byte read.
	 * 
	 * @exception IOException
	 *                if the next byte cannot be read.
	 */
	public byte read_byte() throws IOException {
		final long l = this.read_long(false);
		final byte i = (byte) l;

		if (l != i) {
			throw new IOException("Value does not fit in byte: " + l);
		}

		return i;
	}

	/**
	 * Read a character from the stream.
	 * 
	 * @return the character value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an integer that can
	 *                be represented as a char.
	 */
	public char read_char() throws IOException {
		final long l = this.read_long(true);
		final char i = (char) l;

		if (l != (i & 0xffffL)) {
			throw new IOException("Value does not fit in char: " + l);
		}

		return i;
	}

	/**
	 * Read an unsigned integer from the stream.
	 * 
	 * @return the integer value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream can not be represented as a
	 *                positive integer.
	 */
	public int read_uint() throws IOException {
		final long l = this.read_long(true);
		final int i = (int) l;

		if (l != (i & 0xFFFFffffL)) {
			throw new IOException("Value does not fit in uint: " + l);
		}

		return i;
	}

	/**
	 * Read an integer from the stream.
	 * 
	 * @return the integer value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream can not be represented as
	 *                an integer.
	 */
	public int read_int() throws IOException {
		final long l = this.read_long(false);
		final int i = (int) l;

		if (l != i) {
			throw new IOException("Value does not fit in int: " + l);
		}

		return i;
	}

	/**
	 * Read an unsigned short from the stream.
	 * 
	 * @return the short value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream can not be represented as a
	 *                positive short.
	 */
	public short read_ushort() throws IOException {
		final long l = this.read_long(true);
		final short i = (short) l;

		if (l != (i & 0xffffL)) {
			throw new IOException("Value does not fit in ushort: " + l);
		}

		return i;
	}

	/**
	 * Read a short from the stream.
	 * 
	 * @return the short value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream can not be represented as a
	 *                short.
	 */
	public short read_short() throws IOException {
		final long l = this.read_long(false);
		final short i = (short) l;

		if (l != i) {
			throw new IOException("Value does not fit in short: " + l);
		}

		return i;
	}

	/**
	 * Read an unsigned long from the stream.
	 * 
	 * @return the long value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream can not be represented as a
	 *                positive long.
	 */
	public long read_ulong() throws IOException {
		return this.read_long(true);
	}

	/**
	 * Read a long from the stream.
	 * 
	 * @return the long value.
	 * 
	 * @exception IOException
	 *                if the next term in the stream can not be represented as a
	 *                long.
	 */
	public long read_long() throws IOException {
		return this.read_long(false);
	}

	public long read_long(final boolean unsigned) throws IOException {
		final byte[] b = read_integer_byte_array();
		return EInputStream.byte_array_to_long(b, unsigned);
	}

	public EInteger read_tagged_integer() throws IOException {
		int tag = read1skip_version();

		switch (tag) {
		case EExternal.smallIntTag: return new ESmall(read1());
		case EExternal.intTag:      return new ESmall(read4BE());
		default: setPos(getPos()-1); return ERT.box(new BigInteger(read_integer_byte_array()));
		} // switch
	}

	/**
	 * Read an integer from the stream.
	 * 
	 * @return the value as a big endian 2's complement byte array.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an integer.
	 */
	public byte[] read_integer_byte_array() throws IOException {
		int tag;
		byte[] nb;

		tag = read1skip_version();

		switch (tag) {
		case EExternal.smallIntTag:
			nb = new byte[2];
			nb[0] = 0;
			nb[1] = (byte) read1();
			break;

		case EExternal.intTag:
			nb = new byte[4];
			if (this.readN(nb) != 4) { // Big endian
				throw new IOException("Cannot read from intput stream");
			}
			break;

		case EExternal.smallBigTag:
		case EExternal.largeBigTag:
			int arity;
			int sign;
			if (tag == EExternal.smallBigTag) {
				arity = read1();
				sign = read1();
			} else {
				arity = read4BE();
				sign = read1();
				if (arity + 1 < 0) {
					throw new IOException(
							"Value of largeBig does not fit in BigInteger, arity "
									+ arity + " sign " + sign);
				}
			}
			nb = new byte[arity + 1];
			// Value is read as little endian. The big end is augumented
			// with one zero byte to make the value 2's complement positive.
			if (this.readN(nb, 0, arity) != arity) {
				throw new IOException("Cannot read from intput stream");
			}
			// Reverse the array to make it big endian.
			for (int i = 0, j = nb.length; i < j--; i++) {
				// Swap [i] with [j]
				final byte b = nb[i];
				nb[i] = nb[j];
				nb[j] = b;
			}
			if (sign != 0) {
				// 2's complement negate the big endian value in the array
				int c = 1; // Carry
				for (int j = nb.length; j-- > 0;) {
					c = (~nb[j] & 0xFF) + c;
					nb[j] = (byte) c;
					c >>= 8;
				}
			}
			break;

		default:
			throw new IOException("Not valid integer tag: " + tag);
		}

		return nb;
	}

	public static long byte_array_to_long(final byte[] b, final boolean unsigned)
			throws IOException {
		long v;
		switch (b.length) {
		case 0:
			v = 0;
			break;
		case 2:
			v = ((b[0] & 0xFF) << 8) + (b[1] & 0xFF);
			v = (short) v; // Sign extend
			if (v < 0 && unsigned) {
				throw new IOException("Value not unsigned: " + v);
			}
			break;
		case 4:
			v = ((b[0] & 0xFF) << 24) + ((b[1] & 0xFF) << 16)
					+ ((b[2] & 0xFF) << 8) + (b[3] & 0xFF);
			v = (int) v; // Sign extend
			if (v < 0 && unsigned) {
				throw new IOException("Value not unsigned: " + v);
			}
			break;
		default:
			int i = 0;
			final byte c = b[i];
			// Skip non-essential leading bytes
			if (unsigned) {
				if (c < 0) {
					throw new IOException("Value not unsigned: " + b);
				}
				while (b[i] == 0) {
					i++; // Skip leading zero sign bytes
				}
			} else {
				if (c == 0 || c == -1) { // Leading sign byte
					i = 1;
					// Skip all leading sign bytes
					while (i < b.length && b[i] == c) {
						i++;
					}
					if (i < b.length) {
						// Check first non-sign byte to see if its sign
						// matches the whole number's sign. If not one more
						// byte is needed to represent the value.
						if (((c ^ b[i]) & 0x80) != 0) {
							i--;
						}
					}
				}
			}
			if (b.length - i > 8) {
				// More than 64 bits of value
				throw new IOException("Value does not fit in long: " + b);
			}
			// Convert the necessary bytes
			for (v = c < 0 ? -1 : 0; i < b.length; i++) {
				v = v << 8 | b[i] & 0xFF;
			}
		}
		return v;
	}

	/**
	 * Read a list header from the stream.
	 * 
	 * @return the arity of the list.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a list.
	 */
	public int read_list_head() throws IOException {
		int arity = 0;
		final int tag = read1skip_version();

		switch (tag) {
		case EExternal.nilTag:
			arity = 0;
			break;

		case EExternal.stringTag:
			arity = read2BE();
			break;

		case EExternal.listTag:
			arity = read4BE();
			break;

		default:
			throw new IOException("Not valid list tag: " + tag);
		}

		return arity;
	}

	  public int read_map_head() throws IOException {
		  int arity = 0;
		  final int tag = read1skip_version();
		  
		  // decode the map header and get arity
		  switch (tag) {
		  case EExternal.mapTag:
			  	arity = read4BE();
			  	break;
		  
		  default:
			  throw new IOException("Not valid map tag: " + tag);
		  }
		  
		  return arity;
	  }
	
	/**
	 * Read a tuple header from the stream.
	 * 
	 * @return the arity of the tuple.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a tuple.
	 */
	public int read_tuple_head() throws IOException {
		int arity = 0;
		final int tag = read1skip_version();

		// decode the tuple header and get arity
		switch (tag) {
		case EExternal.smallTupleTag:
			arity = read1();
			break;

		case EExternal.largeTupleTag:
			arity = read4BE();
			break;

		default:
			throw new IOException("Not valid tuple tag: " + tag);
		}

		return arity;
	}

	/**
	 * Read an empty list from the stream.
	 * 
	 * @return zero (the arity of the list).
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an empty list.
	 */
	public int read_nil() throws IOException {
		int arity = 0;
		final int tag = read1skip_version();

		switch (tag) {
		case EExternal.nilTag:
			arity = 0;
			break;

		default:
			throw new IOException("Not valid nil tag: " + tag);
		}

		return arity;
	}

	/**
	 * Read an Erlang PID from the stream.
	 * 
	 * @return the value of the PID.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an Erlang PID.
	 */
	public EPID read_pid() throws IOException {
		EAtom node;
		int id;
		int serial;
		int creation;
		int tag;

		tag = read1skip_version();

		if (tag != EExternal.pidTag) {
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.pidTag + ", got " + tag);
		}

		node = read_atom();
		id = read4BE() & 0x7fff; // 15 bits
		serial = read4BE() & 0x1fff; // 13 bits
		creation = read1() & 0x03; // 2 bits

		return EPID.make(node, id, serial, creation);
	}

	/**
	 * Read an Erlang port from the stream.
	 * 
	 * @return the value of the port.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an Erlang port.
	 */
	public EPort read_port() throws IOException {
		EAtom node;
		int id;
		int creation;
		int tag;

		tag = read1skip_version();

		if (tag != EExternal.portTag) {
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.portTag + ", got " + tag);
		}

		node = read_atom();
		id = read4BE() & 0xfffffff; // 28 bits
		creation = read1() & 0x03; // 2 bits

		return EPort.make(node, id, creation);
	}

	/**
	 * Read an Erlang reference from the stream.
	 * 
	 * @return the value of the reference
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not an Erlang reference.
	 */
	public ERef read_ref() throws IOException {
		EAtom node;
		int id;
		int creation;
		int tag;

		tag = read1skip_version();

		switch (tag) {
		case EExternal.refTag:
			node = read_atom();
			id = read4BE() & 0x3ffff; // 18 bits
			creation = read1() & 0x03; // 2 bits
			return new ERef(node, id, creation);

		case EExternal.newRefTag:
			final int arity = read2BE();
			node = read_atom();
			creation = read1() & 0x03; // 2 bits

			final int[] ids = new int[arity];
			for (int i = 0; i < arity; i++) {
				ids[i] = read4BE();
			}
			ids[0] &= 0x3ffff; // first id gets truncated to 18 bits
			return new ERef(node, ids, creation);

		default:
			throw new IOException("Wrong tag encountered, expected ref, got "
					+ tag);
		}
	}

	public EFun read_fun() throws IOException {
		final int tag = read1skip_version();
		if (tag == EExternal.funTag) {
			final int nFreeVars = read4BE();
			final EPID pid = read_pid();
			final EAtom module = read_atom();
			final long index = read_long();
			final long uniq = read_long();
			final EObject[] freeVars = new EObject[nFreeVars];
			for (int i = 0; i < nFreeVars; ++i) {
				freeVars[i] = read_any();
			}
			return EModuleManager.resolve(pid, module, (int)uniq, (int)index, freeVars);

		} else if (tag == EExternal.newFunTag) {
			@SuppressWarnings("unused")
			final int n = read4BE(); // size
			final int arity = read1();
			final byte[] md5 = new byte[16];
			readN(md5);
			final int index = read4BE();
			final int nFreeVars = read4BE();
			final EAtom module = read_atom();
			final long oldIndex = read_long();
			final long uniq = read_long();
			final EPID pid = read_pid();
			final EObject[] freeVars = new EObject[nFreeVars];
			for (int i = 0; i < nFreeVars; ++i) {
				freeVars[i] = read_any();
			}
			return EModuleManager.resolve(pid, module, new EBinary(md5), index, (int)uniq, (int)oldIndex, arity, freeVars);
			
		} else if (tag == EExternal.externalFunTag) {
			final EAtom module = read_atom();
			final EAtom function = read_atom();
			final int arity = (int) read_long();
			
			return EModuleManager.resolve(new FunID(module,function,arity));
		} else {
			throw new IOException("Wrong tag encountered, expected fun, got "
					+ tag);
		}
	}

	public EFun read_external_fun() throws IOException {
		final int tag = read1skip_version();
		if (tag != EExternal.externalFunTag) {
			throw new IOException(
					"Wrong tag encountered, expected external fun, got " + tag);
		}
		final EAtom module = read_atom();
		final EAtom function = read_atom();
		final int arity = (int) read_long();
		
		return EModuleManager.resolve(new FunID(module,function,arity));
	}

	/**
	 * Read a string from the stream.
	 * 
	 * @return the value of the string.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a string.
	 */
	public ESeq read_string() throws IOException {
		int tag;
		int len;
		byte[] strbuf;
		int[] intbuf;
		tag = read1skip_version();
		switch (tag) {
		case EExternal.stringTag:
			len = read2BE();
			strbuf = new byte[len];
			this.readN(strbuf);
			return EString.make(strbuf);
		case EExternal.nilTag:
			return ERT.NIL;
		case EExternal.listTag: // List when unicode +
			len = read4BE();
			intbuf = new int[len];
			boolean unicode = false;
			for (int i = 0; i < len; i++) {
				intbuf[i] = read_int();
				unicode |= intbuf[i] > 0xff;
				if (!EString.isValidCodePoint(intbuf[i])) {
					throw new IOException("Invalid CodePoint: " + intbuf[i]);
				}
			}
			read_nil();
			return EList.make(intbuf);
		default:
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.stringTag + " or " + EExternal.listTag
					+ ", got " + tag);
		}
	}

	/**
	 * Read a compressed term from the stream
	 * 
	 * @return the resulting uncompressed term.
	 * 
	 * @exception IOException
	 *                if the next term in the stream is not a compressed term.
	 */
	public EObject read_compressed() throws IOException {
		final int tag = read1skip_version();

		if (tag != EExternal.compressedTag) {
			throw new IOException("Wrong tag encountered, expected "
					+ EExternal.compressedTag + ", got " + tag);
		}
		final byte[] buf = read_size_and_inflate();

		final EInputStream ois = new EInputStream(buf, flags);
		return ois.read_any();
	}

	public byte[] read_size_and_inflate() throws IOException {
		final int size = read4BE();
		final byte[] buf = new byte[size];
		final java.util.zip.Inflater inflater = new java.util.zip.Inflater();
		final java.util.zip.InflaterInputStream is = new java.util.zip.InflaterInputStream(this, inflater);

		try {
		int pos = 0;
		while (pos<size) { // Inflate fully
			final int dsize = is.read(buf, pos, size-pos);
			if (dsize==0) break;
			pos += dsize;
		}
		if (pos < size) {
			throw new IOException("Decompression gave " + pos
					      + " bytes, not " + size);
		}

		// Back up if the inflater read ahead:
		int read_ahead = inflater.getRemaining();
		setPos(getPos() - read_ahead);
		} finally {
			is.close();
		}
		
		return buf;
	}

	/**
	 * Read an arbitrary Erlang term from the stream.
	 * 
	 * @return the Erlang term.
	 * 
	 * @exception IOException
	 *                if the stream does not contain a known Erlang type at the
	 *                next position.
	 */
	public EObject read_any() throws IOException {
		// calls one of the above functions, depending on o
		final int tag = peek1skip_version();

		switch (tag) {
		case EExternal.smallIntTag:
		case EExternal.intTag:
		case EExternal.smallBigTag:
		case EExternal.largeBigTag:
			return EInteger.read(this);

		case EExternal.atomCacheRef:
		case EExternal.atomTag:
		case EExternal.smallAtomTag:
			return EAtom.read(this);
			
		case EExternal.floatTag:
		case EExternal.newFloatTag:
			return EDouble.read(this);

		case EExternal.refTag:
		case EExternal.newRefTag:
			return ERef.read(this);

		case EExternal.portTag:
			return EPort.read(this);

		case EExternal.pidTag:
			return EPID.read(this);

		case EExternal.stringTag:
			return EString.read(this);

		case EExternal.listTag:
		case EExternal.nilTag:
			if ((flags & DECODE_INT_LISTS_AS_STRINGS) != 0) {
				final int savePos = getPos();
				try {
					return EString.read(this);
				} catch (final IOException e) {
				}
				setPos(savePos);
			}
			return EList.read(this);

		case EExternal.smallTupleTag:
		case EExternal.largeTupleTag:
			return ETuple.read(this);

		case EExternal.binTag:
			return EBinary.read(this);

		case EExternal.bitBinTag:
			return EBitString.read(this);

		case EExternal.compressedTag:
			return read_compressed();

		case EExternal.newFunTag:
		case EExternal.funTag:
			return EFun.read(this);
			
		case EExternal.externalFunTag:
			return read_external_fun();

		case EExternal.mapTag:
			return EMap.read(this);

		default:
			throw new IOException("Unknown data type: " + tag + " at position " + getPos());
		}
	}

	public void setAtomCacheRefs(EAtom[] atomCacheRefs) {
		this.atom_cache_refs = atomCacheRefs;		
	}

	public void setSafe(boolean safeMode) {
		this.safeMode = safeMode;
	}
}
src/main/java/erjang/EObject.java
public abstract class EObject {
	public static final ErlangOrderComparator ERLANG_ORDERING =
		new ErlangOrderComparator();

	public ECons cons(EObject h)
	{
		ESmall sm;
		if ((sm=h.testSmall()) != null && ((sm.value&0xff)==sm.value)) {
			return new EBinList((byte) sm.value, this);
		} else {
			return new EPair(h, this);
		}
	}
	
	public JavaObject testJavaObject() {
		return null;
	}

	public ERef testReference() {
		return null;
	}
	

	public EString testString()	{
		return null;
	}
	
	public EBigString testBigString()	{
		return null;
	}
	
	public EFun testFunction() {
		return null;
	}
	
	public EFun testFunction2(int nargs) {
		return null;
	}
	
	public EAtom testAtom() {
		return null;
	}

  public EMap testMap() {
    return null;
  }

	public ECons testNonEmptyList() {
		return null;
	}
	
	public ETuple testTuple() {
		return null;
	}

	public ESeq testSeq() {
		return null;
	}

	public EPID testPID() {
		return null;
	}

	public int asInt() {
		throw new RuntimeException("cannot convert " + this + " to int");
	}

	public ENumber testNumber() {
		return null;
	}

	public ECons testCons() {
		return null;
	}

	public EInteger testInteger() {
		return null;
	}

	public ENil testNil() {
		return null;
	}
	
	public boolean isNil() { return testNil() != null; }
	public boolean isBoolean() { return this==ERT.TRUE || this==ERT.FALSE; }

	public EAtom testBoolean() {
		return null;
	}

	public EBinary testBinary() {
		return null;
	}


	/**
	 * @return this if this object is an instance of EPort, otherwise null
	 */
	public EPort testPort() {
		return null;
	}

	/**
	 * @return this if this object is an instance of ESmall, otherwise null
	 */
	public ESmall testSmall() {
		return null;
	}
	
	public EBig testBig() {
		return null;
	}
	

	/**
	 * @return
	 */
	public EDouble testFloat() {
		return null;
	}
	
	public void visitIOList(EIOListVisitor out) throws ErlangError {
		throw ERT.badarg();
	}
	
	public boolean collectIOList(List<ByteBuffer> out) {
		return false;
	}

	/**
	 * @param rest TODO
	 * @return TODO
	 * @throws CharCollector.CollectingException when encountering
	 * something that can't be decoded as characters using the given
	 * CharCollector.  Exception contains the undecoded part of the input.
	 * @throws IOException when out.output throws IOException.
	 * @throws InvalidElementException when the input contains an object
	 * which is neither a list, an integer or a binary (without extra
	 * bits), or the input contains an integer in non-head position.
	 */
	public ESeq collectCharList(CharCollector out, ESeq rest)
		throws CharCollector.CollectingException,
		CharCollector.InvalidElementException,
		IOException
	{
		throw new CharCollector.InvalidElementException();
	}
	
	public Type emit_const(MethodVisitor mv) {
		throw new NotImplemented("emit_const for "+this.getClass().getName());
	}

	//
	// 
	//
	
	@BIF(name="-")
	public ENumber negate() { throw ERT.badarith(this); }

	@BIF(name="+")
	public ENumber add(EObject rhs) { return add(rhs, false); }

	@BIF(name="+")
	public ENumber add(ESmall rhs) { return add(rhs.value, false); }

	@BIF(name="+", type=BIF.Type.GUARD)
	public final ENumber add$g(ESmall rhs) { return add(rhs.value, true); }

	public ENumber add(EObject rhs, boolean guard) { if (guard) return null; throw ERT.badarith(this, rhs); }
	public ENumber add(int lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); }
	public ENumber add(double lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); }
	public ENumber add(BigInteger lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); }

	@BIF(name="-")
	public ENumber subtract(EObject rhs) { return subtract(rhs, false); }
	public ENumber subtract(ESmall rhs) { return subtract(rhs, false); }
	public ENumber subtract(EObject rhs, boolean guard) { if (guard) return null; throw ERT.badarith(this, rhs); }
	public ENumber subtract(int rhs) { throw ERT.badarith(this, rhs); }
	ENumber r_subtract(int lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); }
	ENumber r_subtract(double lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); }
	ENumber r_subtract(BigInteger lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); }
	
	@BIF(name="div")
	public EInteger idiv(EObject rhs) { throw ERT.badarith(this, rhs); }
	public EInteger idiv(int rhs) { throw ERT.badarith(this, rhs); }
	EInteger r_idiv(int lhs) { throw ERT.badarith(lhs, this); }
	EInteger r_idiv(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="rem")
	public EInteger irem(EObject rhs) { throw ERT.badarith(this, rhs); }
	EInteger r_irem(int lhs) { throw ERT.badarith(lhs, this); }
	EInteger r_irem(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="/")
	public EDouble divide(EObject rhs) { throw ERT.badarith(this, rhs); }
	EDouble r_divide(int lhs) { throw ERT.badarith(lhs, this); }
	EDouble r_divide(double lhs) { throw ERT.badarith(lhs, this); }
	EDouble r_divide(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="*")
	public ENumber multiply(EObject rhs) { throw ERT.badarith(this, rhs); }
	public ENumber r_multiply(int lhs) { throw ERT.badarith(new ESmall(lhs), this); }
	public EDouble r_multiply(double lhs) { throw ERT.badarith(new EDouble(lhs), this); }
	public ENumber r_multiply(BigInteger lhs) { throw ERT.badarith(new EBig(lhs), this); }

	@BIF(name="bsr")
	public EInteger bsr(EObject rhs) { throw ERT.badarith(this, rhs); }
	EInteger r_bsr(int lhs) { throw ERT.badarith(lhs, this); }
	EInteger r_bsr(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="bsl")
	public EInteger bsl(EObject rhs) { throw ERT.badarith(this, rhs); }
	EInteger r_bsl(int lhs) { throw ERT.badarith(lhs, this); }
	EInteger r_bsl(BigInteger lhs) { throw ERT.badarith(lhs, this); }
	
	@BIF(name="band")
	public EInteger band(EObject rhs) { throw ERT.badarith(this, rhs); }
	public EInteger band(int lhs) { throw ERT.badarith(lhs, this); }
	public EInteger band(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="bor")
	public EInteger bor(EObject rhs) { throw ERT.badarith(this, rhs); }
	public EInteger bor(int lhs) { throw ERT.badarith(lhs, this); }
	public EInteger bor(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="bxor")
	public EInteger bxor(EObject rhs) { throw ERT.badarith(this, rhs); }
	@BIF(name="bxor")
	public EInteger bxor(ESmall rhs) { return bxor(rhs.value); }

	public EInteger bxor(int lhs) { throw ERT.badarith(lhs, this); }
	public EInteger bxor(BigInteger lhs) { throw ERT.badarith(lhs, this); }

	@BIF(name="bnot")
	public EInteger bnot() { throw ERT.badarith(this); }

	// extra convenience
	
	public EDouble divide(double rhs) { throw ERT.badarith(this,rhs); }
	public EInteger irem(int rhs) { throw ERT.badarith(this,rhs); }

	@Override
	public abstract int hashCode();

	@Override
	public boolean equals(Object other) {
		if (other == this) return true;
		if (other instanceof EObject) {
			return equalsExactly((EObject) other);
		} else {
			return false;
		}
	}
	
	public boolean equals(EObject other) {
		if (other == this) return true;
		return equalsExactly(other);
	}

	public final int erlangCompareTo(EObject rhs) {
		if (rhs == this) return 0;
		int cmp1 = cmp_order();
		int cmp2 = rhs.cmp_order();
		if ( cmp1 == cmp2 ) {
			return compare_same(rhs);
		} else if (cmp1 < cmp2) {
			return -1;
		} else {
			return 1;
		}
	}

	/** Compare two objects that have same cmp_order */
	int compare_same(EObject rhs) { throw new Error("cannot compare"); }
	
	int r_compare_same(ESmall lhs) { throw new NotImplemented(); }
	int r_compare_same(EBig lhs) { throw new NotImplemented(); }
	int r_compare_same(EDouble lhs) { throw new NotImplemented(); }
	int r_compare_same(EInternalPID lhs) { throw new NotImplemented(); }
	
	/** Erlang "equals exactly", also called "matches" (=:= operator).
	 *  Is overridden by some subclasses, inclusing composites.
	 */
	public boolean equalsExactly(EObject rhs) {
		return erlangCompareTo(rhs) == 0;
	}

	/** Erlang "compares same" (== operator). */
	public final boolean erlangEquals(EObject rhs) {
		return erlangCompareTo(rhs) == 0;
	}

	boolean r_equals_exactly(ESmall lhs) { return false; }
	boolean r_equals_exactly(EBig lhs) { return false; }
	boolean r_equals_exactly(EDouble lhs) { return false; }
	
	
	/** used as compare-order for "non-erlang terms", such as 
	 *  compiled ets queries and the tail marker */
	public static final int CMP_ORDER_ERJANG_INTERNAL = -1;
	public static final int CMP_ORDER_NUMBER = 0;
	public static final int CMP_ORDER_ATOM = 1;
	public static final int CMP_ORDER_REFERENCE = 2;
	public static final int CMP_ORDER_FUN = 3;
	public static final int CMP_ORDER_PORT = 4;
	public static final int CMP_ORDER_PID = 5;
	public static final int CMP_ORDER_TUPLE = 6;
	public static final int CMP_ORDER_MAP = 7;
	public static final int CMP_ORDER_LIST = 8;
	public static final int CMP_ORDER_BITSTRING = 9;
	
	/** 
	 * 	number[0] < atom[1] < reference[2] < fun[3] < port[4] < pid[5] < tuple[6] < list[7] < bit string[8]
	 * @return
	 */
	int cmp_order() { throw new Error("cannot compare"); }


	@BIF
	public EAtom is_function(EObject arity) {
		return ERT.FALSE;
	}

    @BIF
    public EAtom is_map() {
        return ERT.FALSE;
    }
    
    @BIF(type=BIF.Type.GUARD, name="is_map")
    public EAtom is_map_g() {
        return null;
    }    

	/**
	 * @param o2
	 * @return
	 */
	public EAtom ge(EObject o2) {
		return ERT.box ( this.erlangCompareTo(o2) >= 0 );
	}


	/**
	 * @return
	 */
	public EBitString testBitString() {
		return null;
	}

	/**
	 * @return non-null if this is an internal port
	 */
	public EInternalPort testInternalPort() {
		return null;
	}

	/**
	 * @return
	 */
	public EPortControl testPortControl() {
		return null;
	}

	/**
	 * @return
	 */
	public EHandle testHandle() {
		return null;
	}

	/**
	 * @return
	 */
	public EInternalPID testInternalPID() {
		return null;
	}

	/**
	 * @return true if this term matches the given matcher
	 */
	public boolean match(ETermPattern matcher, EMatchContext r) {
		return false;
	}

	/**
	 * @param out
	 * @return
	 */
	public ETermPattern compileMatch(Set<Integer> out) {
		// this should continue to be "not implemented".  
		// subclasses should provide an implementation.
		throw ERT.badarg(this);
	}

	public EBinMatchState testBinMatchState() {
		return null;
	}

	public void encode(EOutputStream eos) {
		throw new NotImplemented("Encode for "+getClass().getName());
	}

	final public boolean is_eq(EObject other) {
		return erlangEquals(other);
	}
	
	final public boolean is_eq_exact(EObject other) {
		return equalsExactly(other);
	}
	
	final public boolean is_ne(EObject other) {
		return !erlangEquals(other);
	}
	
	final public boolean is_ne_exact(EObject other) {
		return !equalsExactly(other);
	}
	
	final public boolean is_lt(EObject other) {
		return this.erlangCompareTo(other) < 0;
	}
	
	final public boolean is_ge(EObject other) {
		return this.erlangCompareTo(other) >= 0;
	}
	
	/**
	 * @param c1
	 * @return
	 */
	public EObject prepend(ESeq list) {
		
		// first, rlist=lists:reverse(list)
		ESeq rlist = ERT.NIL;
		while (!list.isNil()) {
			rlist = rlist.cons(list.head());
			list = list.tail();
		}

		// then, prepend rlist on this
		EObject r = this;
		while(!rlist.isNil()) {
			r = r.cons(rlist.head());
			rlist = rlist.tail();
		} 
		
		return r;
	}

	public ENumber inc() { return ESmall.ONE.add(this); }
	public ENumber dec() { return ESmall.MINUS_ONE.add(this); }	
	public boolean is_zero() { return false; }


	public static abstract class ValueComparator implements Comparator<EObject> {
		public abstract int compare(EObject a, EObject b);
	}

	/** Comparator which doesn't distibguish between integers and floats.
	 * Note: this comparator imposes orderings that are inconsistent with equals.
	 */
	private static class ErlangOrderComparator extends ValueComparator {
		public int compare(EObject a, EObject b) {
			return a.erlangCompareTo(b);
		}
	}
}


Question: import static erjang.beam.CodeAtoms.START_ATOM;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import erjang.EAtom;
import erjang.EBinary;
import erjang.EInputStream;
import erjang.EObject;
import erjang.ESeq;
import erjang.EString;
import erjang.beam.BeamOpcode;
import erjang.beam.repr.AnonFun;
import erjang.beam.repr.CodeTables;
import erjang.beam.repr.ExtFun;
import erjang.beam.repr.FunctionInfo;
import erjang.beam.repr.FunctionRepr;
import erjang.beam.repr.Insn;
import erjang.beam.repr.LineRecord;
import erjang.beam.repr.ModuleRepr;
import erjang.beam.repr.Operands;
import erjang.beam.repr.Operands.AllocList;
import erjang.beam.repr.Operands.Atom;
import erjang.beam.repr.Operands.BitString;
import erjang.beam.repr.Operands.ByteString;
import erjang.beam.repr.Operands.DestinationOperand;
import erjang.beam.repr.Operands.FReg;
import erjang.beam.repr.Operands.Int;
import erjang.beam.repr.Operands.Label;
import erjang.beam.repr.Operands.Literal;
import erjang.beam.repr.Operands.Operand;
import erjang.beam.repr.Operands.SelectList;
import erjang.beam.repr.Operands.SourceOperand;
import erjang.beam.repr.Operands.TableLiteral;
import erjang.beam.repr.Operands.XReg;
import erjang.beam.repr.Operands.YReg;
import erjang.driver.IO;
				SelectList mapKeyValues = readSelectList();
				return new Insn.MapUpdate(opcode, optlabel, src, _n, mapKeyValues, dest);
			}
			
			
			default:
				throw new IOException("Unknown instruction: "+opcode);
			} // switch
		} else throw new IOException("Unknown opcode: 0x"+Integer.toHexString(opcode_no));
    }

    //========== Utility functions ==============================
    public String readString(int len) throws IOException {
		return new String(readBinary(len));
    }
    public byte[] readBinary(int len) throws IOException {
		byte[] data = new byte[len];
		in.readFully(data);
		return data;
    }

    //========== Operand tags:
    //TODO: These need to be cleared up. Look at a bit less.
    static final int CODEINT4_TAG = 0;
    static final int INTLIT4_TAG = 1;
    static final int ATOM4_TAG = 2;
    static final int XREG4_TAG = 3;
    static final int YREG4_TAG = 4;
    static final int LABEL4_TAG = 5;
    static final int EXTENDED_TAG = 7;

    static final int CODEINT12_TAG = 8;
    static final int BIGINT_TAG = 9;
    static final int ATOM12_TAG = 10;
    static final int XREG12_TAG = 11;
    static final int YREG12_TAG = 12;
    static final int LABEL12_TAG = 13;
    static final int EXTENDED2_TAG = 7;

    //========== Extended operand tags:
    static final int FLOATLIT_TAG2   = 0;
    static final int SELECTLIST_TAG2 = 1;
    static final int FLOATREG_TAG2   = 2;
    static final int ALLOCLIST_TAG2  = 3;
    static final int LITERAL_TAG2    = 4;

    int peekTag() throws IOException {
		return in.peek() & 0x0F;
    }

    public SourceOperand readSource() throws IOException {
		return readOperand().asSource();
    }
    public DestinationOperand readDestination() throws IOException {
		return readOperand().asDestination();
    }

    public Label readOptionalLabel() throws IOException {
		return (peekTag() == LABEL4_TAG || peekTag() == LABEL12_TAG)
			? readLabel()
			: null; // 'nofail'
    }

    public Label readLabel() throws IOException {
		return readOperand().asLabel();
    }

    public Literal readLiteral() throws IOException {
		return readOperand().asLiteral();
    }

    public Atom readAtom() throws IOException {
		return readOperand().asAtom();
    }

    public BitString readBitstringRef() throws IOException {
		int bits  = readCodeInteger();
		int start = readCodeInteger();
		return new BitString(bitstring(start,bits));
    }
    public ByteString readBytestringRef() throws IOException {
		int bytes  = readCodeInteger();
		int start = readCodeInteger();
		return new ByteString(string(start,bytes));
    }

    public SelectList readSelectList() throws IOException {
		return readOperand().asSelectList();
    }

    public AllocList readAllocList() throws IOException {
		switch (peekTag()) {
		case CODEINT4_TAG:
		case CODEINT12_TAG:
		{
			int words = readCodeInteger();
			return new AllocList(words);
		}
		case EXTENDED_TAG: {
			return readOperand().asAllocList();
		}
		default:
			throw new IOException("Expected alloc list, got "+readOperand().toSymbolic());
		} // switch

    }

    public YReg readYReg() throws IOException {
		return readOperand().asYReg();
    }

    public int readCodeInteger() throws IOException {
		int d1 = in.read1();
		int tag = d1 & 0x07;
		if (tag == CODEINT4_TAG)
			return readSmallIntValue(d1);
		else
			throw new IOException("Not a code-int: "+readOperand(d1).toSymbolic());
    }

Answer:     public Operand readOperand() throws IOException {