def out_of_range(low,high,*args):
    for arg in args:
        if arg < low or arg > high:
            return True
    return False
    

class OrderedSet(set):
    def __init__(self:'OrderedSet',*args):
        # *args means that any number of arguments will be packed into 
        # one tuple named args
        set.__init__(self)
        self._list = []
        cur_size = 0
        for a in args:
            # first try adding via set's add method
            set.add(self,a)
            if len(self) > cur_size:
                # if a was not already in the set, 
                # then add it to the end of the list
                self._list.append(a)
                cur_size += 1
        
    def add(self:'OrderedSet',x):
        """ 
        OVERRIDE set.add, so it adds an element to the end of the set        
        """
        cur_size = len(self)
        # first use set's add method
        set.add(self,x) 
        
        if len(self) > cur_size:            
            self._list.append(x)
            
    def swap(self,i:int,j:int):
        """swap two elements"""
        if out_of_range(0,len(self)-1, i, j):
            raise Exception("swap index out of range")
        temp = self[i]
        self._list[i] = self[j]
        self._list[j] = temp
            
    def __iter__(self:'OrderedSet'): 
        """ 
        override (for ... in ...) syntax 
        we want to use the list's iterator since, unlike the set's iterator,
        it is guaranteed to respect order.
        """
        return self._list.__iter__
    
    def __iadd__(self:'OrderedSet', other:'OrderedSet'):
        """
        this is +, which is a method of lists, but not sets
        so we are EXTENDING set
        """
        for x in other:
            self.add(x)
            
    def __getitem__(self,i:int) -> object:
        """
        this the method called when you use the syntax some_array[i].
        it's a method of lists, but not sets, so we are EXTENDING set        
        """
        return self._list.__getitem__(i)
            
        
    def __repr__(self) -> 'OrderedSet':
        args = tuple(self._list)
        return "OrderedSet{0}".format(args)