enigma-machine/Enigma/Components.swift

104 lines
No EOL
2.9 KiB
Swift

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