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) |