class MultiType extends Type {
    private Map<String,CtClass> interfaces;
    private Type resolved;
    private Type potentialClass;
    private MultiType mergeSource;
    private boolean changed = false;

    public MultiType(Map<String,CtClass> interfaces) {
        this(interfaces, null);
    }

    public MultiType(Map<String,CtClass> interfaces, Type potentialClass) {
        super(null);
        this.interfaces = interfaces;
        this.potentialClass = potentialClass;
    }

    /**
     * Gets the class that corresponds with this type. If this information
     * is not yet known, java.lang.Object will be returned.
     */
    @Override
    public CtClass getCtClass() {
        if (resolved != null)
            return resolved.getCtClass();

        return Type.OBJECT.getCtClass();
    }

    /**
     * Always returns null since this type is never used for an array.
     */
    @Override
    public Type getComponent() {
        return null;
    }

    /**
     * Always returns 1, since this type is a reference.
     */
    @Override
    public int getSize() {
        return 1;
    }

    /**
     * Always reutnrs false since this type is never used for an array
     */
    @Override
    public boolean isArray() {
        return false;
    }

    /**
     * Returns true if the internal state has changed.
     */
    @Override
    boolean popChanged() {
        boolean changed = this.changed;
        this.changed = false;
        return changed;
    }

    @Override
    public boolean isAssignableFrom(Type type) {
        throw new UnsupportedOperationException("Not implemented");
    }

    public boolean isAssignableTo(Type type) {<FILL_FUNCTION_BODY>}

    private void propogateState() {
        MultiType source = mergeSource;
        while (source != null) {
            source.interfaces = interfaces;
            source.potentialClass = potentialClass;
            source = source.mergeSource;
        }
    }

    private void propogateResolved() {
        MultiType source = mergeSource;
        while (source != null) {
            source.resolved = resolved;
            source = source.mergeSource;
        }
    }

    /**
     * Always returns true, since this type is always a reference.
     *
     * @return true
     */
    @Override
    public boolean isReference() {
       return true;
    }

    private Map<String,CtClass> getAllMultiInterfaces(MultiType type) {
        Map<String,CtClass> map = new HashMap<String,CtClass>();

        for (CtClass intf:type.interfaces.values()) {
            map.put(intf.getName(), intf);
            getAllInterfaces(intf, map);
        }

        return map;
    }


    private Map<String,CtClass> mergeMultiInterfaces(MultiType type1, MultiType type2) {
        Map<String,CtClass> map1 = getAllMultiInterfaces(type1);
        Map<String,CtClass> map2 = getAllMultiInterfaces(type2);

        return findCommonInterfaces(map1, map2);
    }

    private Map<String,CtClass> mergeMultiAndSingle(MultiType multi, Type single) {
        Map<String,CtClass> map1 = getAllMultiInterfaces(multi);
        Map<String,CtClass> map2 = getAllInterfaces(single.getCtClass(), null);

        return findCommonInterfaces(map1, map2);
    }

    private boolean inMergeSource(MultiType source) {
        while (source != null) {
            if (source == this)
                return true;

            source = source.mergeSource;
        }

        return false;
    }

    @Override
    public Type merge(Type type) {
        if (this == type)
            return this;

        if (type == UNINIT)
            return this;

        if (type == BOGUS)
            return BOGUS;

        if (type == null)
            return this;

        if (resolved != null)
            return resolved.merge(type);

        if (potentialClass != null) {
            Type mergePotential = potentialClass.merge(type);
            if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) {
                potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential;
                changed = true;
            }
        }

        Map<String,CtClass> merged;

        if (type instanceof MultiType) {
            MultiType multi = (MultiType)type;

            if (multi.resolved != null) {
                merged = mergeMultiAndSingle(this, multi.resolved);
            } else {
                merged = mergeMultiInterfaces(multi, this);
                if (! inMergeSource(multi))
                    mergeSource = multi;
            }
        } else {
            merged = mergeMultiAndSingle(this, type);
        }

        // Keep all previous merge paths up to date
        if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) {
            // Check for changes
            if (merged.size() != interfaces.size())
                changed = true;
            else if (changed == false)
                for (String key:merged.keySet())
                    if (!interfaces.containsKey(key))
                        changed = true;


            interfaces = merged;
            propogateState();

            return this;
        }

        if (merged.size() == 1)
            resolved = Type.get(merged.values().iterator().next());
        else if (potentialClass != null)
            resolved = potentialClass;
        else
            resolved = OBJECT;

        propogateResolved();

        return resolved;
    }

    @Override
    public int hashCode() {
        if (resolved != null)
            return resolved.hashCode();

        return interfaces.keySet().hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (! (o instanceof MultiType))
            return false;

        MultiType multi = (MultiType) o;
        if (resolved != null)
            return resolved.equals(multi.resolved);
        else if (multi.resolved != null)
            return false;

        return interfaces.keySet().equals(multi.interfaces.keySet());
    }

    @Override
    public String toString() {
        if (resolved != null)
            return resolved.toString();

        StringBuffer buffer = new StringBuffer("{");
        for (String key:interfaces.keySet())
            buffer.append(key).append(", ");
        if (potentialClass != null)
            buffer.append("*").append(potentialClass.toString());
        else
            buffer.setLength(buffer.length() - 2);
        buffer.append("}");
        return buffer.toString();
    }
}

        if (resolved != null)
            return type.isAssignableFrom(resolved);

        if (Type.OBJECT.equals(type))
            return true;

        if (potentialClass != null && !type.isAssignableFrom(potentialClass))
            potentialClass = null;

        Map<String,CtClass> map = mergeMultiAndSingle(this, type);

        if (map.size() == 1 && potentialClass == null) {
            // Update previous merge paths to the same resolved type
            resolved = Type.get(map.values().iterator().next());
            propogateResolved();

            return true;
        }

        // Keep all previous merge paths up to date
        if (map.size() >= 1) {
            interfaces = map;
            propogateState();

            return true;
        }

        if (potentialClass != null) {
            resolved = potentialClass;
            propogateResolved();

            return true;
        }

        return false;
    
<methods>public boolean equals(java.lang.Object) ,public static org.hotswap.agent.javassist.bytecode.analysis.Type get(org.hotswap.agent.javassist.CtClass) ,public org.hotswap.agent.javassist.bytecode.analysis.Type getComponent() ,public org.hotswap.agent.javassist.CtClass getCtClass() ,public int getDimensions() ,public int getSize() ,public int hashCode() ,public boolean isArray() ,public boolean isAssignableFrom(org.hotswap.agent.javassist.bytecode.analysis.Type) ,public boolean isReference() ,public boolean isSpecial() ,public org.hotswap.agent.javassist.bytecode.analysis.Type merge(org.hotswap.agent.javassist.bytecode.analysis.Type) ,public java.lang.String toString() <variables>public static final org.hotswap.agent.javassist.bytecode.analysis.Type BOGUS,public static final org.hotswap.agent.javassist.bytecode.analysis.Type BOOLEAN,public static final org.hotswap.agent.javassist.bytecode.analysis.Type BYTE,public static final org.hotswap.agent.javassist.bytecode.analysis.Type CHAR,public static final org.hotswap.agent.javassist.bytecode.analysis.Type CLONEABLE,public static final org.hotswap.agent.javassist.bytecode.analysis.Type DOUBLE,public static final org.hotswap.agent.javassist.bytecode.analysis.Type FLOAT,public static final org.hotswap.agent.javassist.bytecode.analysis.Type INTEGER,public static final org.hotswap.agent.javassist.bytecode.analysis.Type LONG,public static final org.hotswap.agent.javassist.bytecode.analysis.Type OBJECT,public static final org.hotswap.agent.javassist.bytecode.analysis.Type RETURN_ADDRESS,public static final org.hotswap.agent.javassist.bytecode.analysis.Type SERIALIZABLE,public static final org.hotswap.agent.javassist.bytecode.analysis.Type SHORT,public static final org.hotswap.agent.javassist.bytecode.analysis.Type THROWABLE,public static final org.hotswap.agent.javassist.bytecode.analysis.Type TOP,public static final org.hotswap.agent.javassist.bytecode.analysis.Type UNINIT,public static final org.hotswap.agent.javassist.bytecode.analysis.Type VOID,private final non-sealed org.hotswap.agent.javassist.CtClass clazz,private static final Map<org.hotswap.agent.javassist.CtClass,org.hotswap.agent.javassist.bytecode.analysis.Type> prims,private final non-sealed boolean special