Skip to content

Commit

Permalink
Commented and simplified the binary search section of the naming rout…
Browse files Browse the repository at this point in the history
…ine.
  • Loading branch information
Dave Vandenbout committed Jul 18, 2019
1 parent e82c984 commit a84d455
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 19 deletions.
44 changes: 25 additions & 19 deletions skidl/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,51 +345,57 @@ def get_unique_name(lst, attrib, prefix, initial=None):
A string containing the unique name.
"""

name = initial

# Get the unique names used in the list.
unique_names = set([getattr(l,attrib,None) for l in lst])

# If the initial name is None, then create a name based on the prefix
# and the smallest unused number that's available for that prefix.
if not initial:
if not name:

n = 1
# Do a binary search for a unique name formed from the prefix + number.
n = 1 # Starting number to append to the prefix.
while True:
name = prefix + str(n)
# Step forward in larger and larger increments looking for a name
# that isn't in the list.
step = 1
while name in unique_names:
while prefix + str(n) in unique_names:
n += step
step *= 2
name = prefix + str(n)
# If the step is 1, then the first name tried was available, so take it.
# If the step is two, the next sequential name after the first name tried
# was available, so take that.
if step in (1, 2):
name = prefix + str(n)
break
while (name not in unique_names) and (step > 1):
# For larger step sizes, there may be non-existent names preceding
# the current value of n. So search backward starting with a large step
# and making it smaller and smaller until an existing name is found.
while (prefix + str(n) not in unique_names) and (step > 1):
step //= 2
n -= step
name = prefix + str(n)
if step == 1:
break

# The initial name is the prefix plus the number.
initial = prefix + str(n)
# Go back to the start of the loop and search forward from this value
# of n looking for an unused slot.

# If the initial name is just a number, then prepend the prefix to it.
elif isinstance(initial, int):
initial = prefix + str(initial)
elif isinstance(name, int):
name = prefix + str(name)

# Now determine if there are any items in the list with the same name.
# If the name is unique, then return it.
if initial not in unique_names:
return initial
if name not in unique_names:
return name

# Otherwise, determine how many copies of the name are in the list and
# append a number to make this name unique.
filter_dict = {attrib: re.escape(initial) + r"_\d+"}
filter_dict = {attrib: re.escape(name) + r"_\d+"}
n = len(filter_list(lst, **filter_dict))
initial = initial + "_" + str(n + 1)
name = name + "_" + str(n + 1)

# Recursively call this routine using the newly-generated name to
# make sure it's unique. Eventually, a unique name will be returned.
return get_unique_name(lst, attrib, prefix, initial)
return get_unique_name(lst, attrib, prefix, name)


def fullmatch(regex, string, flags=0):
Expand Down
21 changes: 21 additions & 0 deletions tests/test_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,24 @@ def test_name_3():
net_names = [n.name for n in default_circuit.nets]
unique_net_names = set(net_names)
assert len(unique_net_names) == len(net_names)


def test_name_4():
l = 30
for _ in range(l):
n = Net()
assert len(default_circuit.nets) == l + 1 # Account for NC net.


def test_name_5():
from random import shuffle

l = 30
lst = list(range(100))
k = 10
shuffle(lst)
for i in lst[:k]:
n = Net(i)
for _ in range(l):
n = Net()
assert len(default_circuit.nets) == l + k + 1 # Account for NC net.

0 comments on commit a84d455

Please sign in to comment.