Merge pull request #7 from Fedor-Lyanguzov/cma-two-versions
Fast and precise algorithms, with tests
This commit was merged in pull request #7.
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
alias venv='source .venv/Scripts/activate'
|
||||||
|
alias check='flake8 vpn_manager'
|
||||||
|
alias format='black vpn_manager'
|
||||||
|
alias test='pytest'
|
||||||
|
|
||||||
|
venv
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
from vpn_manager.cidr4_merge.fast import solution as fast
|
||||||
|
from vpn_manager.cidr4_merge.precise import solution as precise, f
|
||||||
|
from vpn_manager.cidr4_merge.util import *
|
||||||
|
|
||||||
|
|
||||||
|
def test_true():
|
||||||
|
assert True
|
||||||
|
|
||||||
|
#cidrs = list(map(cidr4_to_node, get_data()))
|
||||||
|
|
||||||
|
def test_fast_single_lifting():
|
||||||
|
assert ([(0, 30)], 0) ==\
|
||||||
|
fast([(0, 31), (2, 31)], 1)
|
||||||
|
assert ([(0, 29)], 2) ==\
|
||||||
|
fast([(0, 30), (4, 31)], 1)
|
||||||
|
assert ([(0, 29)], 3) ==\
|
||||||
|
fast([(0, 30), (4, 32)], 1)
|
||||||
|
|
||||||
|
def test_fast_double_lifting():
|
||||||
|
assert ([(0, 29)], 4) ==\
|
||||||
|
fast([(0, 31), (4, 31)], 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fast_subnets():
|
||||||
|
assert ([(0, 30)], 0) ==\
|
||||||
|
fast([(0, 30), (0, 31)], 1)
|
||||||
|
assert ([(0, 29)], 0) ==\
|
||||||
|
fast([(0, 29), (4, 31)], 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_precise_single_lifting():
|
||||||
|
assert ([(0, 30)], 0) ==\
|
||||||
|
precise([(0, 31), (2, 31)], 1)
|
||||||
|
assert ([(0, 29)], 2) ==\
|
||||||
|
precise([(0, 30), (4, 31)], 1)
|
||||||
|
assert ([(0, 29)], 3) ==\
|
||||||
|
precise([(0, 30), (4, 32)], 1)
|
||||||
|
|
||||||
|
def test_precise_double_lifting():
|
||||||
|
assert ([(0, 29)], 4) ==\
|
||||||
|
precise([(0, 31), (4, 31)], 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_precise_subnets():
|
||||||
|
assert ([(0, 30)], 0) ==\
|
||||||
|
precise([(0, 30), (0, 31)], 1)
|
||||||
|
assert ([(0, 29)], 0) ==\
|
||||||
|
precise([(0, 29), (4, 31)], 1)
|
||||||
|
|
||||||
|
def test_precise_f_single_lifting():
|
||||||
|
assert (0, (0, 30, 0)) ==\
|
||||||
|
f((0, 31, 0), (2, 31, 0))
|
||||||
|
assert (2, (0, 29, 2)) ==\
|
||||||
|
f((0, 30, 0), (4, 31, 0))
|
||||||
|
assert (3, (0, 29, 3)) ==\
|
||||||
|
f((0, 30, 0), (4, 32, 0))
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from heapq import heapify, heappush, heappop
|
||||||
|
from .util import mask, get_data, cidr4_to_node, make_cidr4
|
||||||
|
|
||||||
|
|
||||||
|
def solution(cidrs, M):
|
||||||
|
h = []
|
||||||
|
d = {}
|
||||||
|
for ip, l in cidrs:
|
||||||
|
h.append((32-l, ip, l))
|
||||||
|
d[(ip, l)] = 0
|
||||||
|
heapify(h)
|
||||||
|
while len(h)>M:
|
||||||
|
x1, ip1, l1 = heappop(h)
|
||||||
|
x2, ip2, l2 = h[0]
|
||||||
|
if l1==l2 and ip1 & mask[l1-1] == ip2 & mask[l2-1]:
|
||||||
|
heappop(h)
|
||||||
|
if (ip1 & mask[l1-1], l1-1) not in d:
|
||||||
|
heappush(h, (x1+1, ip1 & mask[l1-1], l1-1))
|
||||||
|
d[(ip1 & mask[l1-1], l1-1)] = d[(ip1, l1)] + d[(ip2, l2)]
|
||||||
|
del d[(ip2, l2)]
|
||||||
|
else:
|
||||||
|
if (ip1 & mask[l1-1], l1-1) not in d:
|
||||||
|
heappush(h, (x1+1, ip1 & mask[l1-1], l1-1))
|
||||||
|
d[(ip1 & mask[l1-1], l1-1)] = d[(ip1, l1)] + 2**x1
|
||||||
|
del d[(ip1, l1)]
|
||||||
|
s = sum(d.values())
|
||||||
|
cidrs = list(d.keys())
|
||||||
|
return cidrs, s
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
M = 20
|
||||||
|
a = get_data()
|
||||||
|
b = list(map(cidr4_to_node, a))
|
||||||
|
cidrs, s = solution(b, M)
|
||||||
|
cidrs = sorted([make_cidr4(*x) for x in cidrs])
|
||||||
|
print(cidrs, s, sep='\n')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
import cProfile
|
||||||
|
main()
|
||||||
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
from .util import mask, get_data, cidr4_to_node, make_cidr4
|
||||||
|
|
||||||
|
|
||||||
|
def f(x, y):
|
||||||
|
t = x
|
||||||
|
b = y
|
||||||
|
if x[1]>y[1]:
|
||||||
|
t = y
|
||||||
|
b = x
|
||||||
|
ip1, l1, a1 = t
|
||||||
|
ip2, l2, a2 = b
|
||||||
|
if ip1 & mask[l1] == ip2 & mask[l1]:
|
||||||
|
return (0, t)
|
||||||
|
t1 = t2 = 0
|
||||||
|
while not l1==l2:
|
||||||
|
t2 += 2**(32-l2)
|
||||||
|
l2 -= 1
|
||||||
|
ip2 = ip2 & mask[l2]
|
||||||
|
while not ip1 & mask[l1-1] == ip2 & mask[l2-1]:
|
||||||
|
t1 += 2**(32-l1)
|
||||||
|
l1 -= 1
|
||||||
|
ip1 = ip1 & mask[l1]
|
||||||
|
t2 += 2**(32-l2)
|
||||||
|
l2 -= 1
|
||||||
|
ip2 = ip2 & mask[l2]
|
||||||
|
r = (ip1 & mask[l1-1], l1-1, a1+a2+t1+t2)
|
||||||
|
return (t1+t2, r)
|
||||||
|
|
||||||
|
|
||||||
|
def solution(cidrs, M):
|
||||||
|
cidrs = sorted((ip, l, 0) for ip, l in cidrs)
|
||||||
|
while len(cidrs)>M:
|
||||||
|
t = (None, float('+inf'), None)
|
||||||
|
for i, (x, y) in enumerate(zip(cidrs, cidrs[1:])):
|
||||||
|
m, r = f(x, y)
|
||||||
|
if m<t[1]:
|
||||||
|
t = (i, m, r)
|
||||||
|
if m==0:
|
||||||
|
break
|
||||||
|
cidrs[t[0]] = t[2]
|
||||||
|
del cidrs[t[0]+1]
|
||||||
|
s = sum(x[2] for x in cidrs)
|
||||||
|
cidrs = [x[:2] for x in cidrs]
|
||||||
|
return cidrs, s
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
M = 20
|
||||||
|
a = get_data()
|
||||||
|
b = list(map(cidr4_to_node, a))
|
||||||
|
cidrs, s = solution(b, M)
|
||||||
|
cidrs = sorted([make_cidr4(*x) for x in cidrs])
|
||||||
|
print(cidrs, s, sep='\n')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
import cProfile
|
||||||
|
main()
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
mask = [((1 << i) - 1) << (32 - i) for i in range(33)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_data(input_file='cidr4.txt'):
|
||||||
|
with open(input_file, "r") as file:
|
||||||
|
return file.read().splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def cidr4_to_node(cidr4: str):
|
||||||
|
ip_address, mask_len = cidr4.strip().split("/")
|
||||||
|
mask_len = int(mask_len)
|
||||||
|
a, b, c, d = list(map(int, ip_address.split(".")))
|
||||||
|
ip = a * 256**3 + b * 256**2 + c * 256**1 + d * 256**0
|
||||||
|
return ip, mask_len
|
||||||
|
|
||||||
|
|
||||||
|
def make_cidr4(ip, mask_len) -> str:
|
||||||
|
lst = [str(ip >> (i << 3) & 0xFF) for i in reversed(range(4))]
|
||||||
|
ip_address = ".".join(lst)
|
||||||
|
return f"{ip_address}/{mask_len}"
|
||||||
Reference in New Issue
Block a user