import Foundation extension String { func paddedLeft(totalLen: Int) -> String { let needed = totalLen - count guard needed > 0 else { return self } return String(repeating: " ", count: needed) + self } } class FCNode { let name: String let weight: Int var coverage: Double { didSet { if oldValue != coverage { parent?.updateCoverage() } } } weak var parent: FCNode? var children = [FCNode]() init(name: String, weight: Int = 1, coverage: Double = 0) { self.name = name self.weight = weight self.coverage = coverage } func addChildren(_ children: [FCNode]) { for child in children { child.parent = self } self.children += children updateCoverage() } func show(level: Int = 0) { let indent = level * 4 let nameLen = name.count + indent print(name.paddedLeft(totalLen: nameLen), terminator: "") print("|".paddedLeft(totalLen: 32 - nameLen), terminator: "") print(String(format: " %3d |", weight), terminator: "") print(String(format: " %8.6f |", coverage)) for child in children { child.show(level: level + 1) } } func updateCoverage() { let v1 = children.reduce(0.0, { $0 + $1.coverage * Double($1.weight) }) let v2 = children.reduce(0.0, { $0 + Double($1.weight) }) coverage = v1 / v2 } } let houses = [ FCNode(name: "house1", weight: 40), FCNode(name: "house2", weight: 60) ] let house1 = [ FCNode(name: "bedrooms", weight: 1, coverage: 0.25), FCNode(name: "bathrooms"), FCNode(name: "attic", weight: 1, coverage: 0.75), FCNode(name: "kitchen", weight: 1, coverage: 0.1), FCNode(name: "living_rooms"), FCNode(name: "basement"), FCNode(name: "garage"), FCNode(name: "garden", weight: 1, coverage: 0.8) ] let house2 = [ FCNode(name: "upstairs"), FCNode(name: "groundfloor"), FCNode(name: "basement") ] let h1Bathrooms = [ FCNode(name: "bathroom1", weight: 1, coverage: 0.5), FCNode(name: "bathroom2"), FCNode(name: "outside_lavatory", weight: 1, coverage: 1.0) ] let h1LivingRooms = [ FCNode(name: "lounge"), FCNode(name: "dining_room"), FCNode(name: "conservatory"), FCNode(name: "playroom", weight: 1, coverage: 1.0) ] let h2Upstairs = [ FCNode(name: "bedrooms"), FCNode(name: "bathroom"), FCNode(name: "toilet"), FCNode(name: "attics", weight: 1, coverage: 0.6) ] let h2Groundfloor = [ FCNode(name: "kitchen"), FCNode(name: "living_rooms"), FCNode(name: "wet_room_&_toilet"), FCNode(name: "garage"), FCNode(name: "garden", weight: 1, coverage: 0.9), FCNode(name: "hot_tub_suite", weight: 1, coverage: 1.0) ] let h2Basement = [ FCNode(name: "cellars", weight: 1, coverage: 1.0), FCNode(name: "wine_cellar", weight: 1, coverage: 1.0), FCNode(name: "cinema", weight: 1, coverage: 0.75) ] let h2UpstairsBedrooms = [ FCNode(name: "suite_1"), FCNode(name: "suite_2"), FCNode(name: "bedroom_3"), FCNode(name: "bedroom_4") ] let h2GroundfloorLivingRooms = [ FCNode(name: "lounge"), FCNode(name: "dining_room"), FCNode(name: "conservatory"), FCNode(name: "playroom") ] let cleaning = FCNode(name: "cleaning") house1[1].addChildren(h1Bathrooms) house1[4].addChildren(h1LivingRooms) houses[0].addChildren(house1) h2Upstairs[0].addChildren(h2UpstairsBedrooms) house2[0].addChildren(h2Upstairs) h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms) house2[1].addChildren(h2Groundfloor) house2[2].addChildren(h2Basement) houses[1].addChildren(house2) cleaning.addChildren(houses) let top = cleaning.coverage print("Top Coverage: \(String(format: "%8.6f", top))") print("Name Hierarchy | Weight | Coverage |") cleaning.show() h2Basement[2].coverage = 1.0 let diff = cleaning.coverage - top print("\nIf the coverage of the Cinema node were increased from 0.75 to 1.0") print("the top level coverage would increase by ") print("\(String(format: "%8.6f", diff)) to \(String(format: "%8.6f", top))")