Exemplo n.º 1
0
    def get_node_by_monotonic_function(self, function, value,
                                       rounding="closest", end_node=None):
        '''
        Get a node by specifying a measure function and a desired value.
        
        The function must be a monotonic rising function on the timeline.
        
        See documentation of garlicsim.general_misc.binary_search.binary_search
        for details about rounding options.
        '''
        
        assert rounding in ["high", "low", "exact", "both", "closest"]        

        if end_node is None:
            correct_both_for_end_node = lambda both: both
        else:
            def correct_both_for_end_node(both):
                new_both = list(both)
                end_clock = end_node.state.clock
                if new_both[0] and new_both[0].state.clock >= end_clock:
                    new_both[0] = end_node
                if new_both[1] and new_both[1].state.clock >= end_clock:
                    new_both[1] = None
                return tuple(new_both)
        
        low = self.root
        
        if function(low) >= value:
            both = correct_both_for_end_node((None, low))
            return binary_search.make_both_data_into_preferred_rounding \
                   (both, function, value, rounding)
        
        '''
        Now we've established that the first node in the path has a lower value
        than what we're looking for.
        '''
        
        for thing in self.iterate_blockwise():
            if isinstance(thing, Block):
                first = thing[0]
                if function(first) >= value:
                    both = correct_both_for_end_node((low, first))
                    return binary_search.make_both_data_into_preferred_rounding \
                           (both, function, value, rounding)
                    
                last = thing[-1]
                if function(last) >= value:
                    # It's in the block
                    both = binary_search.binary_search(thing, function, value,
                                                       rounding="both")
                    both = correct_both_for_end_node(both)
                    return binary_search.make_both_data_into_preferred_rounding \
                           (both, function, value, rounding)
                else:
                    low = last
                    continue
            else: # thing is a Node
                if function(thing) >= value:
                    both = correct_both_for_end_node((low, thing))
                    return binary_search.make_both_data_into_preferred_rounding \
                           (both, function, value, rounding)
                else:
                    low = thing
                    continue
        
        '''
        If the flow reached here, that means that even the last node
        in the path has lower value than the value we're looking for.
        '''
        
        both = correct_both_for_end_node((low, None))
        return binary_search.make_both_data_into_preferred_rounding \
               (both, function, value, rounding)
Exemplo n.º 2
0
    def __get_node_by_monotonic_function_with_both_rounding(self, function,
                                                            value):
        '''
        Get a node by specifying a measure function and a desired value.
        
        The function must be a monotonic rising function on the timeline.
        
        The rounding option used is `binary_search.BOTH`.
        
        Note that this function does not let you specify an end node. Currently
        we're not optimizing for the case where you have an end node and this
        function might waste resources exploring beyond it.
        '''
        
        root = self.root
        
        cmp_root = cmp(function(root), value)
        
        if cmp_root == 1: # function(root) > value
            return (None, root)
        if cmp_root == 0: # function(root) == value
            return (root, root)

        assert cmp_root == -1 # and function(root) < value
        
        # Now we've established that the first node in the path has a strictly
        # lower value than what we're looking for.
        
        # A rule we will strictly obey in this function: `low` will always be a
        # member whose value is lower than the desired value. (Strictly lower,
        # meaning not lower-or-equal.)
        
        low = self.root
        
        for thing in self.iterate_blockwise():
            
            # Rule: Every time we inspect a new node/block, `low` will be the
            # node that is its immediate parent. i.e. The highest node possible
            # from those that we have previously examined.
            
            if isinstance(thing, Block):
                
                block = thing
                
                first = block[0]

                cmp_first = cmp(function(first), value)
                
                if cmp_first == -1: # function(first) < value
                    low = first
                
                elif cmp_first == 0: # function(first) == value
                    return (first, first)
                    
                else: # cmp_first == 1 and function(first) > value
                    return (low, first)
                    
                
                # At this point we know that the first node in the block has a
                # strictly lower value than the target value.
                
                last = block[-1]
                
                cmp_last = cmp(function(last), value)
                
                if cmp_last == -1: # function(last) < value
                    low = last
                    continue
                
                elif cmp_last == 0: # function(last) == value                
                    return (last, last)
                
                else: # cmp_last == 1 and function(last) > value
                    # The two final results are both in the block.
                    return binary_search.binary_search(
                        block, function, value, rounding=binary_search.BOTH
                    )
                    
                
            else: # thing is a Node
                
                node = thing
                
                cmp_node = cmp(function(node), value)
                                
                if cmp_node == -1: # function(node) < value
                    low = node
                    continue
                elif cmp_node == 0: # function(node) == value
                    return (node, node)
                else: # function(node) > value
                    return (low, node)
        

        # If the flow reached here, that means that even the last node in the
        # path has lower value than the value we're looking for.
        
        return (low, None)
Exemplo n.º 3
0
    def __get_node_by_monotonic_function_with_both_rounding(
            self, function, value):
        '''
        Get a node by specifying a measure function and a desired value.
        
        The function must be a monotonic rising function on the timeline.
        
        The rounding option used is `binary_search.BOTH`.
        
        Note that this function does not let you specify a tail node. Currently
        we're not optimizing for the case where you have a tail node and this
        function might waste resources exploring beyond it.
        '''

        root = self.root

        cmp_root = cmp(function(root), value)

        if cmp_root == 1:  # function(root) > value
            return (None, root)
        if cmp_root == 0:  # function(root) == value
            return (root, root)

        assert cmp_root == -1  # and function(root) < value

        # Now we've established that the first node in the path has a strictly
        # lower value than what we're looking for.

        # A rule we will strictly obey in this function: `low` will always be a
        # member whose value is lower than the desired value. (Strictly lower,
        # meaning not lower-or-equal.)

        low = self.root

        for thing in self.iterate_blockwise():

            # Rule: Every time we inspect a new node/block, `low` will be the
            # node that is its immediate parent. i.e. The highest node possible
            # from those that we have previously examined.

            if isinstance(thing, Block):

                block = thing

                first = block[0]

                cmp_first = cmp(function(first), value)

                if cmp_first == -1:  # function(first) < value
                    low = first

                elif cmp_first == 0:  # function(first) == value
                    return (first, first)

                else:  # cmp_first == 1 and function(first) > value
                    return (low, first)

                # At this point we know that the first node in the block has a
                # strictly lower value than the target value.

                last = block[-1]

                cmp_last = cmp(function(last), value)

                if cmp_last == -1:  # function(last) < value
                    low = last
                    continue

                elif cmp_last == 0:  # function(last) == value
                    return (last, last)

                else:  # cmp_last == 1 and function(last) > value
                    # The two final results are both in the block.
                    return binary_search.binary_search(
                        block, function, value, rounding=binary_search.BOTH)

            else:  # thing is a Node

                node = thing

                cmp_node = cmp(function(node), value)

                if cmp_node == -1:  # function(node) < value
                    low = node
                    continue
                elif cmp_node == 0:  # function(node) == value
                    return (node, node)
                else:  # function(node) > value
                    return (low, node)

        # If the flow reached here, that means that even the last node in the
        # path has lower value than the value we're looking for.

        return (low, None)