Code for parts a-d:

import string

d={“A”: 0.08167, “B”: 0.01492, “C”:.02782, “D”:.04253, “E”:.12702, “F”: .02228,

“G”:.02015, “H”:0.06094, “I”:.06996, “J”:.00153, “K”:.00772, “L”:.04025, “M”:.02406, “N”:.06749, “O”:.07507, “P”:.01929, “Q”:.00095, “R”:.05987, “S”:.06327, “T”:.09056, “U”:.02758, “V”:.00978, “W”:.02360, “X”:.00150, “Y”:.01974, “Z”:.00074}

plaintext = “ethicslawanduniversitypoliciestodefendasystemyouneedtobeabletothinklikeanattackerandthatincludesunderstandingtechniquesthatcanbeusedtocompromisesecurityhoweverusingthosetechniquesintherealworldmayviolatethelawandtheuniversityscomputingpracticesormaybeunethicalyoumustrespecttheprivacyandpropertyrightsofothersatalltimesorelseyouwillfailthecourseundersomecircumstancesevenprobingforweaknessesmayresultinseverepenaltiesuptoandincludingcivilfinesexpulsionandjailtimecarefullyreadthecomputerfraudandabuseactcfaaafederalstatutethatbroadlycriminalizescomputerintrusionsthisisjustoneofseverallawsthatgovernhackingunderstandwhatthelawprohibitsyoudontwanttoenduplikethisguyifindoubticanreferyoutoanattorneypleasereviewcaenspolicydocumentonrightsandresponsibilitiesforguidelinesconcerninguseoftechnologyresourcesatumaswellastheengineeringhonorcodeasmembersoftheuniversityyouarerequiredtoadheretothesepolicies”

def relative_freq(text):

n=float(len(text))

d = dict()

#initialize all frequencies to 0

for x in string.ascii_lowercase:

d[x] = 0

#for every instance of letter, increment count by 1

for l in text:

d[l] +=1

#for every letter, divide the frequency by total letters to get

#relative frequency

for x in string.ascii_lowercase:

d[x] = d[x]/n

return d

#function to get mean of frequencies from dictionary of relative freq

def mean(d):

total = 0;

for i in d:

total +=d[i]

return total/len(d)

#calculate population variane by taking input as dictionary of frequency

#and the mean

def population_variance(d,mu):

var = 0;

for i in d:

var += (d[i] – mu)**2

return var/len(d)

#utility function to rotate a list. We will use it to build a lookup table

#to get cipher text

def rotate(l,n):

return l[n:] + l[:n]

#function to build lookup table

def build_lookup_table():

#It is implemented as a nested dictionary.

lut = dict()

l = list()

l = string.ascii_lowercase[:]

counter = 0

for i in string.ascii_lowercase:

counter = 0

lut[i] = {}

for j in string.ascii_lowercase:

lut[i][j] = l[counter]

counter+=1

l = rotate(l,1)

return lut

def ciphertext(plaintext,key,lut):

key_replaced_text=””

while(len(key_replaced_text)+len(key)<=len(plaintext)):

key_replaced_text+=key

if len(key_replaced_text) != len(plaintext):

for i in key:

key_replaced_text+=i

if len(key_replaced_text) == len(plaintext):

break

cipher = “”

for i,j in zip(key_replaced_text,plaintext):

cipher+=lut[i][j]

return cipher

def analyze(text):

d = relative_freq(text)

return population_variance(d,mean(d))

def partd(plaintext,key,lut):

cipher = ciphertext(plaintext,key,lut)

variances = 0

for i in range(len(key)):

s = “”

for j in range(i,len(cipher),len(key)):

s+=cipher[j]

variances += analyze(s)

return variances/len(key)

print “A) Population variance for given dictionary:”

print population_variance(d,mean(d))

pt_d = relative_freq(plaintext)

print “B) Population variance for given plaintext:”

print population_variance(pt_d,mean(pt_d))

print “C) Population variance for cipher text for given keys:”

lut = build_lookup_table()

print analyze(ciphertext(plaintext,”yz”,lut))

print analyze(ciphertext(plaintext,”xyz”,lut))

print analyze(ciphertext(plaintext,”wxyz”,lut))

print analyze(ciphertext(plaintext,”vwxyz”,lut))

print analyze(ciphertext(plaintext,”uvwyz”,lut))

print “D) Result for each key:”

print partd(plaintext,”yz”,lut)

print partd(plaintext,”xyz”,lut)

print partd(plaintext,”wyz”,lut)

print partd(plaintext,”vwyz”,lut)

print partd(plaintext,”uvwyz”,lut)

Output:

A) Population variance for given dictionary:

0.00104056677352

B) Population variance for given plaintext:

0.001035960098

C) Population variance for cipher text for given keys:

0.000522474397184

0.000359166293935

0.00023525436279

0.000189753214864

0.000221136606201

D) Result for each key:

0.00106591263562

0.00111744202495

0.00111744202495

0.00114924294535

0.00122492441558

The lookup table for cipher, which is written as a nested dictionary in python. I am using the build_lookup_table() function to do it. Each row in the table is rotated list of alphabets from a to z. I have used the rotate() function to do it.