class RotorMachine::Reflector

Implementation of the Reflector rotor.

A {Reflector} behaves similarly to a {RotorMachine::Rotor Rotor}, except that the {Reflector} did not rotate. Its purpose is to reflect the signal path back through the rotor stack in the opposite direction, thereby ensuring that the encryption algorithm is symmetric.

The module defines constants for the standard German Enigma reflectors, but you can create a reflector with any string of 26 alphabetic characters. However, you may not repeat a given letter more than once in your string, or else the symmetry of the encipherment algorithm will be broken.

Constants

ALPHABET

A letter mapping for a passthrough reflector; also used by the RSpec tests

REFLECTOR_A

The letter mapping for the German “A” reflector.

REFLECTOR_B

The letter mapping for the German “B” reflector.

REFLECTOR_B_THIN

The letter mapping for the German “B Thin” reflector.

REFLECTOR_C

The letter mapping for the German “C” reflector.

REFLECTOR_C_THIN

The letter mapping for the German “C Thin” reflector.

REFLECTOR_ETW

The letter mapping for the German “ETW” reflector.

Attributes

position[R]

Allow querying the numeric position (ie, the initial position) of the reflector.

Public Class Methods

new(selected_reflector, start_position = 0) click to toggle source

Initialize a new {Reflector}.

@param selected_reflector [String] The character sequqnece for the

reflector. You can use one of the class constants which define
the standard German reflectors, or pass a custom sequence of
26 letters.

@param start_position [Integer] The start position of the reflector.

Because the reflector does not rotate, this is essentially just
an additional permutation factor for the encipherment.
# File lib/rotor_machine/reflector.rb, line 60
def initialize(selected_reflector, start_position = 0)
  raise ArgumentError, "Initialization string contains duplicate letters" unless selected_reflector.is_uniq?

  @letters = selected_reflector.chars.freeze
  @alphabet = ALPHABET.chars.freeze
  @position = start_position
end

Public Instance Methods

==(another_reflector) click to toggle source

Compare this {RotorMachine::Reflector} to another one.

Returns True if the configuration of the supplied {RotorMachine::Reflector} matches this one, false otherwise.

@param another_reflector [RotorMachine::Reflector] The Reflector to compare to this one. @return [Boolean] True if the configurations match, false otherwise.

# File lib/rotor_machine/reflector.rb, line 154
def ==(another_reflector)
  self.letters == another_reflector.letters &&
  self.position == another_reflector.position
end
current_letter() click to toggle source

Get the current letter position of the rotor.

@return [String] The current letter position of the rotor.

# File lib/rotor_machine/reflector.rb, line 133
def current_letter
  @letters[self.position]
end
letters() click to toggle source

Return the sequence of letters on the reflector.

@return [String] The sequence of letters on the reflector.

# File lib/rotor_machine/reflector.rb, line 125
def letters
  @letters.join("")
end
position=(pos) click to toggle source

Set the position of the {Rotor}.

If a numeric position is provided, an {ArgumentError} will be raised if the position is outside the bounds of the rotor. If an alphabetic position is provided, an {ArgumentError} will be raised if the supplied character is not a character represented on the rotor.

@param pos [Numeric, String] The position of the rotor.

# File lib/rotor_machine/reflector.rb, line 77
def position=(pos)
  if pos.class.to_s == "String"
    raise ArgumentError, "#{pos[0]} is not a character on the rotor" unless @letters.include?(pos[0])
    @position = @letters.index(pos[0])
  elsif pos.class.to_s == "Integer"
    raise ArgumentError, "Position #{pos} is invalid" if (pos < 0 or pos > @letters.length)
    @position = pos
  else
    raise ArgumentError, "Invalid argument to position= (#{pos.class.to_s})"
  end
end
reflect(input) click to toggle source

Feed a sequence of characters through the reflector, and return the results.

Any characters which are not present on the reflector will be passed through unchanged.

@param input [String] The string of characters to encipher. @return [String] The results of passing the input string through the

{Reflector}.
# File lib/rotor_machine/reflector.rb, line 99
def reflect(input)
  input.upcase.chars.each.collect { |c|
    if @alphabet.include?(c) then
      @letters[(@alphabet.index(c) + @position) % @alphabet.length]
    else
      c
    end }.join("")
end
reflector_kind_name() click to toggle source

Return the reflector kind.

If the {Reflector} is initialized with one of the provided rotor type constants (such as {REFLECTOR_A}), the name of the reflector will be returned as a symbol. If not, the symbol `:CUSTOM` will be returned..

@return [Symbol] The kind of this {Reflector} object.

# File lib/rotor_machine/reflector.rb, line 116
def reflector_kind_name
  self.class.constants.each { |r| return r if (@letters.join("") == self.class.const_get(r)) }
  return :CUSTOM
end
to_s() click to toggle source

Return a human-readable representation of the {Reflector}

@return [String] A description of the Reflector.

# File lib/rotor_machine/reflector.rb, line 141
def to_s
  "a RotorMachine::Reflector of type '#{self.reflector_kind_name.to_s}'"
end