SHOW:
|
|
- or go back to the newest paste.
1 | #!/bin/sh | |
2 | exec /Users/baldur/scala-2.11.7/bin/scala "$0" "$@" | |
3 | !# | |
4 | ||
5 | val inputRE = """\s*(add|remove)\s+(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)\s*""".r | |
6 | def toIP(i1: Int, i2: Int, i3: Int, i4: Int) = (i1 << 24) | (i2 << 16) | (i3 << 8) | i4 | |
7 | def ipToString(ip: Int): String = f"${(ip>>24)&255}.${(ip>>16)&255}.${(ip>>8)&255}.${ip&255}" | |
8 | case class Subnet(ip: Int, prefixLength: Int) { | |
9 | override def toString: String = f"${ipToString(ip)}/${prefixLength}" | |
10 | } | |
11 | def netmask(prefixLength: Int) = if (prefixLength==0) 0 else -1 << (32 - prefixLength) | |
12 | ||
13 | // a valid subnet is a subnet where (ip & netmask) == ip | |
14 | def makeValid(subnet: Subnet): Subnet = | |
15 | subnet.copy(ip = subnet.ip & netmask(subnet.prefixLength)) | |
16 | ||
17 | // split a subnet into two each with prefixLength+1 | |
18 | def split(subnet: Subnet): (Subnet,Subnet) = ( | |
19 | Subnet(subnet.ip, subnet.prefixLength + 1), | |
20 | Subnet(subnet.ip + (1 << (31 - subnet.prefixLength)), subnet.prefixLength + 1)) | |
21 | ||
22 | /* | |
23 | subnetList: current set of subnets that shall be expanded | |
24 | subnetToAdd: subnet that will be added to subnetList | |
25 | returns: set of subnets that includes everything from subnetList plus subnetToAdd | |
26 | ||
27 | requirements: | |
28 | ||
29 | subnetToAdd is valid | |
30 | ||
31 | subnetList is optimal | |
32 | subnetList does not contain overlapping subnets | |
33 | subnetList does not contain subnets that could be merged | |
34 | subnetList does not contain invalid subnets | |
35 | ||
36 | return value provides same guaranties | |
37 | */ | |
38 | def add(subnetList: Set[Subnet], subnetToAdd: Subnet): Set[Subnet] = { | |
39 | // 1) remove all subnets covered by subnetToAdd | |
40 | val subnetList2 = subnetList.filterNot { subnet => | |
41 | subnet.prefixLength >= subnetToAdd.prefixLength && | |
42 | ((subnet.ip & netmask(subnetToAdd.prefixLength)) == subnetToAdd.ip) | |
43 | } | |
44 | ||
45 | // 2) add subnetToAdd | |
46 | val subnetList3 = subnetList2 + subnetToAdd | |
47 | ||
48 | // 3) possibly merge subnetToAdd with adjicent subnet | |
49 | // 4) recursively perform step 4 until no merge or 0.0.0.0/0 | |
50 | def merge(subnetList: Set[Subnet], toBeMerged: Subnet): Set[Subnet] = { | |
51 | if (toBeMerged.prefixLength == 0) return subnetList | |
52 | val supernet = makeValid(Subnet(toBeMerged.ip,toBeMerged.prefixLength-1)) | |
53 | val (subnet1,subnet2) = split(supernet) | |
54 | // either subnet1 or subnet2 will be equal to toBeMerged | |
55 | if (subnetList(subnet1) && subnetList(subnet2)) { | |
56 | // two subnets found, remove both and add supernet instead | |
57 | // then run merge recursively | |
58 | merge(subnetList - subnet1 - subnet2 + supernet, supernet) | |
59 | } else { | |
60 | // done, could not find two subnets to merge | |
61 | subnetList | |
62 | } | |
63 | ||
64 | } | |
65 | ||
66 | merge(subnetList3, subnetToAdd) | |
67 | } | |
68 | ||
69 | /* | |
70 | subnetList: current set of subnets that shall be reduced | |
71 | subnetToRemove: subnet that will be removed from subnetList | |
72 | returns: set of subnets that includes all address space covered by subnetList excluding subnetToRemove | |
73 | ||
74 | requirements: | |
75 | ||
76 | subnetList is optimal | |
77 | subnetList does not contain overlapping subnets | |
78 | subnetList does not contain subnets that could be merged | |
79 | subnetList does not contain invalid subnets | |
80 | ||
81 | return value provides same guaranties | |
82 | */ | |
83 | def remove(subnetList: Set[Subnet], subnetToRemove: Subnet): Set[Subnet] = { | |
84 | // 1) remove all subnets covered by subnetToRemove | |
85 | val subnetList2 = subnetList.filterNot { subnet => | |
86 | subnet.prefixLength >= subnetToRemove.prefixLength && | |
87 | ((subnet.ip & netmask(subnetToRemove.prefixLength)) == subnetToRemove.ip) | |
88 | } | |
89 | ||
90 | // 2) find all subnets that covers subnetToRemove | |
91 | val subnetCoversList = subnetList2.filter { subnet => | |
92 | (subnetToRemove.ip & netmask(subnet.prefixLength)) == subnet.ip | |
93 | } | |
94 | ||
95 | // 3) select bestMatch, that is the one of largest prefixLength | |
96 | val bestMatch = subnetCoversList.toVector.sortBy(-_.prefixLength).headOption.getOrElse { | |
97 | // subnetCoversList is empty so we are done | |
98 | return subnetList2 | |
99 | } | |
100 | ||
101 | // 4) remove bestMatch | |
102 | val subnetList3 = subnetList2 - bestMatch | |
103 | ||
104 | // 5) possibly split bestMatch and add the half that does not cover subnetToRemove | |
105 | // 6) recursively perform step 4 until subnet to split is equal to subnetToRemove | |
106 | def splitStep(subnetList: Set[Subnet], toBeSplit: Subnet): Set[Subnet] = { | |
107 | if (toBeSplit == subnetToRemove) return subnetList | |
108 | val (subnet1,subnet2) = split(toBeSplit) | |
109 | if ((subnetToRemove.ip & netmask(subnet1.prefixLength)) == subnet1.ip) { | |
110 | // add subnet2 and split subnet1 | |
111 | splitStep(subnetList + subnet2, subnet1) | |
112 | } else { | |
113 | // add subnet1 and split subnet2 | |
114 | splitStep(subnetList + subnet1, subnet2) | |
115 | } | |
116 | } | |
117 | ||
118 | splitStep(subnetList3, bestMatch) | |
119 | } | |
120 | ||
121 | val input = io.Source.stdin.getLines.collect { | |
122 | case inputRE(command,i1,i2,i3,i4,l) => | |
123 | - | (command,Subnet(toIP(i1.toInt,i2.toInt,i3.toInt,i4.toInt),l.toInt)) |
123 | + | (command,makeValid(Subnet(toIP(i1.toInt,i2.toInt,i3.toInt,i4.toInt),l.toInt))) |
124 | } | |
125 | ||
126 | val result = input.foldLeft[Set[Subnet]](Set.empty){ case (acc,op) => op match { | |
127 | case ("add",subnet: Subnet) => add(acc,subnet) | |
128 | case ("remove",subnet: Subnet) => remove(acc,subnet) | |
129 | }} | |
130 | ||
131 | for(subnet <- result.toVector.sortBy(_.prefixLength).sortBy(_.ip.toLong & 0xffffffffL)) | |
132 | println(subnet) |