class Types {

	public static boolean isSubtype(Type subType, Type superType) {
	    if (Types.isEqual(subType, superType)) return true;
	    if (subType instanceof IntegerType && superType instanceof FloatType) {
	        return true;
	    }
	    if (superType instanceof TopReferenceType && isRefType(subType)) {
	        return true;
	    }
	    if (subType instanceof BottomReferenceType && isRefType(superType)) {
	        return true;
	    }
	    if (subType instanceof ObjectType && superType instanceof ObjectType) {
	        // NOTE: this only handles concrete types and does not reason about MRO/MRW
	        ObjectType subObjType = (ObjectType) subType;
	        List<Property> superTypeProps = ((ObjectType)superType).properties();
	        // each super type property should be present in the subtype with (1) the same
	        // type and (2) an equally strong or stronger read-write permission
	        for (Property superProp: superTypeProps) {
	            if (!subObjType.hasProperty(superProp.getName())) {
	                return false;
	            }
	            Property subProp = subObjType.getProperty(superProp.getName());
	            if (!Types.isEqual(superProp.getType(), subProp.getType())) {
	                return false;
	            }
	            if (superProp.isRW() && !subProp.isRW()) { // permissions weakened
	                return false;
	            }
	        }
	        return true;
	    }
	    return false;
	}

}