on master help sign in
Initial commit
commitd5bbe9162ba53a46f0ebb1f6e1c1ba039863df45 authorilyakooo0 <ilyakooo0@gmail.com> dateSun 11 Mar 2018, 16:25 (9 years ago)
8 files changed+1099
.gitignore+68 Galmin.xcodeproj/project.pbxproj+292 Galmin.xcodeproj/project.xcworkspace/contents.xcworkspacedata+7 Galmin.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings+8 Galmin/Graph.swift+263 Galmin/Printer.swift+213 Galmin/Solver.swift+185 Galmin/main.swift+63
.gitignorenew file+68
@@ -0,0 +1,68 @@
1+# Xcode
2+#
3+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4+
5+## Build generated
6+build/
7+DerivedData/
8+
9+## Various settings
10+*.pbxuser
11+!default.pbxuser
12+*.mode1v3
13+!default.mode1v3
14+*.mode2v3
15+!default.mode2v3
16+*.perspectivev3
17+!default.perspectivev3
18+xcuserdata/
19+
20+## Other
21+*.moved-aside
22+*.xccheckout
23+*.xcscmblueprint
24+
25+## Obj-C/Swift specific
26+*.hmap
27+*.ipa
28+*.dSYM.zip
29+*.dSYM
30+
31+## Playgrounds
32+timeline.xctimeline
33+playground.xcworkspace
34+
35+# Swift Package Manager
36+#
37+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38+# Packages/
39+# Package.pins
40+# Package.resolved
41+.build/
42+
43+# CocoaPods
44+#
45+# We recommend against adding the Pods directory to your .gitignore. However
46+# you should judge for yourself, the pros and cons are mentioned at:
47+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48+#
49+# Pods/
50+
51+# Carthage
52+#
53+# Add this line if you want to avoid checking in source code from Carthage dependencies.
54+# Carthage/Checkouts
55+
56+Carthage/Build
57+
58+# fastlane
59+#
60+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61+# screenshots whenever they are needed.
62+# For more information about the recommended setup visit:
63+# https://docs.fastlane.tools/best-practices/source-control/#source-control
64+
65+fastlane/report.xml
66+fastlane/Preview.html
67+fastlane/screenshots
68+fastlane/test_output
Galmin.xcodeproj/project.pbxprojnew file+292
@@ -0,0 +1,292 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 48;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ 0977ACA5203F351500618E62 /* Printer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0977ACA4203F351500618E62 /* Printer.swift */; };
11+ 09C2834F2038A19D00E11545 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C2834E2038A19D00E11545 /* main.swift */; };
12+ 09C283562038A1BB00E11545 /* Graph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C283552038A1BB00E11545 /* Graph.swift */; };
13+ 09C283582038C79800E11545 /* Solver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C283572038C79800E11545 /* Solver.swift */; };
14+/* End PBXBuildFile section */
15+
16+/* Begin PBXCopyFilesBuildPhase section */
17+ 09C283492038A19D00E11545 /* CopyFiles */ = {
18+ isa = PBXCopyFilesBuildPhase;
19+ buildActionMask = 2147483647;
20+ dstPath = /usr/share/man/man1/;
21+ dstSubfolderSpec = 0;
22+ files = (
23+ );
24+ runOnlyForDeploymentPostprocessing = 1;
25+ };
26+/* End PBXCopyFilesBuildPhase section */
27+
28+/* Begin PBXFileReference section */
29+ 0977ACA4203F351500618E62 /* Printer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Printer.swift; sourceTree = "<group>"; };
30+ 09C2834B2038A19D00E11545 /* Galmin */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Galmin; sourceTree = BUILT_PRODUCTS_DIR; };
31+ 09C2834E2038A19D00E11545 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
32+ 09C283552038A1BB00E11545 /* Graph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Graph.swift; sourceTree = "<group>"; };
33+ 09C283572038C79800E11545 /* Solver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Solver.swift; sourceTree = "<group>"; };
34+/* End PBXFileReference section */
35+
36+/* Begin PBXFrameworksBuildPhase section */
37+ 09C283482038A19D00E11545 /* Frameworks */ = {
38+ isa = PBXFrameworksBuildPhase;
39+ buildActionMask = 2147483647;
40+ files = (
41+ );
42+ runOnlyForDeploymentPostprocessing = 0;
43+ };
44+/* End PBXFrameworksBuildPhase section */
45+
46+/* Begin PBXGroup section */
47+ 09C283422038A19D00E11545 = {
48+ isa = PBXGroup;
49+ children = (
50+ 09C2834D2038A19D00E11545 /* Galmin */,
51+ 09C2834C2038A19D00E11545 /* Products */,
52+ );
53+ sourceTree = "<group>";
54+ };
55+ 09C2834C2038A19D00E11545 /* Products */ = {
56+ isa = PBXGroup;
57+ children = (
58+ 09C2834B2038A19D00E11545 /* Galmin */,
59+ );
60+ name = Products;
61+ sourceTree = "<group>";
62+ };
63+ 09C2834D2038A19D00E11545 /* Galmin */ = {
64+ isa = PBXGroup;
65+ children = (
66+ 09C2834E2038A19D00E11545 /* main.swift */,
67+ 09C283552038A1BB00E11545 /* Graph.swift */,
68+ 09C283572038C79800E11545 /* Solver.swift */,
69+ 0977ACA4203F351500618E62 /* Printer.swift */,
70+ );
71+ path = Galmin;
72+ sourceTree = "<group>";
73+ };
74+/* End PBXGroup section */
75+
76+/* Begin PBXNativeTarget section */
77+ 09C2834A2038A19D00E11545 /* Galmin */ = {
78+ isa = PBXNativeTarget;
79+ buildConfigurationList = 09C283522038A19D00E11545 /* Build configuration list for PBXNativeTarget "Galmin" */;
80+ buildPhases = (
81+ 09C283472038A19D00E11545 /* Sources */,
82+ 09C283482038A19D00E11545 /* Frameworks */,
83+ 09C283492038A19D00E11545 /* CopyFiles */,
84+ );
85+ buildRules = (
86+ );
87+ dependencies = (
88+ );
89+ name = Galmin;
90+ productName = Galmin;
91+ productReference = 09C2834B2038A19D00E11545 /* Galmin */;
92+ productType = "com.apple.product-type.tool";
93+ };
94+/* End PBXNativeTarget section */
95+
96+/* Begin PBXProject section */
97+ 09C283432038A19D00E11545 /* Project object */ = {
98+ isa = PBXProject;
99+ attributes = {
100+ LastSwiftUpdateCheck = 0920;
101+ LastUpgradeCheck = 0920;
102+ ORGANIZATIONNAME = "Ilya Kos";
103+ TargetAttributes = {
104+ 09C2834A2038A19D00E11545 = {
105+ CreatedOnToolsVersion = 9.2;
106+ ProvisioningStyle = Automatic;
107+ };
108+ };
109+ };
110+ buildConfigurationList = 09C283462038A19D00E11545 /* Build configuration list for PBXProject "Galmin" */;
111+ compatibilityVersion = "Xcode 8.0";
112+ developmentRegion = en;
113+ hasScannedForEncodings = 0;
114+ knownRegions = (
115+ en,
116+ );
117+ mainGroup = 09C283422038A19D00E11545;
118+ productRefGroup = 09C2834C2038A19D00E11545 /* Products */;
119+ projectDirPath = "";
120+ projectRoot = "";
121+ targets = (
122+ 09C2834A2038A19D00E11545 /* Galmin */,
123+ );
124+ };
125+/* End PBXProject section */
126+
127+/* Begin PBXSourcesBuildPhase section */
128+ 09C283472038A19D00E11545 /* Sources */ = {
129+ isa = PBXSourcesBuildPhase;
130+ buildActionMask = 2147483647;
131+ files = (
132+ 0977ACA5203F351500618E62 /* Printer.swift in Sources */,
133+ 09C283582038C79800E11545 /* Solver.swift in Sources */,
134+ 09C283562038A1BB00E11545 /* Graph.swift in Sources */,
135+ 09C2834F2038A19D00E11545 /* main.swift in Sources */,
136+ );
137+ runOnlyForDeploymentPostprocessing = 0;
138+ };
139+/* End PBXSourcesBuildPhase section */
140+
141+/* Begin XCBuildConfiguration section */
142+ 09C283502038A19D00E11545 /* Debug */ = {
143+ isa = XCBuildConfiguration;
144+ buildSettings = {
145+ ALWAYS_SEARCH_USER_PATHS = NO;
146+ CLANG_ANALYZER_NONNULL = YES;
147+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
148+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
149+ CLANG_CXX_LIBRARY = "libc++";
150+ CLANG_ENABLE_MODULES = YES;
151+ CLANG_ENABLE_OBJC_ARC = YES;
152+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
153+ CLANG_WARN_BOOL_CONVERSION = YES;
154+ CLANG_WARN_COMMA = YES;
155+ CLANG_WARN_CONSTANT_CONVERSION = YES;
156+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
157+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
158+ CLANG_WARN_EMPTY_BODY = YES;
159+ CLANG_WARN_ENUM_CONVERSION = YES;
160+ CLANG_WARN_INFINITE_RECURSION = YES;
161+ CLANG_WARN_INT_CONVERSION = YES;
162+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
163+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
164+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
165+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
166+ CLANG_WARN_STRICT_PROTOTYPES = YES;
167+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
168+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
169+ CLANG_WARN_UNREACHABLE_CODE = YES;
170+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
171+ CODE_SIGN_IDENTITY = "Mac Developer";
172+ COPY_PHASE_STRIP = NO;
173+ DEBUG_INFORMATION_FORMAT = dwarf;
174+ ENABLE_STRICT_OBJC_MSGSEND = YES;
175+ ENABLE_TESTABILITY = YES;
176+ GCC_C_LANGUAGE_STANDARD = gnu11;
177+ GCC_DYNAMIC_NO_PIC = NO;
178+ GCC_NO_COMMON_BLOCKS = YES;
179+ GCC_OPTIMIZATION_LEVEL = 0;
180+ GCC_PREPROCESSOR_DEFINITIONS = (
181+ "DEBUG=1",
182+ "$(inherited)",
183+ );
184+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
185+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
186+ GCC_WARN_UNDECLARED_SELECTOR = YES;
187+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
188+ GCC_WARN_UNUSED_FUNCTION = YES;
189+ GCC_WARN_UNUSED_VARIABLE = YES;
190+ MACOSX_DEPLOYMENT_TARGET = 10.13;
191+ MTL_ENABLE_DEBUG_INFO = YES;
192+ ONLY_ACTIVE_ARCH = YES;
193+ SDKROOT = macosx;
194+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
195+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
196+ };
197+ name = Debug;
198+ };
199+ 09C283512038A19D00E11545 /* Release */ = {
200+ isa = XCBuildConfiguration;
201+ buildSettings = {
202+ ALWAYS_SEARCH_USER_PATHS = NO;
203+ CLANG_ANALYZER_NONNULL = YES;
204+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
205+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
206+ CLANG_CXX_LIBRARY = "libc++";
207+ CLANG_ENABLE_MODULES = YES;
208+ CLANG_ENABLE_OBJC_ARC = YES;
209+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
210+ CLANG_WARN_BOOL_CONVERSION = YES;
211+ CLANG_WARN_COMMA = YES;
212+ CLANG_WARN_CONSTANT_CONVERSION = YES;
213+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
214+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
215+ CLANG_WARN_EMPTY_BODY = YES;
216+ CLANG_WARN_ENUM_CONVERSION = YES;
217+ CLANG_WARN_INFINITE_RECURSION = YES;
218+ CLANG_WARN_INT_CONVERSION = YES;
219+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
220+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
221+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
222+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
223+ CLANG_WARN_STRICT_PROTOTYPES = YES;
224+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
225+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
226+ CLANG_WARN_UNREACHABLE_CODE = YES;
227+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
228+ CODE_SIGN_IDENTITY = "Mac Developer";
229+ COPY_PHASE_STRIP = NO;
230+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
231+ ENABLE_NS_ASSERTIONS = NO;
232+ ENABLE_STRICT_OBJC_MSGSEND = YES;
233+ GCC_C_LANGUAGE_STANDARD = gnu11;
234+ GCC_NO_COMMON_BLOCKS = YES;
235+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
236+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
237+ GCC_WARN_UNDECLARED_SELECTOR = YES;
238+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
239+ GCC_WARN_UNUSED_FUNCTION = YES;
240+ GCC_WARN_UNUSED_VARIABLE = YES;
241+ MACOSX_DEPLOYMENT_TARGET = 10.13;
242+ MTL_ENABLE_DEBUG_INFO = NO;
243+ SDKROOT = macosx;
244+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
245+ };
246+ name = Release;
247+ };
248+ 09C283532038A19D00E11545 /* Debug */ = {
249+ isa = XCBuildConfiguration;
250+ buildSettings = {
251+ CODE_SIGN_STYLE = Automatic;
252+ DEVELOPMENT_TEAM = 8J63JZYYHZ;
253+ PRODUCT_NAME = "$(TARGET_NAME)";
254+ SWIFT_VERSION = 4.0;
255+ };
256+ name = Debug;
257+ };
258+ 09C283542038A19D00E11545 /* Release */ = {
259+ isa = XCBuildConfiguration;
260+ buildSettings = {
261+ CODE_SIGN_STYLE = Automatic;
262+ DEVELOPMENT_TEAM = 8J63JZYYHZ;
263+ PRODUCT_NAME = "$(TARGET_NAME)";
264+ SWIFT_VERSION = 4.0;
265+ };
266+ name = Release;
267+ };
268+/* End XCBuildConfiguration section */
269+
270+/* Begin XCConfigurationList section */
271+ 09C283462038A19D00E11545 /* Build configuration list for PBXProject "Galmin" */ = {
272+ isa = XCConfigurationList;
273+ buildConfigurations = (
274+ 09C283502038A19D00E11545 /* Debug */,
275+ 09C283512038A19D00E11545 /* Release */,
276+ );
277+ defaultConfigurationIsVisible = 0;
278+ defaultConfigurationName = Release;
279+ };
280+ 09C283522038A19D00E11545 /* Build configuration list for PBXNativeTarget "Galmin" */ = {
281+ isa = XCConfigurationList;
282+ buildConfigurations = (
283+ 09C283532038A19D00E11545 /* Debug */,
284+ 09C283542038A19D00E11545 /* Release */,
285+ );
286+ defaultConfigurationIsVisible = 0;
287+ defaultConfigurationName = Release;
288+ };
289+/* End XCConfigurationList section */
290+ };
291+ rootObject = 09C283432038A19D00E11545 /* Project object */;
292+}
Galmin.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:Galmin.xcodeproj">
6+ </FileRef>
7+</Workspace>
Galmin.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettingsnew file+8
@@ -0,0 +1,8 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>BuildSystemType</key>
6+ <string>Latest</string>
7+</dict>
8+</plist>
Galmin/Graph.swiftnew file+263
@@ -0,0 +1,263 @@
1+//
2+// Graph.swift
3+// Galmin
4+//
5+// Created by Ilya Kos on 2/17/18.
6+// Copyright © 2018 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+
12+/// Full graph
13+struct Graph<Vertex> where Vertex: Hashable & Comparable {
14+ private var weights: [Vertex: [Vertex: Weight]] = [:]
15+ private var usedVertices: Set<Vertex>
16+ var usedEdges: [(start: Vertex, end: Vertex)] = []
17+ /// Not actual min weight. It's the minimal possible weight.
18+ var minWeight = 0
19+
20+ var graphWeights: [Vertex: [Vertex: Weight]] {
21+ return weights
22+ }
23+
24+ var components: [Set<Vertex>] {
25+ var out: [Set<Vertex>] = []
26+ for edge in usedEdges {
27+ var first: Set<Vertex>!
28+ for (i, component) in out.enumerated() {
29+ if component.contains(edge.start) {
30+ first = out.remove(at: i)
31+ break // Since only one component can contain a vertex
32+ }
33+ }
34+ if first == nil {
35+ first = Set([edge.start])
36+ }
37+ var second: Set<Vertex>!
38+ for (i, component) in out.enumerated() {
39+ if component.contains(edge.end) {
40+ second = out.remove(at: i)
41+ break // Since only one component can contain a vertex
42+ }
43+ }
44+ if second == nil {
45+ second = Set([edge.end])
46+ }
47+ out.append(first.union(second))
48+ }
49+ return out
50+ }
51+
52+ private mutating func deleteEdge(from start: Vertex, to end: Vertex) {
53+ weights[start] = nil
54+ weights = weights.mapValues { row in
55+ var row = row
56+ row[end] = nil
57+ return row
58+ }
59+ weights[end]?[start]? = .infinity
60+ }
61+
62+ typealias Minimization = (before: [Vertex: [Vertex: Weight]], after: [Vertex: [Vertex: Weight]], reductions: (starts: [Vertex: Int], ends: [Vertex: Int]))
63+
64+ mutating func minimize() -> Minimization {
65+ assert(!weights.isEmpty)
66+ // Minimize rows
67+ let before = weights
68+ var starts: [Vertex: Int] = [:]
69+ for (key, row) in weights {
70+ let offset = row.values.min()!
71+ if offset.numValue == 0 {
72+ continue
73+ }
74+ starts[key] = offset.numValue
75+ minWeight += offset.numValue
76+ weights[key] = row.mapValues {$0 - offset}
77+ }
78+
79+ // Minimize columns
80+ var ends: [Vertex: Int] = [:]
81+ for key in weights.first!.value.keys {
82+ let offset = weights.values.map({$0[key]!}).min()!
83+ if offset.numValue == 0 { // Just optimization
84+ continue
85+ }
86+ ends[key] = offset.numValue
87+ minWeight += offset.numValue
88+ weights = weights.mapValues { row in
89+ var row = row
90+ row[key]! -= offset
91+ return row
92+ }
93+ }
94+ let after = weights
95+ return (before: before, after: after, reductions: (starts: starts, ends: ends))
96+ }
97+
98+ mutating func step() -> (start: Vertex, end: Vertex)? {
99+ if weights.isEmpty {
100+ return nil
101+ }
102+
103+// let reductions = minimize() // Should minimize on creation
104+
105+ var start: Vertex!
106+ var end: Vertex!
107+ var maxCoef = -1
108+
109+ for row in weights {
110+ for weight in row.value {
111+ if weight.value == .value(0) {
112+ let coef = coeficient(for: weights, start: row.key, end: weight.key)
113+ if coef > maxCoef {
114+ start = row.key
115+ end = weight.key
116+ maxCoef = coef
117+ }
118+ }
119+ }
120+ }
121+
122+ usedVertices.insert(start)
123+ usedVertices.insert(end)
124+
125+ deleteEdge(from: start, to: end)
126+
127+ usedEdges.append((start, end))
128+
129+ for component in components {
130+ for start in component {
131+ for end in component {
132+ weights[start]?[end]? = .infinity
133+ }
134+ }
135+ }
136+
137+ return (start, end)
138+ }
139+
140+ func validate() -> Bool {
141+ var ends: [Vertex: Bool] = weights.mapValues {_ in false} // Valid?
142+ for row in weights.values {
143+ if !row.values.reduce(false, {$1 != .infinity ? true : $0}) {
144+ return false
145+ }
146+ ends.filter({!$0.value}).keys.forEach { key in
147+ if row[key] != .infinity {
148+ ends[key] = true
149+ }
150+ }
151+ }
152+ if ends.values.contains(false) {
153+ return false
154+ }
155+ return true
156+ }
157+
158+ func finalSolution() -> [(start: Vertex, end: Vertex)] {
159+ assert(weights.count == 2)
160+ var found: (start: Vertex, end: Vertex)?
161+ for (start, row) in weights {
162+ for (end, weight) in row {
163+ if weight == .value(0) {
164+ if let found = found {
165+ return [found, (start, end)]
166+ } else {
167+ found = (start, end)
168+ }
169+ }
170+ }
171+ }
172+ fatalError("finalSolution fell through. Most likeley: didn't minimize.")
173+ }
174+
175+ var power: Int {
176+ return weights.count + (weights.first?.value.count ?? 0) -
177+ weights.values.reduce(0) {$0 + $1.values.filter({$0 == .infinity}).count}
178+ }
179+
180+ mutating func exclude(edge: (start: Vertex, end: Vertex)) {
181+ weights[edge.start]![edge.end] = .infinity
182+ }
183+
184+ init(with weights: [Vertex: [Vertex: Weight]], used usedVertices: Set<Vertex>) {
185+ self.weights = weights
186+ self.usedVertices = usedVertices
187+ }
188+}
189+
190+enum Weight: Comparable, CustomStringConvertible {
191+ static func <(lhs: Weight, rhs: Weight) -> Bool {
192+ switch lhs {
193+ case .infinity:
194+ return false
195+ case .value(let lv):
196+ switch rhs {
197+ case .infinity:
198+ return true
199+ case .value(let rv):
200+ return lv < rv
201+ }
202+ }
203+ }
204+
205+ static func ==(lhs: Weight, rhs: Weight) -> Bool {
206+ switch lhs {
207+ case .infinity:
208+ if case .infinity = rhs {
209+ return true
210+ } else {
211+ return false
212+ }
213+ case .value(let lv):
214+ if case let .value(rv) = rhs {
215+ return lv == rv
216+ } else {
217+ return false
218+ }
219+ }
220+ }
221+
222+ case infinity
223+ case value(Int)
224+
225+ var numValue: Int {
226+ switch self {
227+ case .infinity:
228+ return 0
229+ case .value(let v):
230+ return v
231+ }
232+ }
233+
234+ static func -(lhs: Weight, rhs: Weight) -> Weight {
235+ return lhs - rhs.numValue
236+ }
237+ static func -(lhs: Weight, rhs: Int) -> Weight {
238+ if case let .value(v) = lhs {
239+ return .value(v - rhs)
240+ } else {
241+ return .infinity
242+ }
243+ }
244+ static func -=(lhs: inout Weight, rhs: Weight) {
245+ lhs = lhs - rhs
246+ }
247+
248+ var description: String {
249+ switch self {
250+ case .value(let v):
251+ return "\(v)"
252+ case .infinity:
253+ return "∞"
254+ }
255+ }
256+}
257+
258+func coeficient<Vertex>(`for` weights: [Vertex: [Vertex: Weight]], start: Vertex, end: Vertex) -> Int {
259+ return weights[start]!.filter({$0.key != end}).values.min()!.numValue +
260+ weights.filter({$0.key != start}).reduce(Weight.infinity) {min($0, $1.value[end]!)}.numValue
261+
262+}
263+
Galmin/Printer.swiftnew file+213
@@ -0,0 +1,213 @@
1+//
2+// Printer.swift
3+// Galmin
4+//
5+// Created by Ilya Kos on 2/22/18.
6+// Copyright © 2018 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+private extension String {
12+ var sub: String {
13+ return self.map { c in
14+ switch c {
15+ case "0":
16+ return "₀"
17+ case "1":
18+ return "₁"
19+ case "2":
20+ return "₂"
21+ case "3":
22+ return "₃"
23+ case "4":
24+ return "₄"
25+ case "5":
26+ return "₅"
27+ case "6":
28+ return "₆"
29+ case "7":
30+ return "₇"
31+ case "8":
32+ return "₈"
33+ case "9":
34+ return "₉"
35+ case "(":
36+ return "₍"
37+ case ")":
38+ return "₎"
39+ default:
40+ return String(c)
41+ }
42+ }.joined()
43+ }
44+ var sup: String {
45+ return self.map { c in
46+ switch c {
47+ case "0":
48+ return "⁰"
49+ case "1":
50+ return "¹"
51+ case "2":
52+ return "²"
53+ case "3":
54+ return "³"
55+ case "4":
56+ return "⁴"
57+ case "5":
58+ return "⁵"
59+ case "6":
60+ return "⁶"
61+ case "7":
62+ return "⁷"
63+ case "8":
64+ return "⁸"
65+ case "9":
66+ return "⁹"
67+ case "(":
68+ return "⁽"
69+ case ")":
70+ return "⁾"
71+ default:
72+ return String(c)
73+ }
74+ }.joined()
75+ }
76+}
77+
78+class Printer<Vertex> where Vertex: Hashable & Comparable {
79+
80+ private func table(`for` matrix: [Vertex: [Vertex: Weight]], with reductions: (starts: [Vertex: Int], ends: [Vertex: Int]), prefix: String, coef: Bool) -> String {
81+ var out: [[String]] = []
82+ let ends = matrix.values.first!.keys.sorted()
83+ let sEnds = ends.map({"\($0)"})
84+ let row: [String] = ["S" + prefix.sub] + sEnds + (reductions.starts.count > 0 ? ["min"] : [])
85+ out.append(row)
86+ for start in matrix.keys.sorted() {
87+ var nextRow = ["\(start)"] + ends.map {"\(matrix[start]![$0]!)" + (coef && matrix[start]![$0]! == .value(0) ? "\(coeficient(for: matrix, start: start, end: $0))".sup : "")}
88+ if let reduction = reductions.starts[start] {
89+ nextRow.append("\(reduction)")
90+ }
91+ out.append(nextRow)
92+ }
93+ if reductions.ends.count > 0 {
94+ out.append(["min"] + ends.map {reductions.ends[$0] == nil ? "" : "\(reductions.ends[$0]!)"})
95+ }
96+ return out.reduce("") {$0 + $1.reduce("", {"\($0)\($1), "}).dropLast(2) + "\n"}
97+ }
98+
99+ func processMinimizations(_ minimizations: (initial: [Vertex: [Vertex: Weight]], includingTables: Graph<Vertex>.Minimization, excludingTables: Graph<Vertex>.Minimization), prefix: String, counter: Int) -> () {
100+ do {
101+ try table(for: minimizations.initial, with: ([:], [:]), prefix: prefix, coef: true).write(with: "\(counter)_1.csv")
102+ try table(for: minimizations.includingTables.before, with: minimizations.includingTables.reductions, prefix: prefix + "1", coef: false).write(with: "\(counter)_2.csv")
103+ try table(for: minimizations.includingTables.after, with: ([:], [:]), prefix: prefix + "1", coef: false).write(with: "\(counter)_3.csv")
104+ try table(for: minimizations.excludingTables.before, with: minimizations.excludingTables.reductions, prefix: prefix + "0", coef: false).write(with: "\(counter)_4.csv")
105+ try table(for: minimizations.excludingTables.after, with: ([:], [:]), prefix: prefix + "0", coef: false).write(with: "\(counter)_5.csv")
106+ } catch {
107+ print("Couldn't write files ¯\\_(ツ)_/¯")
108+ }
109+ }
110+
111+ private var solutions: FileHandle!
112+
113+ func processInitialMinimization(minimization: Graph<Vertex>.Minimization, counter: Int) -> () {
114+ do {
115+ try FileManager.default.createDirectory(atPath: "diskraDz3", withIntermediateDirectories: true)
116+ FileManager.default.createFile(atPath: "diskraDz3/solutions.txt", contents: nil)
117+ solutions = FileHandle(forWritingAtPath: "diskraDz3/solutions.txt")
118+ try table(for: minimization.before, with: minimization.reductions, prefix: "", coef: false).write(with: "\(counter)_1.csv")
119+ try table(for: minimization.after, with: ([:], [:]), prefix: "", coef: false).write(with: "\(counter)_2.csv")
120+ } catch {
121+ print("Couldn't write files ¯\\_(ツ)_/¯")
122+ }
123+ }
124+
125+ func processTree(tree: Tree<Vertex>, selecting: Tree<Vertex>?, counter: Int) -> () {
126+ var nodeCounter = 0
127+ func process(tree: Tree<Vertex>) -> (declarations: [String], graph: [String], node: Int) {
128+ let node = nodeCounter
129+ nodeCounter += 1
130+ var declarations: [String] = []
131+ var graph: [String] = []
132+ var edge = ""
133+ if let selected = tree.chosenEdge {
134+ edge = "<sub><i>(\(selected.start), \(selected.end))</i></sub>"
135+ }
136+ var solution = ""
137+ if let s = tree.solution {
138+ solution = "<sub><i><u>\(s.map({String(describing: $0)}).joined())</u></i></sub>"
139+ }
140+ declarations.append("\(node) [label=<S\(tree.prefix.sub)<sup><b>\(tree.minWeight)</b></sup>\(edge)\(solution)>\( tree === selecting ? " shape=ellipse" : " shape=box" )]")
141+// declarations.append("\(node) [label=<S\(tree.prefix.sub)<sup>\(tree.minWeight)</sup>\(edge)>\( tree === selecting ? " color=\"red\"" : "" )]")
142+//// declarations.append("\(node) [label=<S<sub>\(tree.prefix.count == 0 ? " " : tree.prefix)</sub><sup>\(tree.minWeight)</sup>\(edge)>\( tree === selecting ? " color=\"red\"" : "" )]")
143+////// declarations.append("\(node) [label=\"S\(tree.prefix.count == 0 ? "" : tree.prefix.sub)\(String(tree.minWeight).sup)\(edge.sub)\"\( tree === selecting ? " color=\"red\"" : "" )]")
144+ if let (including, excluding) = tree.nodes {
145+ let excludingResult = process(tree: excluding)
146+ declarations.append(contentsOf: excludingResult.declarations)
147+ graph.append("\(node) -> \(excludingResult.node)")
148+ graph.append(contentsOf: excludingResult.graph)
149+ let includingResult = process(tree: including)
150+ declarations.append(contentsOf: includingResult.declarations)
151+ graph.append("\(node) -> \(includingResult.node)")
152+ graph.append(contentsOf: includingResult.graph)
153+ }
154+ return (declarations, graph, node)
155+ }
156+ let (declaration, graph, _) = process(tree: tree)
157+ let out = """
158+ digraph {
159+ {
160+ \(declaration.joined(separator: "\n\t"))
161+ }
162+ \(graph.joined(separator: "\n"))
163+ }
164+ """
165+ do {
166+ try out.write(with: "\(counter).gv")
167+ } catch {
168+ print("Couldn't write files ¯\\_(ツ)_/¯")
169+ }
170+ }
171+
172+ func processResult(tree: Tree<Vertex>, counter: Int) -> () {
173+ solutions.seekToEndOfFile()
174+ let text = "Step: \(counter) \tweight = \(tree.minWeight) \tsolution: (\(tree.solution!.map({String(describing: $0)}).joined(separator: ", ")))\n"
175+// print(text)
176+ solutions.write(text.data(using: .utf8)!)
177+ }
178+
179+ func flushResults() {
180+ solutions.truncateFile(atOffset: 0)
181+ }
182+
183+}
184+
185+extension Tree {
186+ var path: [Vertex] {
187+ let graph = self.value
188+ var edges = graph.usedEdges + graph.finalSolution()
189+ var vertices: Set<Vertex> = []
190+ for (start, end) in edges {
191+ vertices.insert(start)
192+ vertices.insert(end)
193+ }
194+ var out: [Vertex] = [vertices.min()!]
195+ edgesLoop: while edges.count > 0 {
196+ for (i, edge) in edges.enumerated() {
197+ if edge.start == out.last! {
198+ out.append(edge.end)
199+ edges.remove(at: i)
200+ continue edgesLoop
201+ }
202+ }
203+ fatalError()
204+ }
205+ return Array(out.dropLast())
206+ }
207+}
208+
209+private extension String {
210+ func write(with name: String) throws {
211+ try self.write(toFile: "diskraDz3/\(name)", atomically: true, encoding: .utf8)
212+ }
213+}
Galmin/Solver.swiftnew file+185
@@ -0,0 +1,185 @@
1+//
2+// Tree.swift
3+// Galmin
4+//
5+// Created by Ilya Kos on 2/17/18.
6+// Copyright © 2018 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+class Solver<Vertex> where Vertex: Hashable & Comparable {
12+
13+ private var tree: Tree<Vertex>
14+
15+ init(weights: [Vertex: [Vertex: Int]]) {
16+ var weights = weights.mapValues {$0.mapValues {Weight.value($0)}}
17+ for key in weights.keys {
18+ weights[key]![key] = .infinity
19+ }
20+ tree = Tree(with: Graph(with: weights, used: [])) // TODO: Need to minimize initial tree
21+
22+ }
23+
24+ var counter = 1
25+
26+ var processInitialMinimization: ((Graph<Vertex>.Minimization, _ counter: Int) -> ())?
27+ var processMinimizations: (((initial: [Vertex: [Vertex: Weight]], includingTables: Graph<Vertex>.Minimization, excludingTables: Graph<Vertex>.Minimization), _ prefix: String ,_ counter: Int) -> ())?
28+ var processTree: ((Tree<Vertex>, _ selecting: Tree<Vertex>?, _ counter: Int) -> ())?
29+ var processResult: ((Tree<Vertex>, _ counter: Int) -> ())?
30+ var flushResults: (() -> ())?
31+
32+ private var solutions: [Tree<Vertex>] = []
33+
34+
35+ func start() {
36+ solutions = []
37+
38+ var root = tree.value
39+
40+ let minimization = root.minimize()
41+
42+ tree = Tree(with: root)
43+
44+ processInitialMinimization?(minimization, counter)
45+// processTree?(tree, tree, counter)
46+ var minResult: Int?
47+
48+ largeLoop: while true {
49+ counter += 1
50+
51+ let next = tree.leaves.lazy.filter({!self.solutions.contains($0)}).min()!
52+// print(next.minWeight)
53+ processTree?(tree, next, counter)
54+ let (minimizations, result, excludeResults) = next.step()
55+ processMinimizations?(minimizations, next.prefix, counter)
56+
57+ solutions.append(contentsOf: excludeResults)
58+
59+ if let result = result {
60+// print("\t\t\tFOUND: \(result.minWeight)")
61+ if minResult == nil {
62+ minResult = result.minWeight
63+ } else {
64+ if minResult! > result.minWeight {
65+ flushResults?()
66+ minResult = result.minWeight
67+ }
68+ }
69+ if result.minWeight > minResult! {
70+ solutions.append(result)
71+ continue
72+ }
73+ processResult?(result, counter)
74+ solutions.append(result)
75+ }
76+ if let minResult = minResult {
77+ for leaf in tree.leaves.lazy.filter({!self.solutions.contains($0)}) {
78+// print("\t\(leaf.minWeight)")
79+ if leaf.minWeight <= minResult {
80+ continue largeLoop
81+ }
82+ }
83+ processTree?(tree, nil, counter+1)
84+ break
85+ }
86+ }
87+ }
88+
89+// private func step() {
90+// let next = tree.leaves.min()!
91+// let minimizations = next.step()
92+// processMinimizations?(minimizations)
93+// processTree?(tree)
94+// }
95+}
96+
97+
98+class Tree<Vertex>: Comparable where Vertex: Hashable & Comparable {
99+ let value: Graph<Vertex>
100+ var nodes: (including: Tree<Vertex>, excluding: Tree<Vertex>)?
101+ var chosenEdge: (start: Vertex, end: Vertex)?
102+ var prefix = ""
103+ var solution: [Vertex]?
104+
105+ /// Steps the graph
106+ init(with graph: Graph<Vertex>) {
107+ self.value = graph
108+ }
109+
110+ var leaves: [Tree<Vertex>] {
111+ if let (including, excluding) = nodes {
112+ return including.leaves + excluding.leaves
113+ } else {
114+ return [self]
115+ }
116+ }
117+
118+ var minWeight: Int {
119+ return value.minWeight
120+ }
121+
122+ var power: Int {
123+ return value.power
124+ }
125+
126+
127+ func step() -> ((initial: [Vertex: [Vertex: Weight]],
128+ includingTables: Graph<Vertex>.Minimization,
129+ excludingTables: Graph<Vertex>.Minimization),
130+ result: Tree<Vertex>?,
131+ excludeResults: [Tree<Vertex>]) {
132+ assert(nodes == nil)
133+ var including = value
134+ chosenEdge = including.step()
135+ var excluding = value
136+ excluding.exclude(edge: chosenEdge!)
137+ let includingTables = including.minimize()
138+ let excludingTables = excluding.minimize()
139+
140+
141+ let includingTree = Tree(with: including)
142+ includingTree.prefix = prefix + "1"
143+ let excludingTree = Tree(with: excluding)
144+ excludingTree.prefix = prefix + "0"
145+
146+ var excludeResults: [Tree<Vertex>] = []
147+
148+ if !including.validate() {
149+ excludeResults.append(includingTree)
150+ }
151+ if !excluding.validate() {
152+ excludeResults.append(excludingTree)
153+ }
154+
155+ nodes = (including: includingTree, excluding: excludingTree)
156+
157+ var includingResult: Tree<Vertex>?
158+
159+ if including.graphWeights.count == 2 && including.validate() {
160+ includingResult = nodes?.including
161+ includingResult?.solution = includingResult!.path
162+ }
163+
164+ return ((value.graphWeights, includingTables, excludingTables), includingResult, excludeResults)
165+ }
166+
167+ // MARK: Comparable
168+ static func <(lhs: Tree<Vertex>, rhs: Tree<Vertex>) -> Bool {
169+ if lhs.minWeight < rhs.minWeight {
170+ return true
171+ } else if lhs.minWeight > rhs.minWeight {
172+ return false
173+ } else {
174+ if lhs.power < rhs.power {
175+ return true
176+ } else {
177+ return false
178+ }
179+ }
180+ }
181+
182+ static func ==(lhs: Tree<Vertex>, rhs: Tree<Vertex>) -> Bool {
183+ return lhs.minWeight == rhs.minWeight && lhs.power == rhs.power
184+ }
185+}
Galmin/main.swiftnew file+63
@@ -0,0 +1,63 @@
1+//
2+// main.swift
3+// Galmin
4+//
5+// Created by Ilya Kos on 2/17/18.
6+// Copyright © 2018 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+let printer = Printer<Int>()
12+
13+print("Enter your values in the folowing format:")
14+print("(x0, y0): 1 2")
15+
16+var inputs: [Int: (x: Int, y: Int)] = [:]
17+
18+func iter(i: Int) {
19+ if let next = readLine(strippingNewline: true)?.split(separator: " ", omittingEmptySubsequences: true).flatMap({Int($0)}),
20+ next.count == 2,
21+ let x = next.first,
22+ let y = next.last {
23+ inputs[i] = (x, y)
24+ } else {
25+ print("nope.")
26+ iter(i: i)
27+ }
28+}
29+
30+for i in 1...6 {
31+ print("(x\(i), y\(i)): ", terminator: "")
32+ iter(i: i)
33+}
34+
35+//inputs = [1: (4, 1), 2: (4, 3), 3: (2, 7), 4: (9, 6), 5: (10, 7), 6: (6, 10)]
36+
37+var weights: [Int: [Int: Int]] = [:]
38+
39+for (start, sVal) in inputs {
40+ weights[start] = [:]
41+ for (end, eVal) in inputs {
42+ weights[start]![end] = abs(sVal.x - eVal.x) + abs(sVal.y - eVal.y)
43+ }
44+}
45+
46+let solver = Solver<Int>(weights: weights)
47+
48+solver.processInitialMinimization = printer.processInitialMinimization
49+solver.processMinimizations = printer.processMinimizations
50+solver.processResult = printer.processResult
51+solver.processTree = printer.processTree
52+solver.flushResults = printer.flushResults
53+
54+solver.start()
55+
56+print("""
57+
58++-------+
59+| i l y |
60+| a k o |
61+| o o 0 |
62++-------+
63+""")