From bb1ba6a1fc69e4e2efe3fb8cdc97fa7d166bea0e Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 18 Jul 2015 23:00:26 -0700 Subject: [PATCH] Rename Rotor.swift -> Components.swift --- Enigma.xcodeproj/project.pbxproj | 8 +-- Enigma/Components.swift | 104 +++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 Enigma/Components.swift diff --git a/Enigma.xcodeproj/project.pbxproj b/Enigma.xcodeproj/project.pbxproj index 6e883ae..346c2a7 100644 --- a/Enigma.xcodeproj/project.pbxproj +++ b/Enigma.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ C0DA3A9B1B5AACB300D8D68E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C0DA3A991B5AACB300D8D68E /* LaunchScreen.storyboard */; }; C0DA3AA61B5AACB300D8D68E /* EnigmaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DA3AA51B5AACB300D8D68E /* EnigmaTests.swift */; }; C0DA3AB11B5AACB300D8D68E /* EnigmaUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DA3AB01B5AACB300D8D68E /* EnigmaUITests.swift */; }; - C0DA3ABF1B5AB49200D8D68E /* Rotor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DA3ABE1B5AB49200D8D68E /* Rotor.swift */; }; + C0DA3ABF1B5AB49200D8D68E /* Components.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DA3ABE1B5AB49200D8D68E /* Components.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -50,7 +50,7 @@ C0DA3AAC1B5AACB300D8D68E /* EnigmaUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EnigmaUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C0DA3AB01B5AACB300D8D68E /* EnigmaUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnigmaUITests.swift; sourceTree = ""; }; C0DA3AB21B5AACB300D8D68E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C0DA3ABE1B5AB49200D8D68E /* Rotor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rotor.swift; sourceTree = ""; }; + C0DA3ABE1B5AB49200D8D68E /* Components.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Components.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -107,7 +107,7 @@ C0DA3A971B5AACB300D8D68E /* Assets.xcassets */, C0DA3A991B5AACB300D8D68E /* LaunchScreen.storyboard */, C0DA3A9C1B5AACB300D8D68E /* Info.plist */, - C0DA3ABE1B5AB49200D8D68E /* Rotor.swift */, + C0DA3ABE1B5AB49200D8D68E /* Components.swift */, C04D337E1B5B3F6100E2888E /* Enigma.swift */, ); path = Enigma; @@ -262,7 +262,7 @@ buildActionMask = 2147483647; files = ( C04D337F1B5B3F6100E2888E /* Enigma.swift in Sources */, - C0DA3ABF1B5AB49200D8D68E /* Rotor.swift in Sources */, + C0DA3ABF1B5AB49200D8D68E /* Components.swift in Sources */, C0DA3A931B5AACB300D8D68E /* ViewController.swift in Sources */, C0DA3A911B5AACB300D8D68E /* AppDelegate.swift in Sources */, ); diff --git a/Enigma/Components.swift b/Enigma/Components.swift new file mode 100644 index 0000000..436fbed --- /dev/null +++ b/Enigma/Components.swift @@ -0,0 +1,104 @@ +// +// Components.swift +// Enigma +// +// Created by Eryn Wells on 2015-07-18. +// Copyright © 2015 Eryn Wells. All rights reserved. +// + +import Foundation + + +enum EncoderError: ErrorType { + /** Thrown when an input character is found that is not in the alphabet. */ + case InvalidCharacter(ch: Character) +} + + +protocol Encoder { + func encode(c: Character) throws -> Character +} + + +class Cryptor { + /** Array of all possible characters to encrypt. */ + static let alphabet: [Character] = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters) +} + + +class FixedRotor: Cryptor, Encoder { + enum Error: ErrorType { + /** Thrown when the initializer is given an invalid series. */ + case InvalidSeries + } + + /** The series of characters that this rotor cycles through. */ + let series: [Character]! + + convenience init(series: String) throws { + try self.init(series: Array(series.characters)) + } + + init(series: [Character]) throws { + self.series = series + super.init() + guard series.count == Cryptor.alphabet.count else { + throw Error.InvalidSeries + } + } + + func encode(c: Character) throws -> Character { + if let offset = FixedRotor.alphabet.indexOf(c) { + return series[offset] + } else { + throw EncoderError.InvalidCharacter(ch: c) + } + } +} + + +/** Rotors are FixedRotors that have a variable position, which offsets the alphabet from the series and changes which character is substituted for a given input. */ +class Rotor: FixedRotor { + /** The position of first letter in `series` in the `alphabet`. */ + var position: Int = 0 { + willSet { + self.position = newValue % Cryptor.alphabet.count + } + } + + func advance(count: Int = 1) { + position = (position + count) % Rotor.alphabet.count + } + + override func encode(c: Character) throws -> Character { + if let offset = Rotor.alphabet.indexOf(c) { + return series[(offset + position) % series.count] + } else { + throw EncoderError.InvalidCharacter(ch: c) + } + } +} + + +class Reflector: FixedRotor { } + + +/** A Plugboard is a Cryptor that substitutes one character for another based on a set of pairs. A pair of characters is mutually exclusive of other pairs; that is, a character can only belong to one pair. Furthermore, the Plugboard always trades one character for the same character and vice versa. */ +class Plugboard: Cryptor, Encoder { + private(set) var plugs: [Character: Character] = [:] + + func addPlug(a: Character, b: Character) { + plugs[a] = b + plugs[b] = a + } + + func encode(c: Character) throws -> Character { + if let output = plugs[c] { + return output + } else if Plugboard.alphabet.contains(c) { + return c + } else { + throw EncoderError.InvalidCharacter(ch: c) + } + } +} \ No newline at end of file