CS 3110 Functional Programming

back to projects
    (* CS3110 A1 : Enigma *)
    (* Reference string of the standard alphabet to determine index. *)
    let id       = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"


    (* [simulate] takes the same inputs as [cipher] but prints
     * a simulation of the Enigma machine at each step of the
     * computation.
     *)
    let simulate (refl:string) (rotors:string list) (notches:char list)
                 (starts:char list) (s:string) : unit
    =
      print_string "I'm pretty sure it's correct :P and I really don't
                    have time to implement this......\n"


    (* Map input index to output index according to rotor internal wiring.
     * The encryption direction is from right to left and left to right.
     *)
    let rotor_RtoL (rotor:string)(start:int)(input:int) : int
    =
    ((26-start)+(String.index id (String.get rotor ((start+input) mod 26)))) mod 26

    let rotor_LtoR (rotor:string)(start:int)(input:int) : int
    =
    ((26-start)+(String.index rotor (String.get id ((start+input) mod 26)))) mod 26

    (* Reflector function, map input index to output index according to wiring *)
    let reflect (refl:string)(start:int)(input:int) : int
    =
    ((26-start)+(String.index refl (String.get id ((start+input) mod 26)))) mod 26


    (* The two functions that use rotor_RtoL and rotor_LtoR as helper functions.
     * Takes in list of rotor wirings as they are positioned in the machine.
     * using starting position of each rotor to determine the actual mapping
     * of characters across all the motors install from right to left and back
     *)
    let rec encrypt_RtoL (rotors:string list) (starts:int list) (input:int) : int
    =
      match rotors,starts with
      | [],[] -> input
      | h1::t1,h2::t2 -> rotor_RtoL h1 h2 (encrypt_RtoL t1 t2 input)
      | [],_ -> failwith"rotorList shorter than startListRL"
      | _,[] -> failwith"startList shorter than rotorListRL"

    let rec encrypt_LtoR (rotors:string list) (starts:int list) (input:int) : int
    =
      match rotors,starts with
      | [],[] -> input
      | h1::t1,h2::t2 -> encrypt_LtoR t1 t2 (rotor_LtoR h1 h2 input)
      | [],_ -> failwith"rotorList shorter than startListLR"
      | _,[] -> failwith"startList shorter than rotorListLR"


    (* This function uses the two directional encryption helper function as well as
     * the reflect function to encrypt one letter according to the machine setting.
     *)
    let index_encrypt (rotors:string list) (refl:string)
                      (input:int) (starts:int list) : int
    =
    encrypt_LtoR rotors starts (
      reflect refl 0 (
        encrypt_RtoL rotors starts input))


    (* The rev_stepping and stepping functions handle the stepping of the rotors
     * after a key is pressed.
     *)
    let rec rev_stepping (rev_starts:int list) (rev_notchs:int list)
                     (doStep:bool) : int list
    =
      match rev_starts,rev_notchs with
      | [],[] -> []
      | h1::t1,h2::t2 ->
          if doStep then
            ((h1+1) mod 26)::(rev_stepping t1 t2 (h1=h2))
          else
          if List.length t1 = 0 then
            (h1)::(rev_stepping t1 t2 (h1=h2))
          else
          if h1=h2 then
            ((h1+1) mod 26)::(rev_stepping t1 t2 (h1=h2))
          else
            (h1)::(rev_stepping t1 t2 (h1=h2))
      | [],_ -> failwith"startList shorter than notchList"
      | _,[] -> failwith"notchList shorter than startList"

    let stepping (starts:int list) (notchs:int list) : int list
    =
    List.rev (rev_stepping (List.rev starts) (List.rev notchs) true)


    (* This function adapts the index_encrypt function to allow direct character
     * input/output as opposed to character index input/output
     *)
    let char_encrypt (rotors:string list) (refl:string)
                     (input:char) (starts:int list) : char
    =
    String.get id (index_encrypt rotors refl (String.index id input) starts)


    (* Two simple helper functions that converts list of characters to list of
     * integers and vice versa.
     *)
    let rec charListToIntList (inputs:char list) : int list
    =
      match inputs with
      |[] -> []
      |h::t -> (String.index id h)::(charListToIntList t)

    let rec intListToCharList (inputs:int list) : char list
    =
    match inputs with
      |[] -> []
      |h::t -> (String.get id h)::(intListToCharList t)


    (* [cipher refl rotors notches starts s] computes the Enigma cipher, where
     *  - [refl] is the wiring of the reflector,
     *  - [rotors] is a list of the rotors (which must contain at least
     *      one element), as they are installed from left to right on
     *      the spindle,
     *  - [notches] is a list of where the notch is on each rotor,
     *      where the nth character of [notches] is the location of
     *      the notch on the nth rotor in [rotors],
     *  - [starts] is a list of the starting character for each rotor
     *      as positioned on the spindle, where the nth character of
     *      [starts] is the starting character for the nth rotor in
     *      [rotors].
     *  - [s] is the string to be ciphered.
     *)
    let rec cipher (refl:string) (rotors:string list) (notches:char list)
                   (starts:char list) (s:string) : string
    =
    let new_list = stepping (charListToIntList starts) (charListToIntList notches) in
    if String.get s 0 = ' ' then
    " " ^ cipher refl rotors notches
                 starts (String.sub s 1 ((String.length s)-1))
    else
    (Char.escaped (char_encrypt rotors refl (String.get s 0) (new_list))) ^
      if String.length s = 1 then
      ""
      else
      cipher refl rotors notches
             (intListToCharList new_list) (String.sub s 1 ((String.length s)-1))

source code for enabling syntax coloring from google-code-prettify