def test_bisect_left(): slt = SortedList() assert slt.bisect_left(0) == 0 slt = SortedList(range(100), load=17) slt.update(range(100)) slt._check() assert slt.bisect_left(50) == 100 assert slt.bisect_left(200) == 200
class MyCalendar: ''' We need to make sure that q1 == q2, that is that the point we we need to insert start and end is in the same gap. Also q1 % 2 == 0, because it should be gap between two intervals, see [1, 3), [5, 10). If we found that we need to insert both start and end to [3, 5), it is OK for us, if to [1, 3) or [5, 10) it is not OK. Complexity Time complexity of one book is O(log n), time complexity of all algorithm is O(n log n). Space complexity is O(n). ''' def __init__(self): self.s_list = SortedList() def book(self, start: int, end: int) -> bool: left = self.s_list.bisect_right(start) right = self.s_list.bisect_left(end) if left == right and left % 2 == 0: #can insert self.s_list.add(start) self.s_list.add(end) return True return False # Your MyCalendar object will be instantiated and called as such: # obj = MyCalendar() # param_1 = obj.book(start,end)
class MKAverage: def __init__(self, m: int, k: int): self.m, self.k = m, k self.deque = collections.deque() self.sl = SortedList() self.total = self.first_k = self.last_k = 0 def addElement(self, num: int) -> None: self.total += num self.deque.append(num) index = self.sl.bisect_left(num) if index < self.k: self.first_k += num if len(self.sl) >= self.k: self.first_k -= self.sl[self.k - 1] if index >= len(self.sl) + 1 - self.k: self.last_k += num if len(self.sl) >= self.k: self.last_k -= self.sl[-self.k] self.sl.add(num) if len(self.deque) > self.m: num = self.deque.popleft() self.total -= num index = self.sl.index(num) if index < self.k: self.first_k -= num self.first_k += self.sl[self.k] elif index >= len(self.sl) - self.k: self.last_k -= num self.last_k += self.sl[-self.k - 1] self.sl.remove(num) def calculateMKAverage(self) -> int: if len(self.sl) < self.m: return -1 return (self.total - self.first_k - self.last_k) // (self.m - 2 * self.k)
def closestRoom(self, rooms: List[List[int]], queries: List[List[int]]) -> List[int]: new_rooms = [] for roomId, size in rooms: new_rooms.append((-size, roomId)) new_rooms.sort() new_queries = [] for query_idx, (preferred, minSize) in enumerate(queries): new_queries.append((-minSize, preferred, query_idx)) new_queries.sort() sorted_list = SortedList() idx = 0 ans = [-1 for _ in range(len(queries))] for minSize, preferred, query_idx in new_queries: while idx < len(new_rooms) and new_rooms[idx][0] <= minSize: sorted_list.add(new_rooms[idx][1]) idx += 1 option1 = sorted_list.bisect_left(preferred) best_room = -1 if 0 <= option1 < len(sorted_list): best_room = sorted_list[option1] option2 = option1 - 1 if 0 <= option2 < len(sorted_list): # use "<=" because option2 is smaller than option1 and is preferred when absolute distance is same if abs(preferred - sorted_list[option2]) <= abs(preferred - best_room): best_room = sorted_list[option2] ans[query_idx] = best_room return ans ''' Notes: [1,2,3] bisect left will get the 2 at idx at 1 [1,3,3] bisect left will get the 3 at idx at 1 that is why we also check bosect left -1 to consider 1 at idx at 0 '''
def get_next_book(sequence, timestamp, books_df): """ Get next book after the sequence if available. Parameters ---------- sequence: int timestamp: datetime books_df: DataFrame multiple books Returns ------- GdaxOrderBook or None """ if books_df.empty: logger.error('No books available.') return # get index of next sequence seq_list = SortedList(books_df['sequence'].unique()) next_seq_idx = seq_list.bisect_left(sequence) if next_seq_idx >= len(seq_list): logger.error( 'No book found after {} ({}). Available books: {}'.format( timestamp, sequence, seq_list)) return # next book next_seq = seq_list[next_seq_idx] book_df = books_df[books_df['sequence'] == next_seq] book = sutil.df_to_book(book_df) logger.info('Current message at {} ({}). Got book at {} ({})'.format( timestamp, sequence, book.sequence, book.timestamp)) return book
def busiest_servers(self, k: int, arrival: List[int], load: List[int]) -> List[int]: available = SortedList(range(k)) busy = [] requests = [0] * k for i, start in enumerate(arrival): while busy and busy[0][0] <= start: available.add(busy[0][1]) heapq.heappop(busy) if not available: continue j = available.bisect_left(i % k) if j == len(available): j = 0 idx = available[j] requests[idx] += 1 heapq.heappush(busy, (start + load[i], idx)) available.remove(idx) max_cnt = max(requests) ans = [] for i, req in enumerate(requests): if req == max_cnt: ans.append(i) return ans
def sequentialDigits(self, low: int, high: int) -> List[int]: def digit_dp(curnumber,limit,ans): if curnumber>limit: return ans.add(curnumber) if curnumber==0: for i in range(1,10): digit_dp(i,limit,ans) else: lastdigit = int(str(curnumber)[-1]) if lastdigit!=9: digit_dp(curnumber*10+lastdigit+1,limit,ans) return ansb = SortedList() digit_dp(0,high,ansb) #ansb = sorted(ansb) pos = ansb.bisect_left(low) return ansb[pos:]
class AbstractAllocator: """Manage allocations of arbitrary size within a sliceable block of items. This is abstract because we don't actually assume anything about the types of the items or how to write to them. """ # TODO: rename this to something like ContiguousBlockAllocator def __init__(self, capacity: int = 8192): self.capacity = capacity self.allocs = {} # mapping of offset -> reserved size self._free = SortedList([(capacity, 0)]) def avail(self) -> int: """Get the current available capacity.""" return sum(cap for cap, off in self._free) def grow(self, new_capacity: int): """Grow the available space for allocation to new_capacity. No reallocations or compactions are done here so the only guaranteed contiguous new free space is at the end. """ self._release(self.capacity, new_capacity - self.capacity) self.capacity = new_capacity def _release(self, offset, length): """Release the given block back to the pool. We consider joining this block to a subsequent one. We don't currently consider joining it to the previous block. """ if not length: return while True: k = (length, offset) idx = self._free.bisect_left(k) if idx >= len(self._free): self._free.add(k) break next_length, next_off = self._free[idx] if next_off != (length + offset): self._free.add(k) break self._free.pop(idx) length += next_length def check(self): """Check invariants.""" free = sum(cap for cap, _ in self._free) allocated = sum(self.allocs.values()) assert free + allocated == self.capacity, \ f"Free: {free}, Allocated: {allocated}, Capacity: {self.capacity}" return True def alloc(self, num: int) -> slice: """Allocate a block of size num. Return a slice, or raise NoCapacity if there was insufficient capacity available. """ pos = self._free.bisect_left((num, 0)) if pos == len(self._free): # capacity is not high enough new_capacity = self.capacity while new_capacity < num: new_capacity *= 2 err = NoCapacity() err.recommended = self.capacity + new_capacity raise err # We have located where we want to reserve, insert it return self._reserve(pos, num) def _reserve(self, pos, num) -> slice: """Update the free list with the given reservation. Return the reserved slice. """ block_size, offset = self._free.pop(pos) # Release the rest of the block in power-of-2 blocks mid = block_size // 2 while mid >= num >= 2: self._release(offset + mid, block_size - mid) block_size = mid mid = block_size // 2 end_off = offset + num self._release(end_off, block_size - num) # Store the size of the block that we allocated self.allocs[offset] = num return slice(offset, end_off) def realloc(self, offset: Union[int, slice], new_size: int) -> slice: """Reallocate the given block. This is optimised so that if there is extra space in the original allocation, it can be done without moving the block. This operation can fail, raising NoCapacity. """ if isinstance(offset, slice): offset = offset.start try: size = self.allocs[offset] except KeyError: raise KeyError(f"Offset {offset} is not allocated.") from None if new_size <= size: return slice(offset, offset + new_size) del self.allocs[offset] # TODO: copying the list before use makes this operation # O(n) (but probably low constant factor) when it should be # O(log n)-ish free_before = list(self._free) self._release(offset, size) try: return self.alloc(new_size) except NoCapacity: # We don't have enough capacity, re-insert before raising self.allocs[offset] = size self._free = SortedList(free_before) raise def free(self, offset: Union[int, slice]): """Free the block at offset.""" if isinstance(offset, slice): offset = offset.start try: size = self.allocs.pop(offset) except KeyError: raise KeyError(f"Offset {offset} is not allocated.") from None self._release(offset, size)
# 1871. Jump Game VII # https://leetcode.com/problems/jump-game-vii from sortedcontainers import SortedList class Solution: def canReach(self, s: str, minJump: int, maxJump: int) -> bool: n = len(s) sl = SortedList([0]) for i in range(1, n): if s[i] == "0": index = sl.bisect_left(i - maxJump) if 0 <= index < len(sl) and sl[index] + minJump <= i: sl.add(i) return n - 1 in sl
class IntervalList: def __init__(self, intervals): self.intervals = SortedList([], key=_get_lower) for interval in intervals: self.unite(interval) def unite(self, interval): """ Inserts the given interval into the list of interval. Takes care to merge any adjacent intervals. """ first, last = self._intersect_continuous(interval) if first != last: interval.lower = min(interval.lower, self.intervals[first].lower) interval.upper = max(interval.upper, self.intervals[last - 1].upper) del self.intervals[first:last] self.intervals.add(interval) def _intersect_continuous(self, interval): """ Finds all intervals whose intersection with interval is not empty; also includes the two intervals the the most left and right if their bounds are directly adjacent to the interval. For example, if intervals contains: [..., [1, 3), [6, 8), ...] and this function is called with [3, 6), it includes the two intervals. """ first = self.intervals.bisect_left(interval) last = first while first > 0 and \ self.intervals[first - 1].upper >= interval.lower: first -= 1 while last < len(self.intervals) and \ self.intervals[last].lower <= interval.upper: last += 1 return first, last def _intersect(self, interval): """ Finds all intervals whose intersection with interval is not empty. """ first = self.intervals.bisect_left(interval) last = first while first > 0 and \ self.intervals[first - 1].upper > interval.lower: first -= 1 while last < len(self.intervals) and \ self.intervals[last].lower < interval.upper: last += 1 return first, last def subtract(self, interval): """ Removes the given interval from all intervals. Takes care to remove empty intervals. """ first, last = self._intersect(interval) if last - first == 1: new_intervals = self.intervals[first].subtract(interval) del self.intervals[first] self.intervals.update(new_intervals) elif last - first != 0: if self.intervals[first].lower != interval.lower: self.intervals[first].upper = interval.lower if self.intervals[first].length > 0: first = min(first + 1, len(self.intervals)) if self.intervals[last - 1].upper != interval.upper: self.intervals[last - 1].lower = interval.upper if self.intervals[last - 1].length > 0: last = max(last - 1, 0) del self.intervals[first:last] def find(self, value): """ Returns the interval that contains the given value. """ index = self.intervals.bisect_left(value) if index < len( self.intervals) and self.intervals[index].lower == value: return self.intervals[index] if index > 0 and self.intervals[index - 1].contains(value): return self.intervals[index - 1] return None def contains(self, interval): """ Returns true if the list of intervals has a non-empty intersection with the given interval. """ first, last = self._intersect(interval) return first != last def __iter__(self): return self.intervals.__iter__() def __len__(self): return self.intervals.__len__()
class PriorityDict(MutableMapping): """ A PriorityDict provides the same methods as a dict. Additionally, a PriorityDict efficiently maintains its keys in value sorted order. Consequently, the keys method will return the keys in value sorted order, the popitem method will remove the item with the highest value, etc. """ def __init__(self, *args, **kwargs): """ A PriorityDict provides the same methods as a dict. Additionally, a PriorityDict efficiently maintains its keys in value sorted order. Consequently, the keys method will return the keys in value sorted order, the popitem method will remove the item with the highest value, etc. If the first argument is the boolean value False, then it indicates that keys are not comparable. By default this setting is True and duplicate values are tie-breaked on the key. Using comparable keys improves the performance of the PriorityDict. An optional *iterable* argument provides an initial series of items to populate the PriorityDict. Each item in the sequence must itself contain two items. The first is used as a key in the new dictionary, and the second as the key's value. If a given key is seen more than once, the last value associated with it is retained in the new dictionary. If keyword arguments are given, the keywords themselves with their associated values are added as items to the dictionary. If a key is specified both in the positional argument and as a keyword argument, the value associated with the keyword is retained in the dictionary. For example, these all return a dictionary equal to ``{"one": 2, "two": 3}``: * ``SortedDict(one=2, two=3)`` * ``SortedDict({'one': 2, 'two': 3})`` * ``SortedDict(zip(('one', 'two'), (2, 3)))`` * ``SortedDict([['two', 3], ['one', 2]])`` The first example only works for keys that are valid Python identifiers; the others work with any valid keys. Note that this constructor mimics the Python dict constructor. If you're looking for a constructor like collections.Counter(...), see PriorityDict.count(...). """ self._dict = dict() if len(args) > 0 and isinstance(args[0], bool): if args[0]: self._list = SortedList() else: self._list = SortedListWithKey(key=lambda tup: tup[0]) else: self._list = SortedList() self.iloc = _IlocWrapper(self) self.update(*args, **kwargs) def clear(self): """Remove all elements from the dictionary.""" self._dict.clear() self._list.clear() def clean(self, value=0): """ Remove all items with value less than or equal to `value`. Default `value` is 0. """ _list, _dict = self._list, self._dict pos = self.bisect_right(value) for key in (key for value, key in _list[:pos]): del _dict[key] del _list[:pos] def __contains__(self, key): """Return True if and only if *key* is in the dictionary.""" return key in self._dict def __delitem__(self, key): """ Remove ``d[key]`` from *d*. Raises a KeyError if *key* is not in the dictionary. """ value = self._dict[key] self._list.remove((value, key)) del self._dict[key] def __getitem__(self, key): """ Return the priority of *key* in *d*. Raises a KeyError if *key* is not in the dictionary. """ return self._dict[key] def __iter__(self): """ Create an iterator over the keys of the dictionary ordered by the value sort order. """ return iter(key for value, key in self._list) def __reversed__(self): """ Create an iterator over the keys of the dictionary ordered by the reversed value sort order. """ return iter(key for value, key in reversed(self._list)) def __len__(self): """Return the number of (key, value) pairs in the dictionary.""" return len(self._dict) def __setitem__(self, key, value): """Set `d[key]` to *value*.""" if key in self._dict: old_value = self._dict[key] self._list.remove((old_value, key)) self._list.add((value, key)) self._dict[key] = value def copy(self): """Create a shallow copy of the dictionary.""" result = PriorityDict() result._dict = self._dict.copy() result._list = self._list.copy() result.iloc = _IlocWrapper(result) return result def __copy__(self): """Create a shallow copy of the dictionary.""" return self.copy() @classmethod def fromkeys(cls, iterable, value=0): """ Create a new dictionary with keys from `iterable` and values set to `value`. The default *value* is 0. """ return PriorityDict((key, value) for key in iterable) def get(self, key, default=None): """ Return the value for *key* if *key* is in the dictionary, else *default*. If *default* is not given, it defaults to ``None``, so that this method never raises a KeyError. """ return self._dict.get(key, default) def has_key(self, key): """Return True if and only in *key* is in the dictionary.""" return key in self._dict def pop(self, key, default=_NotGiven): """ If *key* is in the dictionary, remove it and return its value, else return *default*. If *default* is not given and *key* is not in the dictionary, a KeyError is raised. """ if key in self._dict: value = self._dict[key] self._list.remove((value, key)) return self._dict.pop(key) else: if default == _NotGiven: raise KeyError else: return default def popitem(self, index=-1): """ Remove and return item at *index* (default: -1). Raises IndexError if dict is empty or index is out of range. Negative indices are supported as for slice indices. """ value, key = self._list.pop(index) del self._dict[key] return key, value def setdefault(self, key, default=0): """ If *key* is in the dictionary, return its value. If not, insert *key* with a value of *default* and return *default*. *default* defaults to ``0``. """ if key in self._dict: return self._dict[key] else: self._dict[key] = default self._list.add((default, key)) return default def elements(self): """ Return an iterator over elements repeating each as many times as its count. Elements are returned in value sort-order. If an element’s count is less than one, elements() will ignore it. """ values = (repeat(key, value) for value, key in self._list) return chain.from_iterable(values) def most_common(self, count=None): """ Return a list of the `count` highest priority elements with their priority. If `count` is not specified, `most_common` returns *all* elements in the dict. Elements with equal counts are ordered by key. """ _list, _dict = self._list, self._dict if count is None: return [(key, value) for value, key in reversed(_list)] end = len(_dict) start = end - count return [(key, value) for value, key in reversed(_list[start:end])] def subtract(self, elements): """ Elements are subtracted from an iterable or from another mapping (or counter). Like dict.update() but subtracts counts instead of replacing them. Both inputs and outputs may be zero or negative. """ self -= Counter(elements) def tally(self, *args, **kwargs): """ Elements are counted from an iterable or added-in from another mapping (or counter). Like dict.update() but adds counts instead of replacing them. Also, the iterable is expected to be a sequence of elements, not a sequence of (key, value) pairs. """ self += Counter(*args, **kwargs) @classmethod def count(self, *args, **kwargs): """ Consume `args` and `kwargs` with a Counter and use that mapping to initialize a PriorityDict. """ return PriorityDict(Counter(*args, **kwargs)) def update(self, *args, **kwargs): """ Update the dictionary with the key/value pairs from *other*, overwriting existing keys. *update* accepts either another dictionary object or an iterable of key/value pairs (as a tuple or other iterable of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: ``d.update(red=1, blue=2)``. """ _list, _dict = self._list, self._dict if len(args) == 1 and len(kwargs) == 0 and isinstance(args[0], Mapping): items = args[0] else: items = dict(*args, **kwargs) if (10 * len(items)) > len(_dict): _dict.update(items) _list.clear() _list.update((value, key) for key, value in iteritems(_dict)) else: for key, value in iteritems(items): old_value = _dict[key] _list.remove((old_value, key)) _dict[key] = value _list.add((value, key)) def index(self, key): """ Return the smallest *i* such that `d.iloc[i] == key`. Raises KeyError if *key* is not present. """ value = self._dict[key] return self._list.index((value, key)) def bisect_left(self, value): """ Similar to the ``bisect`` module in the standard library, this returns an appropriate index to insert *value* in PriorityDict. If *value* is already present in PriorityDict, the insertion point will be before (to the left of) any existing entries. """ return self._list.bisect_left((value,)) def bisect(self, value): """Same as bisect_left.""" return self._list.bisect((value,)) def bisect_right(self, value): """ Same as `bisect_left`, but if *value* is already present in PriorityDict, the insertion point will be after (to the right of) any existing entries. """ return self._list.bisect_right((value, _Biggest)) def __iadd__(self, that): """Add values from `that` mapping.""" _list, _dict = self._list, self._dict if len(_dict) == 0: _dict.update(that) _list.update((value, key) for key, value in iteritems(_dict)) elif len(that) * 3 > len(_dict): _list.clear() for key, value in iteritems(that): if key in _dict: _dict[key] += value else: _dict[key] = value _list.update((value, key) for key, value in iteritems(_dict)) else: for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _list.remove((old_value, key)) value = old_value + value _dict[key] = value _list.add((value, key)) return self def __isub__(self, that): """Subtract values from `that` mapping.""" _list, _dict = self._list, self._dict if len(_dict) == 0: _dict.clear() _list.clear() elif len(that) * 3 > len(_dict): _list.clear() for key, value in iteritems(that): if key in _dict: _dict[key] -= value _list.update((value, key) for key, value in iteritems(_dict)) else: for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _list.remove((old_value, key)) value = old_value - value _dict[key] = value _list.add((value, key)) return self def __ior__(self, that): """Or values from `that` mapping (max(v1, v2)).""" _list, _dict = self._list, self._dict if len(_dict) == 0: _dict.update(that) _list.update((value, key) for key, value in iteritems(_dict)) elif len(that) * 3 > len(_dict): _list.clear() for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _dict[key] = old_value if old_value > value else value else: _dict[key] = value _list.update((value, key) for key, value in iteritems(_dict)) else: for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _list.remove((old_value, key)) value = old_value if old_value > value else value _dict[key] = value _list.add((value, key)) return self def __iand__(self, that): """And values from `that` mapping (min(v1, v2)).""" _list, _dict = self._list, self._dict if len(_dict) == 0: _dict.clear() _list.clear() elif len(that) * 3 > len(_dict): _list.clear() for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _dict[key] = old_value if old_value < value else value _list.update((value, key) for key, value in iteritems(_dict)) else: for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _list.remove((old_value, key)) value = old_value if old_value < value else value _dict[key] = value _list.add((value, key)) return self def __add__(self, that): """Add values from this and `that` mapping.""" result = PriorityDict() _list, _dict = result._list, result._dict _dict.update(self._dict) for key, value in iteritems(that): if key in _dict: _dict[key] += value else: _dict[key] = value _list.update((value, key) for key, value in iteritems(_dict)) return result def __sub__(self, that): """Subtract values in `that` mapping from this.""" result = PriorityDict() _list, _dict = result._list, result._dict _dict.update(self._dict) for key, value in iteritems(that): if key in _dict: _dict[key] -= value _list.update((value, key) for key, value in iteritems(_dict)) return result def __or__(self, that): """Or values from this and `that` mapping.""" result = PriorityDict() _list, _dict = result._list, result._dict _dict.update(self._dict) for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _dict[key] = old_value if old_value > value else value else: _dict[key] = value _list.update((value, key) for key, value in iteritems(_dict)) return result def __and__(self, that): """And values from this and `that` mapping.""" result = PriorityDict() _list, _dict = result._list, result._dict _dict.update(self._dict) for key, value in iteritems(that): if key in _dict: old_value = _dict[key] _dict[key] = old_value if old_value < value else value _list.update((value, key) for key, value in iteritems(_dict)) return result def __eq__(self, that): """Compare two mappings for equality.""" if isinstance(that, PriorityDict): that = that._dict return self._dict == that def __ne__(self, that): """Compare two mappings for inequality.""" if isinstance(that, PriorityDict): that = that._dict return self._dict != that def __lt__(self, that): """Compare two mappings for less than.""" if isinstance(that, PriorityDict): that = that._dict _dict = self._dict return (_dict != that and self <= that) def __le__(self, that): """Compare two mappings for less than equal.""" if isinstance(that, PriorityDict): that = that._dict _dict = self._dict return (len(_dict) <= len(that) and all(_dict[key] <= that[key] if key in that else False for key in _dict)) def __gt__(self, that): """Compare two mappings for greater than.""" if isinstance(that, PriorityDict): that = that._dict _dict = self._dict return (_dict != that and self >= that) def __ge__(self, that): """Compare two mappings for greater than equal.""" if isinstance(that, PriorityDict): that = that._dict _dict = self._dict return (len(_dict) >= len(that) and all(_dict[key] >= that[key] if key in _dict else False for key in that)) def isdisjoint(self, that): """ Return True if no key in `self` is also in `that`. This doesn't check that the value is greater than zero. To remove keys with value less than or equal to zero see *clean*. """ return not any(key in self for key in that) def items(self): """ Return a list of the dictionary's items (``(key, value)`` pairs). Items are ordered by their value from least to greatest. """ return list((key, value) for value, key in self._list) def iteritems(self): """ Return an iterable over the items (``(key, value)`` pairs) of the dictionary. Items are ordered by their value from least to greatest. """ return iter((key, value) for value, key in self._list) @not26 def viewitems(self): """ In Python 2.7 and later, return a new `ItemsView` of the dictionary's items. Beware iterating the `ItemsView` as items are unordered. In Python 2.6, raise a NotImplementedError. """ if hexversion < 0x03000000: return self._dict.viewitems() else: return self._dict.items() def keys(self): """ Return a list of the dictionary's keys. Keys are ordered by their corresponding value from least to greatest. """ return list(key for value, key in self._list) def iterkeys(self): """ Return an iterable over the keys of the dictionary. Keys are ordered by their corresponding value from least to greatest. """ return iter(key for value, key in self._list) @not26 def viewkeys(self): """ In Python 2.7 and later, return a new `KeysView` of the dictionary's keys. Beware iterating the `KeysView` as keys are unordered. In Python 2.6, raise a NotImplementedError. """ if hexversion < 0x03000000: return self._dict.viewkeys() else: return self._dict.keys() def values(self): """ Return a list of the dictionary's values. Values are ordered from least to greatest. """ return list(value for value, key in self._list) def itervalues(self): """ Return an iterable over the values of the dictionary. Values are iterated from least to greatest. """ return iter(value for value, key in self._list) @not26 def viewvalues(self): """ In Python 2.7 and later, return a `ValuesView` of the dictionary's values. Beware iterating the `ValuesView` as values are unordered. In Python 2.6, raise a NotImplementedError. """ if hexversion < 0x03000000: return self._dict.viewvalues() else: return self._dict.values() def __repr__(self): """Return a string representation of PriorityDict.""" return 'PriorityDict({0})'.format(repr(dict(self))) def _check(self): self._list._check() assert len(self._dict) == len(self._list) assert all(key in self._dict and self._dict[key] == value for value, key in self._list)
class Analyse(): def __init__(self, config): self.year = config['YEAR'] self.startTime = config['START_TIME'] self.endTime = config['END_TIME'] self.stockList = config['STOCK_LIST'] self.mode = config['MODE'] self.logBucket = config['LOG_BUCKET_DATA'] self.hedgeFlag = config['HEDGE'] self.hedgeStock = config['HEDGE_STOCK'] self.divideByVol = config['DIVIDE_BY_VOLATILITY'] self.modStockList = self.stockList if (self.hedgeFlag): self.betaCorrelation = config['BETA_CORR'] self.modStockList = [config['HEDGE_STOCK']] + self.stockList self.corrFlag = config['BETA_CORR_TYPE'] if (self.mode == 'bucket'): self.bucketSize = config['BUCKET_SIZE'] self.numBucket = config['NUM_BUCKET'] elif (self.mode == 'percentile'): self.bucketSize = config['BUCKET_SIZE'] self.minSize = config['MIN_SIZE'] self.maxSize = config['MAX_SIZE'] self.absFlag = config['ABS_FLAG'] config['STOCK_LIST'] = self.modStockList # Datastore contains functions to read and update prices self.dataStore = dataStore(config) config['STOCK_LIST'] = self.stockList # Class members containing relevant Statistics # self.results: Dictionary containing stock names as keys # Maps to a list of lists, where each list member # contains gapSize, timeStamp, Open/Close prices # along with holding periods, etc self.results = {} self.gapListNormalized = [] self.prevCloseVWAPWindow = config['VWAP_PREV_CLOSE_WINDOW'] self.currOpenVWAPWindow = config['VWAP_CURR_OPEN_WINDOW'] self.posEntryVWAPWindow = config['VWAP_POSITION_ENTRY_WINDOW'] self.posExitVWAPWindow = config['VWAP_POSITION_EXIT_WINDOW'] self.printFlag = 0 self.stopLoss = config['STOP_LOSS'] self.targetPrice = config['TARGET_PRICE'] self.tTestFlag = config['T_TEST_FLAG'] if (self.tTestFlag): self.profitByGapPercentile = {} for i in range(0, 100): self.profitByGapPercentile[i] = [] self.stockReturns = {} def loadData(self): ''' Loads price data for the specified year and stock list Returns: None, only class members are modified ''' self.dataStore.loadPriceData() for stock in self.stockList: price = pd.DataFrame( self.dataStore.priceDataList[stock][:]).iloc[:, 6] returns = ((price / price.shift(1)) - 1)[1:] self.stockReturns[stock] = returns if (self.hedgeFlag): price = pd.DataFrame( self.dataStore.priceDataList[self.hedgeStock][:]).iloc[:, 6] returns = ((price / price.shift(1)) - 1)[1:] self.stockReturns[self.hedgeStock] = returns # print(self.stockReturns) def getRetList(self, stock): price = pd.DataFrame( self.dataStore.priceDataList[stock][::minInDay]).iloc[:, 6] price = ((price / price.shift(1)) - 1)[1:] return price def getBenchmarkVolatility(self): price = pd.DataFrame( self.hedgePriceList[self.hedgeStock][::minInDay]).iloc[:, 6] price = ((price / price.shift(1)) - 1)[1:] return price def getVolatilityNDays(self, stock, n, currTimeRow): """ Gets the volatility by taking returns of close prices for the last n days and does P(t) / P(t-1) - 1 for each of the n days and takes stDev """ # price = pd.DataFrame(self.dataStore.priceDataList[stock][currTimeRow - 1 - (n * 375):currTimeRow - 1]).iloc[:, 6] # returns = ((price / price.shift(1)) - 1)[1:] returns = self.stockReturns[stock].iloc[currTimeRow - 1 - (n * 375):currTimeRow - 1] if (debug): print("Volatility: " + str(np.std(returns))) return np.std(returns) def getCorrelation(self, stock1, stock2, i1, i2, n): """ Takes the prices of two stocks, calculates their return and gives their correlation """ # price1 = pd.DataFrame(self.dataStore.priceDataList[stock1][-(n * 375) - 1 + i1:i1]).iloc[:, 6] # price2 = pd.DataFrame(self.dataStore.priceDataList[stock2][-(n * 375) - 1 + i2:i2]).iloc[:, 6] returns1 = self.stockReturns[stock1].iloc[i1 - 1 - (n * 375):i1 - 1] returns2 = self.stockReturns[stock2].iloc[i2 - 1 - (n * 375):i2 - 1] print(i1, i2) # print(returns1[-10:]) # print(returns2[-10:]) # if(len(price1) > len(price2)): # # print("Price1: " + str(price1)) # # print("Price2: " + str(price2)) # price1 = price1[-len(price2):] # print(i1,i2,len(price1),len(price2)) # if(len(price2) > len(price1)): # price2 = price2[-len(price1):] # print(i1,i2,len(price1),len(price2)) correlation = np.corrcoef(returns1, returns2)[1][0] return correlation def getVolAvgPrice(self, stock, left, right): ''' Computes the volume weighted price for the range [left, right) price = (low + high + open + close)/4 ''' if (debug): print('\n' + ''.join(['*'] * 50)) print("Stock prices") print(left, right) print("Left price: " + str(self.dataStore.priceDataList[stock][left])) print("Right price: " + str(self.dataStore.priceDataList[stock][right])) price = np.array(self.dataStore.priceDataList[stock][left:right])[:, 5:] price = price.astype(np.float64) # 5, 6, 7, 8, 9: Open, Close, Low, High, Volume # After trimming off strings, 0, 1, 2, 3, 4: Opne, Close, Low, High, Volume avgPrice = (price[:, 0] + price[:, 1] + price[:, 2] + price[:, 3]) / 4.0 volume = price[:, 4] volAvgPrice = np.average(avgPrice, weights=volume) return volAvgPrice def getTTestScores(self, boundary, profitByGapPercentileLocal, verbose=False): #Returns the T test score and p-value of two arrays arr1 = [] arr2 = [] for i in range(1, boundary + 1): arr1 += profitByGapPercentileLocal[i] for i in range(boundary + 1, 99): arr2 += profitByGapPercentileLocal[i] tTest = ttest_ind(arr1, arr2) tValue, pValue = tTest[0], tTest[1] if (verbose): print("Boundary: " + str(boundary)) print("T Value: " + str(tValue)) print("P Value: " + str(pValue)) return tValue, pValue def getGapStats(self, holdPeriodList, volType='nGapVol', verbose=False): ''' Gives the statistics (Gap trading) for all hold periods specified The stats include timestamp, curr open price (after VWAP), prev close price (after VWAP), volatility holding period (H), min price/max price in interval, closing price after H etc Args: holdPeriodList: Contains holding periods as number of minutes volType; dailyVol or nDayVol (n = 30 by default) Returns: Dictionary as described above ''' statList = {} priceList = {} gapList = {} if (self.hedgeFlag): # BM is benchmark gapListBM = [] volListBM = [] timeListBM = [] # retList contains daily returns retListBM = [] priceListBM = [] priceTimeBM = [] #Stores all the timestamps for which the benchmark is indexed benchmarkTimeStamps = [ eachList[0] for eachList in self.dataStore.priceDataList[self.hedgeStock] ] volN = 70 # For standard volatility calculation of gapsize if (volType != 'stdVol'): volN = 30 volDays = 70 # For standard volatility of entire calculation of returns for stock in self.modStockList: # Perform analysis for each stock infoList = self.dataStore.priceDataList[stock] statList[stock] = [] priceList[stock] = [] gapList[stock] = [] # gapListBenchmark[self.hedgeStock] = [] retList = self.getRetList(stock) prevTime = 0 print 'Currently analysing:', stock for i in range(len(infoList)): currTime = infoList[i][0] currTimeStamp = datetime.fromtimestamp(currTime) currDay = currTimeStamp.date() currHour = currTimeStamp.time().hour currMins = currTimeStamp.time().minute # Account for duplicates if (prevTime == currTime): continue prevTime = currTime if (not (self.startTime <= currTime <= self.endTime)): # Check if it is in the valid range continue if ((currHour == 9) and (currMins == 15)): # Checking for day starting time if (stock == 'SBIN' and currTimeStamp.date().day == 9 and currTimeStamp.date().month == 11 and self.year == 2016): self.printFlag = 1 if (debug): print('\n' + ''.join(['*'] * 50)) #getting prices for stock currOpen = self.getVolAvgPrice(stock, i, i + self.currOpenVWAPWindow) prevClose = self.getVolAvgPrice( stock, i - self.prevCloseVWAPWindow, i) posEntryPrice = self.getVolAvgPrice( stock, i + self.currOpenVWAPWindow, i + self.currOpenVWAPWindow + self.posEntryVWAPWindow) if ((self.hedgeFlag) and (self.hedgeStock == stock)): priceListBM.append(currOpen) priceTimeBM.append(currTime) priceList[stock].append(currOpen) gapList[stock].append((currOpen - prevClose) / prevClose) # Not enough samples to compute std dev, added five to handle edge cases if (len(gapList[stock]) < volN + 5): continue # Refers to the stats common accross the holding periods commStats = {} commStats['time'] = currTime commStats['readableTime'] = datetime.fromtimestamp( currTime) commStats['ticker'] = stock commStats['currOpen'] = currOpen commStats['prevClose'] = prevClose commStats['posEntryPrice'] = posEntryPrice commStats['gapSize'] = ((currOpen - prevClose) / prevClose) if (self.absFlag): commStats['gapSize'] = np.abs(commStats['gapSize']) if (volType == 'stdVol'): commStats['volatility'] = np.std( retList[len(gapList[stock]) - volN:len(gapList[stock])]) else: commStats['volatility'] = np.std( gapList[stock][-volN:]) commStats['gapRatio'] = commStats['gapSize'] / commStats[ 'volatility'] #correct volatility using stDev of returns for 70 days of per minute returns commStats['stockVolatility'] = self.getVolatilityNDays( stock, volDays, i) if (self.hedgeFlag): if (stock != self.hedgeStock): # Binary search in the timeStamps of the benchmark row = bisect(timeListBM, currTime) - 1 retBM = retListBM[row] volBM = volListBM[row] bmI = bisect_left(benchmarkTimeStamps, currTime) posEntryBM = self.getVolAvgPrice( self.hedgeStock, bmI + self.currOpenVWAPWindow, bmI + self.currOpenVWAPWindow + self.posEntryVWAPWindow) #modifying volatility commStats[ 'indexVolatility'] = self.getVolatilityNDays( self.hedgeStock, volDays, bmI) if (debug): #Prints the timestamps of both the current stock row and the current benchmark row print( self.dataStore.priceDataList[stock][i][0], self.dataStore.priceDataList[ self.hedgeStock][bmI][0]) commStats['posEntryPriceBM'] = posEntryBM if (self.corrFlag != 'constant'): priceRow = bisect(priceTimeBM, currTime) # print len(priceList[stock][-volN:]) # print len(priceList[stock]) # print len(priceListBM) # print -volN + priceRow, priceRow # print len(priceList[self.hedgeStock][-volN + priceRow: priceRow]) self.betaCorrelation = np.corrcoef( priceList[stock][-volN:], priceListBM[-volN + priceRow:priceRow])[1][0] # self.betaCorrelation = self.getCorrelation(stock,self.hedgeStock,i,bmI,volDays) # beta = self.betaCorrelation * (volBM / commStats['volatility']) # beta = self.betaCorrelation * (commStats['volatility'] / volBM) beta = self.betaCorrelation * ( commStats['stockVolatility'] / commStats['indexVolatility']) if (debug): print("Stock Volatility: " + str(commStats['stockVolatility'])) print("Index Volatility: " + str(commStats['indexVolatility'])) commStats['betaCorr'] = self.betaCorrelation commStats['Beta'] = beta if (verbose): print(''.join(['*'] * 50)) print("Beta : " + str(beta)) print("Stock : " + stock) print("Stock currOpen: " + str(currOpen)) print("Stock prevClose: " + str(prevClose)) print("Stock Return: " + str(commStats['gapSize'])) print("Stock Volatility: " + str(commStats['volatility'])) print("Stock Normalized Return: " + str(commStats['gapRatio'])) print("Benchmark Return: " + str(retBM)) print("Benchmark Volatility: " + str(volBM)) print("Benchmark Normalized Return: " + str(retBM / volBM)) else: timeListBM.append(currTime) retListBM.append(commStats['gapSize']) volListBM.append(commStats['volatility']) minPriceList = [float(infoList[i][6])] maxPriceList = [float(infoList[i][6])] # Identifying the array index limit holdLim = min(max(holdPeriodList), len(infoList) - i - 1) for j in range(holdLim): minPriceList.append( min(minPriceList[-1], float(infoList[i + j][6]))) maxPriceList.append( max(maxPriceList[-1], float(infoList[i + j][6]))) #Appending volatility normalized gap value for determining distribution plot self.gapListNormalized.append(commStats['gapSize'] / commStats['volatility']) reachedStopOrTarget = 0 stopOrTargetRelReturn = 0 for hold in holdPeriodList: tmpStats = commStats.copy() minPrice = minPriceList[min(hold, holdLim)] maxPrice = maxPriceList[min(hold, holdLim)] tmpStats['holdPeriod'] = hold tmpStats['min'] = minPrice tmpStats['max'] = maxPrice tmpStats['finClose'] = infoList[min( (i + self.currOpenVWAPWindow + self.posEntryVWAPWindow + hold), len(infoList) - 1)][6] #Normalizing the volatility based on hold period tmpStats['stockVolAfterNorm'] = commStats[ 'stockVolatility'] * np.sqrt(hold) if (self.hedgeFlag): bmI = bisect_left(benchmarkTimeStamps, currTime) tmpStats['finCloseBM'] = self.dataStore.priceDataList[self.hedgeStock][min((bmI + self.currOpenVWAPWindow + self.posEntryVWAPWindow + hold)\ , len(self.dataStore.priceDataList[self.hedgeStock]) - 1)][6] # exitTime = infoList[i + hold][0] # bmI = bisect_left(benchmarkTimeStamps, exitTime) # tmpStats['finCloseBM'] = self.dataStore.priceDataList[self.hedgeStock][min((bmIExit), len(infoList) -1)][6] if (not (stock == self.hedgeStock)): #Calculating profits and all tmpStats['profit'] = ((- np.sign(tmpStats['currOpen'] - tmpStats['prevClose'])) * \ ((tmpStats['finClose'] - tmpStats['posEntryPrice']) / tmpStats['posEntryPrice'])) tmpStats['absReturn'] = tmpStats['profit'] tmpStats['absReturnPerUnitVol'] = tmpStats[ 'absReturn'] / tmpStats['stockVolAfterNorm'] if (self.hedgeFlag): tmpStats['marketReturn'] = ( (tmpStats['finCloseBM'] - tmpStats['posEntryPriceBM']) / tmpStats['posEntryPriceBM']) tmpStats['returnOffset'] = ( (tmpStats['finCloseBM'] - tmpStats['posEntryPriceBM']) / tmpStats['posEntryPriceBM'] ) * tmpStats['Beta'] tmpStats['relReturn'] = tmpStats['profit'] + ( np.sign(tmpStats['currOpen'] - tmpStats['prevClose']) * tmpStats['returnOffset']) tmpStats['relReturnPerUnitVol'] = tmpStats[ 'relReturn'] / tmpStats['stockVolAfterNorm'] if ((tmpStats['relReturn'] <= self.stopLoss or tmpStats['relReturn'] >= self.targetPrice) and reachedStopOrTarget == 0): reachedStopOrTarget = 1 stopOrTargetRelReturn = tmpStats[ 'relReturn'] if (reachedStopOrTarget): tmpStats[ 'relReturnWithStopLoss'] = stopOrTargetRelReturn else: tmpStats[ 'relReturnWithStopLoss'] = tmpStats[ 'relReturn'] else: tmpStats['relReturn'] = tmpStats['profit'] tmpStats['relReturnPerUnitVol'] = tmpStats[ 'absReturnPerUnitVol'] # tmpStats['profitDividedByVol'] = tmpStats['relReturn'] / tmpStats['stockVolAfterNorm'] if (self.printFlag == 1): for key in tmpStats: print(key + ": " + str(tmpStats[key])) statList[stock].append(tmpStats) if (not (stock == self.hedgeStock)): grandDict[stock].append(tmpStats) # grandDF.append(tmpStats) self.printFlag = 0 self.results = statList # print sorted([statList[key][x]['gapRatio'] for key in statList.keys() for x in range(len(statList[key]))])[-1000:-900] return statList def compileResults(self, holdPeriodList): ''' Compile the results extracted from getGapStats() The rows are indexed with RELATIVE RANK The columns are Count (For all stocks), also compute stock based results. E, P, R fraction. Win Rate: The fraction of actual fades Anti: Average profit on winning fade trades With: Average loss on losing fade trades Exp: Expectation of profit Args: Hold period list, should be consistent with getGapStats() Returns: Matrix with the following column convention 0: Count, 1: E, 2: P, 3: R, 4: P(S), 5: Anti, 6: With, 7:Exp ''' self.timeWiseStats = {} self.cumStats = {} for hold in holdPeriodList: # numStocks rows, column mapping is given above if self.mode == 'relative': numRows = len(self.stockList) elif self.mode == 'percentile': numRows = int(100 / self.bucketSize) else: numRows = (2 * self.numBucket) + 1 self.cumStats[hold] = np.zeros((numRows, 8)) self.timeWiseStats[hold] = {} if (self.logBucket): # Stores a list of timetamps for each bucket # Stores a list of self.bucketTimeList = [] self.bucketTradeList = [] tmpDict = {key: [] for key in self.stockList} for i in range(numRows): self.bucketTimeList.append(list()) self.bucketTradeList.append(tmpDict.copy()) for stockId in range(len(self.stockList)): stock = self.stockList[stockId] for i in range(len(self.results[stock])): tmpStats = self.results[stock][i] time = tmpStats['time'] hold = tmpStats['holdPeriod'] currOpen = tmpStats['currOpen'] prevClose = tmpStats['prevClose'] minPrice = tmpStats['min'] maxPrice = tmpStats['max'] finClose = tmpStats['finClose'] gapRatio = tmpStats['gapRatio'] gapSize = tmpStats['gapSize'] # volatility= tmpStats['volatility'] posEntry = tmpStats['posEntryPrice'] finClose = tmpStats['finClose'] volatility = tmpStats['stockVolAfterNorm'] #Hedging support, not technically hedging, just offsetting with respect to the index return if (self.hedgeFlag): hedge = ( (tmpStats['finCloseBM'] - tmpStats['posEntryPriceBM']) / tmpStats['posEntryPriceBM']) * tmpStats['Beta'] if (self.divideByVol): hedge /= volatility # Initial 8 elements represent the standard stats # The last ones will be used for ranking later tmpArr = np.zeros(12) tmpArr[0] += 1 tmpArr[8] = stockId tmpArr[9] = gapSize tmpArr[10] = gapRatio tmpArr[11] = stockId targetPrice = finClose profit = ((-np.sign(currOpen - prevClose)) * ((targetPrice - posEntry) / posEntry)) if (self.divideByVol): profit /= volatility if (self.hedgeFlag): profit -= (-np.sign(currOpen - prevClose)) * hedge fillFlag = np.sign(profit) if (fillFlag < 0): # Refers to the E case i.e. extension tmpArr[1] += 1 tmpArr[6] += profit else: if ((currOpen - prevClose) * (prevClose - targetPrice) < 0): # Refers to the P case i.e. partial fill tmpArr[2] += 1 else: # Refers to the R case i.e. reversal tmpArr[3] += 1 # Adding profits tmpArr[5] += profit # Adding the result to the corresponding time in the dict if (time not in self.timeWiseStats[hold]): self.timeWiseStats[hold][time] = [] self.timeWiseStats[hold][time].append(tmpArr) for hold in holdPeriodList: if self.mode == 'percentile': minSize = self.minSize maxSize = self.maxSize self.gapQueue = deque([], maxlen=maxSize) self.orderedGaps = SortedList(load=50) for time in sorted(self.timeWiseStats[hold].keys()): if (self.mode == 'relative'): # Sort the list according to the magnitude of gap size self.timeWiseStats[hold][time].sort( key=lambda x: np.abs(x[-1]), reverse=True) for i in range(len(self.timeWiseStats[hold][time])): self.cumStats[hold][i] += self.timeWiseStats[hold][ time][i][:8] elif (self.mode == 'percentile'): newGapLen = len(self.timeWiseStats[hold][time]) newValList = [] # If there are enough elements for identifying percentile if (len(self.gapQueue) >= minSize): for i in range(newGapLen): searchKey = self.timeWiseStats[hold][time][i][10] # if (self.absFlag): # searchKey = np.abs(searchKey) percentile = self.orderedGaps.bisect_left( searchKey) currSize = len(self.gapQueue) # To avoid having percentile as 1.0, since percentile <= percSize + 1 percentile = percentile / (currSize + 2.0) row = int(percentile * int(100 / self.bucketSize)) # print row self.cumStats[hold][row] += self.timeWiseStats[ hold][time][i][:8] if (self.tTestFlag): self.profitByGapPercentile[int( percentile * 100)].append( self.timeWiseStats[hold][time][i][5] + self.timeWiseStats[hold][time][i][6]) if (self.logBucket): # Adding time to this bucket's list self.bucketTimeList[row].append(time) # Since at least one of these is zero, by construction profit = self.timeWiseStats[hold][time][i][ 5] + self.timeWiseStats[hold][time][i][6] stockId = int( self.timeWiseStats[hold][time][i][11]) self.bucketTradeList[row][ self.stockList[stockId]].append(profit) bucketTradeListGlobal[row][ self.stockList[stockId]].append(profit) # Updating the queue and removing elements from the tree for i in range(newGapLen): lastVal = self.gapQueue.popleft() self.orderedGaps.remove(lastVal) for i in range(newGapLen): searchKey = self.timeWiseStats[hold][time][i][10] # if (self.absFlag): # searchKey = np.abs(searchKey) newValList.append(searchKey) # Adding the new values to the queue simultaneously self.gapQueue.extend(newValList) # Adding the new values to the tree simultaneously self.orderedGaps.update(newValList) else: for i in range(len(self.timeWiseStats[hold][time])): # Sort the list according to the magnitude of gap size gapRatio = self.timeWiseStats[hold][time][i][10] # Get the position in the matrix, note that the bucket sizes are of size 10% bucket = int( np.sign(gapRatio) * int(np.abs(gapRatio * 10) / self.bucketSize)) bucket = int( np.sign(bucket) * self.numBucket ) if np.abs(bucket) >= self.numBucket else bucket row = self.numBucket + bucket self.cumStats[hold][row] += self.timeWiseStats[hold][ time][i][:8] def tTestWrapper(self, profitByGapPercentile, verbose=True): """ Tries various boundary values and gets the stats for each value from 1..99 as the boundary for percentile and Perfroms T Test on the profits >=value and <=value arrays """ print(''.join(['*'] * 50)) print("Cumulative Stats") if (self.tTestFlag): for i in range(10, 100, 10): tValue, pValue = self.getTTestScores(i, profitByGapPercentile) if (verbose): print("Boundary: " + str(i)) print("T Value: " + str(tValue)) print("P Value: " + str(pValue)) def getProfitGapPercentile(self): return self.profitByGapPercentile def finalizeStats(self, holdPeriodList): ''' Finally processes the stats matrices, note that the resulting matrices cannot be compiled again directly as frequencies have become probs ''' for hold in holdPeriodList: self.cumStats[hold] = processStatMatrix(self.cumStats[hold]) def plotDistribution(self, plotSeries, saveAsFile=False, logValues=False): ''' Plots a histogram for the given plotsSeries Args: saveAsFile: Whether to save to file or plotting on screen logValues: Whether the y axis is log scaled Return: None, side effects could include saving a file ''' stDev = np.std(plotSeries) #xLabels from -3*sigma to 3*sigma xLabels = np.array(range(-3, 4)) * stDev plt.figure(figsize=(100, 100)) fig, ax = plt.subplots(1, 1) axes = plt.gca() plt.hist(plotSeries, bins=100, log=logValues) plt.xlabel("Normalized Gap Size") plt.ylabel("Number of Gap Sizes") axes.set_xlim([xLabels[0] - 0.5, xLabels[-1] + 0.5]) ax.set_xticks(xLabels) plt.tight_layout() if (saveAsFile): plt.savefig("results/gapDistribution.svg") else: plt.show()
ann_id = 0 for gen_id in tqdm(range(TOT_GEN)): remain = 2048 current_height = 0 noceil = 0 seg_id = random.randint(0,len(top_segs)-1) top_segment = top_segs[seg_id] # print(top_segment) remain -= top_segment['height'] file_name = "top" + str(seg_id) top_frq[seg_id] += 1 bot_segment = None if remain >= bot_segs[0]['height']: idx = min( bot_segs.bisect_left({'height': remain}) ,len(bot_segs)-1) seg_id = random.randint(0, idx) bot_segment = bot_segs[seg_id] remain -= bot_segment['height'] file_name += "-bot" + str(seg_id) bot_frq[seg_id] += 1 # print(bot_segment) gen_image = Image.new("L", (1447, 2048), color=255) annotation, height = cut_image(gen_image, top_segment, gen_id, ann_id, current_height) annotations.extend(annotation) current_height += math.ceil(height) # noceil+=height ann_id += len(annotation)
class ListWithAdjustments(object): """ To prepare inserts, we adjust elements to be inserted and elements in the underlying list. We don't want to actually touch the underlying list, but we need to remember the adjustments, because later adjustments may depend on and readjust earlier ones. """ def __init__(self, orig_list): """ Orig_list must be a a SortedListWithKey. """ self._orig_list = orig_list self._key = orig_list._key # Stores pairs (i, new_key) where i is an index into orig_list. # Note that adjustments don't affect the order in the original list, so the list is sorted # both on keys an on indices; and a missing index i means that (i, orig_key) fits into the # adjustments list both by key and by index. self._adjustments = SortedListWithKey(key=lambda pair: pair[1]) # Stores keys for new insertions. self._insertions = SortedList() def get_insertions(self): return self._insertions def get_adjustments(self): return self._adjustments def _adj_bisect_key_left(self, key): """ Works as bisect_key_left(key) on the orig_list as if all adjustments have been applied. """ adj_index = self._adjustments.bisect_key_left(key) adj_next = (self._adjustments[adj_index][0] if adj_index < len(self._adjustments) else len( self._orig_list)) adj_prev = self._adjustments[adj_index - 1][0] if adj_index > 0 else -1 orig_index = self._orig_list.bisect_key_left(key) if adj_prev < orig_index and orig_index < adj_next: return orig_index return adj_next def _adj_get_key(self, index): """ Returns the key corresponding to the given index into orig_list as if all adjustments have been applied. """ i = bisect.bisect_left(self._adjustments, (index, float('-inf'))) if i < len(self._adjustments) and self._adjustments[i][0] == index: return self._adjustments[i][1] return self._key(self._orig_list[index]) def count_range(self, begin, end): """ Returns the number of elements with keys in the half-open interval [begin, end). """ adj_begin = self._adj_bisect_key_left(begin) adj_end = self._adj_bisect_key_left(end) ins_begin = self._insertions.bisect_left(begin) ins_end = self._insertions.bisect_left(end) return (adj_end - adj_begin) + (ins_end - ins_begin) def _adjust_range(self, begin, end): """ Make changes to stored adjustments and insertions to distribute them equally in the half-open interval of keys [begin, end). """ adj_begin = self._adj_bisect_key_left(begin) adj_end = self._adj_bisect_key_left(end) ins_begin = self._insertions.bisect_left(begin) ins_end = self._insertions.bisect_left(end) self._do_adjust_range(adj_begin, adj_end, ins_begin, ins_end, begin, end) def _adjust_all(self): """ Renumber everything to be equally distributed in the open interval (new_begin, new_end). """ orig_len = len(self._orig_list) ins_len = len(self._insertions) self._do_adjust_range(0, orig_len, 0, ins_len, 0.0, orig_len + ins_len + 1.0) def _do_adjust_range(self, adj_begin, adj_end, ins_begin, ins_end, new_begin_key, new_end_key): """ Implements renumbering as used by _adjust_range() and _adjust_all(). """ count = (adj_end - adj_begin) + (ins_end - ins_begin) prev_keys = ([(self._adj_get_key(i), False, i) for i in xrange(adj_begin, adj_end)] + [(self._insertions[i], True, i) for i in xrange(ins_begin, ins_end)]) prev_keys.sort() new_keys = get_range(new_begin_key, new_end_key, count) for (old_key, is_insert, i), new_key in zip(prev_keys, new_keys): if is_insert: self._insertions.remove(old_key) self._insertions.add(new_key) else: # (i, old_key) pair may not be among _adjustments, so we discard() rather than remove(). self._adjustments.discard((i, old_key)) self._adjustments.add((i, new_key)) def prep_inserts_at_index(self, index, count): # This is the crux of the algorithm, inspired by the [Bender] paper (cited above). # Here's a brief summary of the algorithm, and of our departures from it. # - The algorithm inserts keys while it is able. When there isn't enough space, it walks # enclosing intervals around the key it wants to insert, doubling the interval each time, # until it finds an interval that doesn't overflow. The overflow threshold is calculated in # such a way that the bigger the interval, the smaller the density it seeks. # - The algorithm uses integers, picking the number of bits to work for list length between # n/2 and 2n, and rebuilding from scratch any time length moves out of this range. We don't # rebuild anything, don't change number of bits, and use floats. This breaks some of the # theoretical results, and thinking about floats is much harder than about integers. So we # are not on particularly solid ground with these changes (but it seems to work). # - We try different thresholds, which seems to perform better. This is mentioned in "Variable # T" section of [Bender] paper, but our approach isn't quite the same. So it's also on shaky # theoretical ground. assert count > 0 begin = self._adj_get_key(index - 1) if index > 0 else 0.0 end = self._adj_get_key(index) if index < len( self._orig_list) else begin + count + 1 if begin < 0 or end <= 0 or math.isinf(max(begin, end)): # This should only happen if we have some invalid positions (e.g. from before we started # using this logic). In this case, just renumber everything 1 through n (leaving space so # that the count insertions take the first count integers). self._insertions.update([begin if index > 0 else float('-inf')] * count) self._adjust_all() return self._insertions.update(get_range(begin, end, count)) if not is_valid_range(begin, self._insertions.irange(begin, end), end): assert self.count_range(begin, end) > 0 min_key, max_key = self._find_sparse_enough_range(begin, end) self._adjust_range(min_key, max_key) assert is_valid_range(begin, self._insertions.irange(begin, end), end) def _find_sparse_enough_range(self, begin, end): # frac is a parameter used for relabeling, corresponding to 2/T in [Bender]. Its # interpretation is that frac^i is the overflow limit for intervals of size 2^i. for frac in (1.14, 1.3): thresh = 1 for i in xrange(64): rbegin, rend = range_around_float(begin, i) assert self.count_range(rbegin, rend) > 0 if end <= rend and self.count_range(rbegin, rend) < thresh: return (rbegin, rend) thresh *= frac raise ValueError("This isn't expected")
class ManagerNode(object): def __init__(self, node, parent, position): """ :param node: the behavior that is represented by this ManagerNode :type node: py_trees.behaviour.Behaviour :param parent: the parent of the behavior that is represented by this ManagerNode :type parent: py_trees.behaviour.Behaviour :param position: the position of the node in the list of children of the parent :type position: int """ self.node = node self.parent = parent self.position = position self.disabled_children = SortedList() self.enabled_children = SortedList() def __lt__(self, other): return self.position < other.position def __gt__(self, other): return self.position > other.position def __eq__(self, other): return self.node == other.node and self.parent == other.parent def disable_child(self, manager_node): """ marks the given manager node as disabled in the internal tree representation and removes it to the behavior tree :param manager_node: :type manager_node: TreeManager.ManagerNode :return: """ self.enabled_children.remove(manager_node) self.disabled_children.add(manager_node) if isinstance(self.node, PluginBehavior): self.node.remove_plugin(manager_node.node.name) else: self.node.remove_child(manager_node.node) def enable_child(self, manager_node): """ marks the given manager node as enabled in the internal tree representation and adds it to the behavior tree :param manager_node: :type manager_node: TreeManager.ManagerNode :return: """ self.disabled_children.remove(manager_node) self.enabled_children.add(manager_node) if isinstance(self.node, PluginBehavior): self.node.add_plugin(manager_node.node) else: idx = self.enabled_children.index(manager_node) self.node.insert_child(manager_node.node, idx) def add_child(self, manager_node): """ adds the given manager node to the internal tree map and the corresponding behavior to the behavior tree :param manager_node: :type manager_node: TreeManager.ManagerNode :return: """ if isinstance(self.node, PluginBehavior): self.enabled_children.add(manager_node) self.node.add_plugin(manager_node.node) else: if manager_node.position < 0: manager_node.position = 0 if self.enabled_children: manager_node.position = max( manager_node.position, self.enabled_children[-1].position + 1) if self.disabled_children: manager_node.position = max( manager_node.position, self.disabled_children[-1].position + 1) idx = manager_node.position else: idx = self.disabled_children.bisect_left(manager_node) for c in self.disabled_children.islice(start=idx): c.position += 1 idx = self.enabled_children.bisect_left(manager_node) for c in self.enabled_children.islice(start=idx): c.position += 1 self.node.insert_child(manager_node.node, idx) self.enabled_children.add(manager_node) def remove_child(self, manager_node): """ removes the given manager_node from the internal tree map and the corresponding behavior from the behavior tree :param manager_node: :type manager_node: TreeManager.ManagerNode :return: """ if isinstance(self.node, PluginBehavior): if manager_node in self.enabled_children: self.enabled_children.remove(manager_node) self.node.remove_plugin(manager_node.node.name) elif manager_node in self.disabled_children: self.disabled_children.remove(manager_node) else: raise RuntimeError( 'could not remove node from parent. this probably means that the tree is inconsistent' ) else: if manager_node in self.enabled_children: self.enabled_children.remove(manager_node) self.node.remove_child(manager_node.node) elif manager_node in self.disabled_children: self.disabled_children.remove(manager_node) else: raise RuntimeError( 'could not remove node. this probably means that the tree is inconsistent' ) idx = self.disabled_children.bisect_right(manager_node) for c in self.disabled_children.islice(start=idx): c.position -= 1 idx = self.enabled_children.bisect_right(manager_node) for c in self.enabled_children.islice(start=idx): c.position -= 1
# 1944. Number of Visible People in a Queue # https://leetcode.com/problems/number-of-visible-people-in-a-queue/ from sortedcontainers import SortedList class Solution: def canSeePersonsCount(self, heights: List[int]) -> List[int]: n = len(heights) res = [1] * (n - 1) + [0] sl = SortedList() mp = collections.defaultdict(int) for i, h in list(enumerate(heights))[::-1]: index = sl.bisect_left(h) if sl: res[i] = max(1, min(len(sl), index + 1)) if i + 1 < n and h > heights[i + 1]: for _ in range(index): sl.pop(0) sl.add(h) mp[h] = i return res
class MKAverage(object): def __init__(self, m, k): """ :type m: int :type k: int """ self.m, self.k, self.sum = m, k, 0 self.lower, self.middle, self.upper = SortedList(), SortedList( ), SortedList() self.nums = collections.deque() def shiftLeft(self, l_list, r_list): l_list.add(r_list.pop(0)) def shiftRight(self, l_list, r_list): r_list.add(l_list.pop()) def addElement(self, num): """ :type num: int :rtype: None """ self.nums.append(num) if self.lower and self.lower[-1] >= num: self.lower.add(num) elif self.upper and self.upper[0] <= num: self.upper.add(num) else: self.middle.add(num) self.sum += num while len(self.lower) > self.k: self.sum += self.lower[-1] self.shiftRight(self.lower, self.middle) while len(self.upper) > self.k: self.sum += self.upper[0] self.shiftLeft(self.middle, self.upper) if len(self.nums) > self.m: d = self.nums.popleft() if self.lower.bisect_left(d) < self.k: self.lower.remove(d) elif self.middle.bisect_left(d) < len(self.middle): self.middle.remove(d) self.sum -= d else: self.upper.remove(d) if len(self.nums) >= self.m: while len(self.lower) < self.k: self.sum -= self.middle[0] self.shiftLeft(self.lower, self.middle) while len(self.upper) < self.k: self.sum -= self.middle[-1] self.shiftRight(self.middle, self.upper) def calculateMKAverage(self): """ :rtype: int """ if len(self.nums) < self.m: return -1 return self.sum // (self.m - self.k * 2)
maxSize = min(rows, cols) prefix = [[0] * (cols + 1) for _ in range(rows + 1)] for i in range(rows): for j in range(cols): prefix[i + 1][j + 1] += prefix[i + 1][j] + matrix[i][j] for i in range(rows): for j in range(cols): prefix[i + 1][j + 1] += prefix[i][j + 1] res = float('-inf') for row1 in range(rows): for row2 in range(row1, rows): sl = SortedList() sl.add(0) for j in range(cols): curr = prefix[row2 + 1][j + 1] - prefix[row1][j + 1] target = curr - k index = sl.bisect_left(target) if 0 <= index < len(sl): res = max(res, curr - sl[index]) sl.add(curr) return res
class MKAverage: ''' use sorted list it is a data structure to add and remove at O(logN) time and can get min and max in an efficient way ''' def __init__(self, m: int, k: int): self.m = m self.k = k self.nums = deque() self.sorted_nums = SortedList() self.total = self.topk_big = self.topk_small = 0 def addElement(self, num: int) -> None: # handle new number num self.total += num self.nums.append(num) idx = self.sorted_nums.bisect_left(num) # handle topksmall if idx < self.k: self.topk_small += num if len(self.sorted_nums) >= self.k: self.topk_small -= self.sorted_nums[self.k - 1] # handle topkbig if idx >= len(self.sorted_nums) + 1 - self.k: self.topk_big += num if len(self.sorted_nums) >= self.k: self.topk_big -= self.sorted_nums[-self.k] self.sorted_nums.add(num) # handle the num that needs to be removed from size m sliding window if len(self.nums) > self.m: num_to_remove = self.nums.popleft() self.total -= num_to_remove idx = self.sorted_nums.index(num_to_remove) if idx < self.k: self.topk_small -= num_to_remove self.topk_small += self.sorted_nums[self.k] if idx >= len(self.sorted_nums) - self.k: self.topk_big -= num_to_remove self.topk_big += self.sorted_nums[-self.k - 1] self.sorted_nums.remove(num_to_remove) def calculateMKAverage(self) -> int: if len(self.nums) < self.m: return -1 return (self.total - self.topk_small - self.topk_big) // (self.m - (2 * self.k)) # Your MKAverage object will be instantiated and called as such: # obj = MKAverage(m, k) # obj.addElement(num) # param_2 = obj.calculateMKAverage()
# 2179. Count Good Triplets in an Array # https://leetcode.com/problems/count-good-triplets-in-an-array from sortedcontainers import SortedList class Solution: def goodTriplets(self, nums1: List[int], nums2: List[int]) -> int: n = len(nums1) pos = {x:i for i,x in enumerate(nums2)} sl = SortedList([pos[nums1[0]]]) prefix = [0] for i in range(1, n): sl.add(pos[nums1[i]]) prefix.append(sl.bisect_left(pos[nums1[i]])) sl = SortedList([pos[nums1[-1]]]) suffix = [0] for i in range(n - 2, -1, -1): index = sl.bisect_left(pos[nums1[i]]) suffix.append(len(sl) - index) sl.add(pos[nums1[i]]) suffix.reverse() res = 0 for a, b in zip(prefix, suffix): res += a * b return res
# 1944. Number of Visible People in a Queue # https://leetcode.com/problems/number-of-visible-people-in-a-queue from sortedcontainers import SortedList class Solution: def canSeePersonsCount(self, heights: List[int]) -> List[int]: heights.reverse() res = [] sl = SortedList() for h in heights: res.append(min(len(sl), sl.bisect_left(h) + 1)) while sl and h > sl[0]: sl.pop(0) sl.add(h) return res[::-1]
class AutoComplete(wx.Frame): def __init__(self, parent, mainFrame, dictfile=None): self.geekey = mainFrame wx.Frame.__init__(self, parent, wx.NewId(), "Popup", style=wx.STAY_ON_TOP | wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.FRAME_TOOL_WINDOW) self.MaxNum = 9 self.ShowBitNum = 3 self.fontsize = 12 ################### font = wx.Font(self.fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL) self.lists = wx.ListBox(self, ) self.lists.SetFont(font) self.lists.Bind(wx.EVT_LISTBOX, self.OnLists) ################### self.min_width = 100 self.max_width = 1666 # change to the longest candidates length self.item_num = self.MaxNum #self.char_width,self.char_height = (8,16) self.char_width, self.char_height = self.lists.GetTextExtent('O') self.switch_force_on = False self.auto_complete_on = False #log.log 'char width,height =',self.char_width,self.char_height ################### read word list in from file self.word_list = SortedList() self.StateReset() ################### #get self hw self.ui = GUITHREADINFO(cbSize=sizeof(GUITHREADINFO)) self.hw = self.GetTopWindow() #log.log("current hw is %s"%self.hw) self.Show(False) self.Raise() self.Move((-200, -200)) self.popup_hw = self.GetTopWindow() win32gui.SetForegroundWindow(self.hw) #self.ui = None #################### #load dict self.dictfile = dictfile if os.path.exists(dictfile): f = open(dictfile, 'r') cont = f.read() f.close() for row in cont.split('\n'): items = row.split() if len(items) == 0: continue word = items[0] freq = 1 if len(items) > 1: freq = toFloat(items[1], freq) self.UpdateWord(word, freq * 0.9) pass pass #### self.height = 0 self.width = 0 self.start_pos = (0, 0) pass def FindWord(self, word): ind = self.word_list.bisect_left([word.lower(), word, -1]) if ind >= len(self.word_list): return False, ind if self.word_list[ind][1] != word: return False, ind return True, ind def FindSection(self, prefix): left_ind = self.word_list.bisect_left( [prefix.lower(), prefix.upper(), -1]) right_ind = self.word_list.bisect_right( [prefix.lower() + "~", prefix.lower() + "~", -1]) return (left_ind, right_ind) def UpdateWord(self, word, freq=1): if freq <= 0: return res, ind = self.FindWord(word) #log.log("update list of len %s with word %s"%(len(self.word_list),self.word) ) if res: self.word_list[ind][2] += freq else: self.word_list.add([word.lower(), word, freq]) pass pass def DeleteWord(self, word): res, ind = self.FindWord(word) if res: del self.word_list[ind] pass pass def PopupActive(self): return (self.GetTopWindow() == self.popup_hw) def GetInput(self, evtType, key, geekey, shift, ctrl, alt): ### if self.geekey.getConfig('geekeyenabled') == 'False': return True if not self.auto_complete_on: return True #log.log("get into get input") self.state_on_geekey = geekey self.state_on_shift = shift self.state_on_ctrl = ctrl self.state_on_alt = alt is_active = (self.GetTopWindow() == self.popup_hw) # # esc to cancel candinate selection # if is_active and key == 'esc': # if is_active: # log.log('cancel auto complete by esc') # self.StateReset() # return False if key == 'tab': #log.log("tab here in tab dealing") #log.log("prepare to deal with tab without geekey state_is_on = %s"%self.state_is_on) if self.state_is_on: #candidate is shown on the screen if evtType == 'key up': return False #log.log("is active = %s wtop = %s wpop = %s"%(is_active,self.GetTopWindow(),self.popup_hw ) ) if is_active: #the same as self.state_is_selection #pop up current selection #log.log("the popup window is focused.") select = self.lists.GetSelection() select_word = self.lists.GetString(select) win32gui.SetForegroundWindow(self.hw) log.log("update candidate %s with %s" % (select_word, self.word)) self.UpdateTabCandidate(select_word, self.word) self.StateReset() elif self.state_is_selection: #on screen current selection pass elif self.state_is_tab_selection: #move to next candidate in tab selection mode #log.log("already in tab selection mode, move to the the next one candidate") direction = 'down' if not shift else 'up' word = self.MoveCandidateSelection(direction) #log.log("update candidate %s with %s"%(self.word,word ) ) self.UpdateTabCandidate(word, self.word) self.word = word pass else: #still in input mode, enter the tab selection mode #log.log("start the tab selection mode") #choose the first one to be on screen self.state_is_tab_selection = True select = self.lists.GetSelection() reset = True if select == wx.NOT_FOUND: self.lists.SetSelection(0) select = 0 reset = False pass new_word = self.lists.GetString(select) #log.log("update candidate %s on %s"%(new_word,self.word) ) self.UpdateTabCandidate(new_word, self.word) self.word = new_word if reset or self.item_num == 1: self.StateReset() pass #log.log("get out tab when is selection") return False else: pass # normal tab, do nothing, and return True to pass the tab by #log.log("get out normal tab") return True if key == 'Packet': return True if key == '': return True if self.state_is_on and key == 'return': if evtType == 'key up': return False select = self.lists.GetSelection() if select < 0: self.StateReset() return True select_word = self.lists.GetString(select) self.UpdateTabCandidate(select_word, self.word) self.StateReset() return False if self.state_is_on and key == 'delete': if evtType == 'key up': return False select = self.lists.GetSelection() select_word = self.lists.GetString(select) if select_word != '': self.DeleteWord(select_word) self.StateReset() return False if key in ('up', 'down'): if self.state_is_on: ##get into selection mode if evtType == 'key up': return False #log.log("get in up down") self.MoveCandidateSelection(key) self.state_is_tab_selection = False #self.StateReset() #log.log("get out up down") return False #else return True if key in ('page up', 'page down'): if self.state_is_on: ##get into selection mode if evtType == 'key up': return False #log.log("get in page up down") incr = self.item_num if key == 'page down' else -self.item_num self.MoveCandidateSelection(incr) #self.StateReset() self.state_is_tab_selection = False #log.log("get out page up down") return False #else return True if is_active: return False if evtType == 'key up': #log.log("get out key up") return True pos = self.GetCaretPosition() #log.log(pos) old_pos = self.last_pos self.last_pos = pos #log.log('pos = %s old_pos = %s'%(pos,old_pos) ) if (old_pos and (pos[0] - old_pos[0] < -8 or pos[1] - old_pos[1] > 16 or pos[1] - old_pos[1] < -16)): #log.log("position condition not meet. StateReset") self.StateReset() return True #log.log 'with key = %s'%(key) ### combo key with ctrl or alt encoutered, rest if ctrl or alt or not key in StringKeys: #log.log("non-string key encountered") if key == 'backspace': if len(self.section_list) >= 1: del self.section_list[-1] if self.word: self.word = self.word[:-1] self.ShowSelection() #log.log("get out up backspace") return True else: #record the word to word_list #log.log("try update %s to word_list %s"%(self.word,self.word_list) ) #log.log len(self.word), self.ShowBitNum #remove last SpecKeys while len(self.word) > 1 and self.word[-1] in SpecKeys: self.word = self.word[:-1] if len(self.word) >= self.ShowBitNum + 2 and min( self.word) != max(self.word): #log.log("update word %s"%self.word) #check if word is consist of a single char ignore self.UpdateWord(self.word) #log.log("after word_list = %s"%(self.word_list) ) pass else: #log.log("word content condition not meet to updateword") #log.log("word = %s min %s max %s"%(self.word,min(self.word),max(self.word))) pass self.StateReset() #log.log("get out up backspace") return True pass ### normal char dealing #log.log("normal char dealing key = %s shift = %s"%(key,shift) ) ### add key to word if shift: self.word += (UpperKeys[key] if key in UpperKeys else key.upper()) else: self.word += key #log.log("word expanded to %s"%self.word) ### if len(self.word) > 2 and self.word[0] == self.word[1]: self.StateReset() return True if not self.auto_complete_on: return True #log.log self.word wordlen = len(self.word) if wordlen == 1: self.start_pos = pos return True if wordlen < self.ShowBitNum: return True if wordlen == self.ShowBitNum: ### show selection window at starting pos #candidate will be at least MaxNum+2 bits lind, rind = self.FindSection(self.word) self.section_list.append( (lind, rind)) ## may append (0,0) if not find self.ShowSelection(True) return True if wordlen > self.ShowBitNum: ### update selection window lind, rind = self.FindSection(self.word) self.section_list.append((lind, rind)) self.ShowSelection() return True ### won't come here return True def ShowSelection(self, initshow=False): lind, rind = (0, 0) if len(self.section_list) > 0: lind, rind = self.section_list[-1] #log.log lind,rind if lind == rind: self.state_is_on = False self.state_no_candidate = True self.Show(False) return False #log.log "section = %s lind = %s rind = %s"%(self.section_list,lind,rind) #log.log "section list now ",self.word_list[ lind:rind ] #find out the max width self.state_is_on = True self.width = self.min_width self.width = min( self.max_width, max( list( map(lambda x: len(x[1]) * self.char_width, self.word_list[lind:rind]))) + 40) self.item_num = min(rind - lind, self.MaxNum) #log.log 'show list with item_num = ',self.item_num self.old_height = self.height self.height = (self.char_height) * self.item_num + 6 self.SetSize((self.width, self.height)) self.Move((self.start_pos[0], self.start_pos[1] - self.height)) word_list = list(map(lambda x: x[1], self.word_list[lind:rind])) self.lists.Set(word_list) self.Show(True) win32gui.SetForegroundWindow(self.hw) pass def HideSelection(self): self.Show(False) def StateReset(self): self.Show(False) self.section_list = [] self.word = '' self.start_pos = None self.state_is_on = False self.state_is_selection = False self.state_is_tab_selection = False self.state_no_candidate = False self.tab_selection_word = '' self.last_pos = None pass def UpdateTabCandidate(self, new_word, old_word): #log.log("get into update cadidate") for i in range(len(old_word)): geeKeyboard.keyPress('backspace') geeKeyboard.keyRelease('backspace') pass for ch in new_word: upper = False if ch in LowerKeys: upper = True ch = LowerKeys[ch] elif ch.isupper(): upper = True ch = ch.lower() pass #log.log("deal char %s with upper = %s and shift = %s"%(ch,upper,self.state_on_shift) ) if self.state_on_shift: shift_key = 'left shift' if keyboard.is_pressed( 'left shift') else 'right shift' if upper: #log.log"case 1" #shift is on and a upper char geeKeyboard.keyPress(ch) geeKeyboard.keyRelease(ch) else: #log.log"case 2" #not upper char, but the shift key is on geeKeyboard.keyRelease(shift_key) geeKeyboard.keyPress(ch) geeKeyboard.keyRelease(ch) geeKeyboard.keyPress(shift_key) pass pass else: # not on shift if upper: #log.log"case 3" #not on shift and A upper char geeKeyboard.keyPress('left shift') geeKeyboard.keyPress(ch) geeKeyboard.keyRelease(ch) geeKeyboard.keyRelease('left shift') else: #log.log"case 4" #not shift and not upper geeKeyboard.keyPress(ch) geeKeyboard.keyRelease(ch) pass pass pass #log.log("get out of update cadidate") pass def MoveCandidateSelection(self, direction='down'): if direction == 'down': incr = 1 elif direction == 'up': incr = -1 else: incr = direction new_item = self.lists.GetSelection() + incr if new_item < 0: new_item = 0 elif new_item >= self.lists.GetCount(): new_item = self.lists.GetCount() - 1 else: pass word = self.lists.GetString(new_item) self.lists.SetSelection(new_item) self.lists.EnsureVisible(new_item) return word def GetTopWindow(self): fhwnd = win32gui.GetFocus() user32.GetGUIThreadInfo(0, byref(self.ui)) return self.ui.hwndFocus def GetCaretPosition(self): fhwnd = win32gui.GetFocus() user32.GetGUIThreadInfo(None, byref(self.ui)) # None/0 for foreground thread if self.ui.hwndFocus != self.popup_hw: self.hw = self.ui.hwndFocus return win32gui.ClientToScreen( self.ui.hwndFocus, (self.ui.rcCaret.left, self.ui.rcCaret.top)) def OnLists(self, evt): #log.log("selection = %s "%(self.lists.GetSelection() ) ) pass def Destroy(self): ### save lists to dictfile cont = '' for item in self.word_list: if item[2] <= 0: continue cont += "%s %s\n" % (item[1], item[2]) pass f = open(self.dictfile, 'w') f.write(cont) f.close() super(AutoComplete, self).Destroy() pass def SwitchState(self, state=None): #log.log("geekey+tab to switch the auto complete state") if not state: self.auto_complete_on = not self.auto_complete_on else: self.auto_complete_on = True if state in ('True', 'on') else False if not self.auto_complete_on: #turn off self.StateReset() self.geekey.vim.indicator.StateReset(lt('_autocomplete'), lt('_autocomplete_off')) pass else: #turn on self.geekey.vim.indicator.StateReset(lt('_autocomplete'), lt('_autocomplete_on')) pass pass pass
from sortedcontainers import SortedList class Solution: def minimumDifference(self, nums: List[int]) -> int: m = len(nums) n = m // 3 left = SortedList(nums[:n]) right = SortedList(nums[n:]) sumLeft = sum(left) sumRight = sum(right[-n:]) res = sumLeft - sumRight for i in range(n, 2 * n): index = right.bisect_left(nums[i]) if index >= len(right) - n: sumRight -= nums[i] sumRight += right[-n-1] index = left.bisect_left(nums[i]) if index < n: sumLeft += nums[i] sumLeft -= left[n - 1] right.discard(nums[i]) left.add(nums[i]) res = min(res, sumLeft - sumRight)
class ExamRoom: def __init__(self, n: int): self.treemap1 = SortedList( ) # 每个元素为(区间大小,start,end),因为区间大小为偶数时与减少一时相同,所以要减少1 self.treemap2 = SortedList() # 每个元素为(start,end),主要目的是在leave时,定位p所在的区间 self.n = n self.treemap1.add((self.distance(0, n - 1), 0, n - 1)) self.treemap2.add((0, n - 1)) def distance(self, start, end): d = end - start + 1 if d % 2 == 0: d -= 1 return d def seat(self) -> int: r = self.treemap1.pop() # 弹出最大的区间 size, start, end = r start = -start self.treemap2.remove((start, end)) if start == 0: # 左边界 p = 0 if end >= 1: rr = (self.distance(1, end), -1, end) self.treemap1.add(rr) self.treemap2.add((1, end)) elif end == self.n - 1: # 右边界 p = self.n - 1 if p - 1 >= start: lr = (self.distance(start, p - 1), -start, p - 1) self.treemap1.add(lr) self.treemap2.add((start, p - 1)) else: # 选中了中间的区间,区间可以拆分成2个 p = (start + end) // 2 if p > start: lr = (self.distance(start, p - 1), -start, p - 1) self.treemap1.add(lr) self.treemap2.add((start, p - 1)) if p < end: rr = (self.distance(p + 1, end), -(p + 1), end) self.treemap1.add(rr) self.treemap2.add((p + 1, end)) return p def leave(self, p: int) -> None: midRange = (p, p) idx = self.treemap2.bisect_left(midRange) if idx > 0: leftRange = self.treemap2[idx - 1] if leftRange[1] == p - 1: # 左侧区间与p相邻,进行合并 self.treemap2.remove(leftRange) self.treemap1.remove( (self.distance(leftRange[0], leftRange[1]), -leftRange[0], leftRange[1])) midRange = (leftRange[0], p) idx -= 1 if idx < len(self.treemap2): rightRange = self.treemap2[idx] if rightRange[0] == p + 1: # 右侧区间与p相邻,进行合并 self.treemap2.remove(rightRange) self.treemap1.remove((self.distance(rightRange[0], rightRange[1]), -rightRange[0], rightRange[1])) midRange = (midRange[0], rightRange[1]) self.treemap2.add(midRange) self.treemap1.add( (self.distance(midRange[0], midRange[1]), -midRange[0], midRange[1]))
from sortedcontainers import SortedList class Solution: def totalSteps(self, nums: List[int]) -> int: nums = [float('-inf')] + nums + [float('inf')] res = 0 sl = SortedList([(i, x) for i, x in enumerate(nums)]) p = set() for i, (a, b) in enumerate(zip(nums, nums[1:])): if a > b: p.add((i + 1, b)) while p: newP = set() res += 1 for j, b in p: index = sl.bisect_left((j, b)) i, a = sl[index - 1] k, c = sl[index + 1] del sl[index] if a > c and (k, c) not in p: newP.add((k, c)) p = newP return res
'''
# 2012. Sum of Beauty in the Array # https://leetcode.com/problems/sum-of-beauty-in-the-array/ from sortedcontainers import SortedList class Solution: def sumOfBeauties(self, nums: List[int]) -> int: n = len(nums) left = SortedList() left.add(nums[0]) right = SortedList() for i in range(2, n): right.add(nums[i]) res = 0 for i in range(1, n - 1): left_index = left.bisect(nums[i]) right_index = right.bisect_left(nums[i]) if left_index == len(left) and left[left_index - 1] < nums[i] and right_index == 0 and right[0] > nums[i]: res += 2 elif nums[i - 1] < nums[i] < nums[i + 1]: res += 1 left.add(nums[i]) right.remove(nums[i + 1]) return res