on master help sign in
8 files changed+427−2
since splitsnapshots
unifiedsplit
.gitignore+67 README.md+37 Truther.xcodeproj/project.pbxproj+14 Truther.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/xcschememanagement.plist+8 Truther/Formatter.swift+39 Truther/Operator.swift+94 Truther/Truther.swift+159 Truther/main.swift+9−2
.gitignorenew file+67
@@ -0,0 +1,67 @@
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+.build/
41+
42+# CocoaPods
43+#
44+# We recommend against adding the Pods directory to your .gitignore. However
45+# you should judge for yourself, the pros and cons are mentioned at:
46+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
47+#
48+# Pods/
49+
50+# Carthage
51+#
52+# Add this line if you want to avoid checking in source code from Carthage dependencies.
53+# Carthage/Checkouts
54+
55+Carthage/Build
56+
57+# fastlane
58+#
59+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
60+# screenshots whenever they are needed.
61+# For more information about the recommended setup visit:
62+# https://docs.fastlane.tools/best-practices/source-control/#source-control
63+
64+fastlane/report.xml
65+fastlane/Preview.html
66+fastlane/screenshots
67+fastlane/test_output
README.mdnew file+37
@@ -0,0 +1,37 @@
1+# Truther
2+
3+Makes a truth table for the given boolean function
4+
5+*All operators have equal precedence and are left-associative*
6+
7+## Usage
8+
9+```
10+truther "a * c (+) b"
11+```
12+
13+```
14+a | b | c | F
15+-------------
16+0 | 0 | 0 | 0
17+0 | 0 | 1 | 0
18+0 | 1 | 0 | 1
19+0 | 1 | 1 | 1
20+1 | 0 | 0 | 0
21+1 | 0 | 1 | 1
22+1 | 1 | 0 | 1
23+1 | 1 | 1 | 0
24+```
25+
26+## Available functions
27+
28+- `+` `or`
29+- `(+)` `xor` `/=`
30+- `*` `and` `&`
31+- `=>`
32+- `=/>`
33+- `<=`
34+- `</=`
35+- `o` `nor`
36+- `=` `<=>`
37+- `|` `nand`
Truther.xcodeproj/project.pbxproj+14
@@ -7,7 +7,10 @@
77 objects = {
88
99/* Begin PBXBuildFile section */
10+ 091685E31F8834C800CC8645 /* Formatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 091685E21F8834C800CC8645 /* Formatter.swift */; };
1011 097824FA1F87E21400064ADB /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097824F91F87E21400064ADB /* main.swift */; };
12+ 097825011F87E23000064ADB /* Truther.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097825001F87E23000064ADB /* Truther.swift */; };
13+ 097825031F87E48500064ADB /* Operator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097825021F87E48500064ADB /* Operator.swift */; };
1114/* End PBXBuildFile section */
1215
1316/* Begin PBXCopyFilesBuildPhase section */
@@ -23,8 +26,12 @@
2326/* End PBXCopyFilesBuildPhase section */
2427
2528/* Begin PBXFileReference section */
29+ 0915F8131F8907DB00D96279 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
30+ 091685E21F8834C800CC8645 /* Formatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Formatter.swift; sourceTree = "<group>"; };
2631 097824F61F87E21400064ADB /* Truther */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Truther; sourceTree = BUILT_PRODUCTS_DIR; };
2732 097824F91F87E21400064ADB /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
33+ 097825001F87E23000064ADB /* Truther.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Truther.swift; sourceTree = "<group>"; };
34+ 097825021F87E48500064ADB /* Operator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operator.swift; sourceTree = "<group>"; };
2835/* End PBXFileReference section */
2936
3037/* Begin PBXFrameworksBuildPhase section */
@@ -41,6 +48,7 @@
4148 097824ED1F87E21400064ADB = {
4249 isa = PBXGroup;
4350 children = (
51+ 0915F8131F8907DB00D96279 /* README.md */,
4452 097824F81F87E21400064ADB /* Truther */,
4553 097824F71F87E21400064ADB /* Products */,
4654 );
@@ -58,6 +66,9 @@
5866 isa = PBXGroup;
5967 children = (
6068 097824F91F87E21400064ADB /* main.swift */,
69+ 097825001F87E23000064ADB /* Truther.swift */,
70+ 091685E21F8834C800CC8645 /* Formatter.swift */,
71+ 097825021F87E48500064ADB /* Operator.swift */,
6172 );
6273 path = Truther;
6374 sourceTree = "<group>";
@@ -120,6 +131,9 @@
120131 isa = PBXSourcesBuildPhase;
121132 buildActionMask = 2147483647;
122133 files = (
134+ 097825011F87E23000064ADB /* Truther.swift in Sources */,
135+ 097825031F87E48500064ADB /* Operator.swift in Sources */,
136+ 091685E31F8834C800CC8645 /* Formatter.swift in Sources */,
123137 097824FA1F87E21400064ADB /* main.swift in Sources */,
124138 );
125139 runOnlyForDeploymentPostprocessing = 0;
Truther.xcodeproj/xcuserdata/ilya.xcuserdatad/xcschemes/xcschememanagement.plist+8
@@ -10,5 +10,13 @@
1010 <integer>0</integer>
1111 </dict>
1212 </dict>
13+ <key>SuppressBuildableAutocreation</key>
14+ <dict>
15+ <key>097824F51F87E21400064ADB</key>
16+ <dict>
17+ <key>primary</key>
18+ <true/>
19+ </dict>
20+ </dict>
1321</dict>
1422</plist>
Truther/Formatter.swiftnew file+39
@@ -0,0 +1,39 @@
1+//
2+// Formatter.swift
3+// Truther
4+//
5+// Created by Ilya Kos on 10/7/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+private let seperator = " | "
12+
13+class Formatter {
14+ static func format(table: [[String]]) -> String {
15+ var lengths: [Int] = []
16+ for i in 0..<table[0].count {
17+ var maxLength = 0
18+ for row in table {
19+ maxLength = max(maxLength, row[i].count)
20+ }
21+ lengths.append(maxLength)
22+ }
23+ var out = ""
24+ for i in 0..<table.count {
25+ let row = table[i]
26+ for u in 0..<row.count {
27+ out.append(row[u].padding(toLength: lengths[u], withPad: " ", startingAt: 0))
28+ out.append(seperator)
29+ }
30+ out = String(out.dropLast(seperator.count))
31+ out.append("\n")
32+ if i == 0 {
33+ out.append(String.init(repeating: "-", count: lengths.reduce(0, +) + seperator.count * (lengths.count - 1)))
34+ out.append("\n")
35+ }
36+ }
37+ return out
38+ }
39+}
Truther/Operator.swiftnew file+94
@@ -0,0 +1,94 @@
1+//
2+// Operator.swift
3+// Truther
4+//
5+// Created by Ilya Kos on 10/6/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+enum Operator {
12+ case f0
13+ case f1
14+ case f2
15+ case f3
16+ case f4
17+ case f5
18+ case f6
19+ case f7
20+ case f8
21+ case f9
22+ case f10
23+ case f11
24+ case f12
25+ case f13
26+ case f14
27+ case f15
28+ var value: ((Bool, Bool) -> (Bool)) {
29+ switch self {
30+ case .f0:
31+ return {_, _ in false}
32+ case .f1:
33+ return {$0 && $1}
34+ case .f2:
35+ return { $0 && !$1 }
36+ case .f3:
37+ return {a, _ in a}
38+ case .f4:
39+ return {!$0 && $1}
40+ case .f5:
41+ return {_, b in b }
42+ case .f6:
43+ return {!($0 == $1)}
44+ case .f7:
45+ return {$0 || $1}
46+ case .f8:
47+ return {!($0 || $1)}
48+ case .f9:
49+ return {$0 == $1}
50+ case .f10:
51+ return {_, b in !b}
52+ case .f11:
53+ return {!(!$0 && $1)}
54+ case .f12:
55+ return {a, _ in !a}
56+ case .f13:
57+ return {!($0 && !$1)}
58+ case .f14:
59+ return {!($0 && $1)}
60+ case .f15:
61+ return {_, _ in true}
62+ }
63+ }
64+ static func process(_ op: Substring) -> Operator? {
65+ switch op.trimmingCharacters(in: .whitespaces).lowercased() {
66+ case "+", "or", "||":
67+ return .f7
68+ case "(+)", "⊕", "xor", "!=", "=!=", "=/=", "/=":
69+ return .f6
70+ case "*", "and", "&", "&&", "⚫︎":
71+ return .f1
72+ case "1":
73+ return .f15
74+ case "0":
75+ return .f0
76+ case "=/>", "=/=>", "-/>", "-/->", "!=>", "!->", "=!>", "-!>", "=!=>", "-!->":
77+ return .f2
78+ case "</=", "<=/=", "</-", "<-/-", "!<=", "!<-", "<!=", "<!-", "<=!=", "<-!-":
79+ return .f4
80+ case "o", "O", "о", "О", "◯", "⚬", "𐩒", "⚪︎", "nor", "web":
81+ return .f8
82+ case "=", "<=>", "==":
83+ return .f9
84+ case "<=", "<-", "<==", "<--":
85+ return .f11
86+ case "=>", "->", "==>", "-->":
87+ return .f13
88+ case "|", "nand", "shif":
89+ return .f14
90+ default:
91+ return nil
92+ }
93+ }
94+}
Truther/Truther.swiftnew file+159
@@ -0,0 +1,159 @@
1+//
2+// Truther.swift
3+// Truther
4+//
5+// Created by Ilya Kos on 10/6/17.
6+// Copyright © 2017 Ilya Kos. All rights reserved.
7+//
8+
9+import Foundation
10+
11+struct Truther {
12+ var formula: Formula
13+ func run() -> [(inp: [Bool], result: Bool)] {
14+ var out: [(inp: [Bool], result: Bool)] = []
15+ for i in 0..<Int(pow(2, Double(formula.variables.count))) {
16+ var t = 0
17+ var inp: [Bool] = []
18+ for _ in formula.variables {
19+ inp.append((UInt(i)>>t) & 1 == 1)
20+ t += 1
21+ }
22+ inp = inp.reversed()
23+ out.append((inp, formula.run(with: inp)))
24+ }
25+ return out
26+ }
27+}
28+
29+struct Formula {
30+ fileprivate func run(with inputs: [Bool]) -> Bool {
31+ var ins: [Variable: Bool] = [:]
32+ for (v, va) in zip(variables, inputs) {
33+ ins[v] = va
34+ }
35+ return value.evaluate(with: ins)
36+ }
37+ init(_ f: String) {
38+ var vars: Set<String> = Set()
39+ func process(_ s: Substring, startingLeft: FormulaElement? = nil) -> FormulaElement {
40+ if s.isEmpty {
41+ guard let startingLeft = startingLeft else {
42+ print("Internal error: Empty input")
43+ exit(1)
44+ }
45+ return startingLeft
46+ }
47+ var ns = s.trimmingCharacters(in: .whitespaces)
48+ if ns == "0" {
49+ return .constant(false)
50+ }
51+ if ns == "1" {
52+ return .constant(true)
53+ }
54+ if ns.trimmingCharacters(in: .whitespaces).reduce(true, {$0 && CharacterSet.letters.contains($1.unicodeScalars.first!)}) {
55+ vars.insert(String(s))
56+ return .variable(String(s))
57+ }
58+ func extractVar() -> Substring {
59+ if ns.first == "(" {
60+ let parts = extractBrackets(from: ns)
61+ ns = parts.after.trimmingCharacters(in: .whitespaces)
62+ return Substring(parts.wasInside.trimmingCharacters(in: .whitespaces))
63+ } else {
64+ if CharacterSet.whitespaces.contains((ns.first?.unicodeScalars.first)!) {
65+ print("Internal error: weird variable name first charecter \(ns.first!)")
66+ exit(1)
67+ }
68+ switch ns.first! {
69+ case "0":
70+ ns = ns.dropFirst().trimmingCharacters(in: .whitespaces)
71+ return "0"
72+ case "1":
73+ ns = ns.dropFirst().trimmingCharacters(in: .whitespaces)
74+ return "1"
75+ default:
76+ break
77+ }
78+ var i = ns.startIndex
79+ while i < ns.endIndex {
80+ if !CharacterSet.letters.contains(ns[i].unicodeScalars.first!) {
81+ break
82+ }
83+ i = ns.index(after: i)
84+ }
85+ let out = ns[..<i]
86+ ns = ns[i...].trimmingCharacters(in: .whitespaces)
87+ return out
88+ }
89+ }
90+ let left = startingLeft ?? process(extractVar())
91+ ns = ns.trimmingCharacters(in: .whitespaces)
92+ let isLetterOrNot = CharacterSet.letters.contains((ns.first?.unicodeScalars.first)!)
93+ var i = ns.startIndex
94+ while i < ns.endIndex {
95+ if CharacterSet.letters.contains(ns[i].unicodeScalars.first!) != isLetterOrNot || ns[i] == " " {
96+ break
97+ }
98+ i = ns.index(after: i)
99+ }
100+ guard let op = Operator.process(ns[..<i]) else {
101+ print("No such operator \(ns[..<i])")
102+ exit(1)
103+ }
104+ ns = ns[i...].trimmingCharacters(in: .whitespaces)
105+ let right = process(extractVar())
106+ return process(Substring(ns), startingLeft: .operation(left, op, right))
107+ }
108+ self.value = process(Substring(f))
109+ self.variables = vars.sorted()
110+ }
111+ let variables: [String]
112+ private let value: FormulaElement
113+}
114+
115+private func extractBrackets(from s: String) -> (wasInside: Substring, after: Substring) {
116+ var i = s.startIndex
117+ var brackets = 0
118+ while i < s.endIndex {
119+ switch s[i] {
120+ case "(":
121+ brackets += 1
122+ case ")":
123+ brackets -= 1
124+ default:
125+ break
126+ }
127+ if brackets == 0 {
128+ break
129+ }
130+ i = s.index(after: i)
131+ }
132+ if brackets != 0 {
133+ print("Mismatched brackets")
134+ exit(1)
135+ }
136+ return (s[s.index(after: s.startIndex)..<i], s[s.index(after: i)...])
137+}
138+
139+private typealias Variable = String
140+private indirect enum FormulaElement {
141+ case variable(Variable)
142+ case constant(Bool)
143+ case operation(FormulaElement, Operator, FormulaElement)
144+ func evaluate(with variables: [Variable: Bool]) -> Bool {
145+ switch self {
146+ case .variable(let v):
147+ if let va = variables[v] {
148+ return va
149+ } else {
150+ print("Internal error: didn't initialize all variables")
151+ exit(1)
152+ }
153+ case .operation(let l, let op, let r):
154+ return op.value(l.evaluate(with: variables), r.evaluate(with: variables))
155+ case .constant(let b):
156+ return b
157+ }
158+ }
159+}
Truther/main.swift+9−2
@@ -8,5 +8,12 @@
88
99import Foundation
1010
11−print("Hello, World!")
12−
11+if CommandLine.arguments.count > 1 {
12+ let form = Formula.init(CommandLine.arguments[1])
13+ let table = Truther.init(formula: form).run().map { $0.0 + [$0.1] }
14+ .map {$0.map {$0 ? "1" : "0"}}
15+
16+ print(Formatter.format(table: [form.variables + ["F"]] + table))
17+} else {
18+ print("You did't supply a formula")
19+}