summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordavidovski <david@sendula.com>2022-11-30 10:06:56 +0000
committerdavidovski <david@sendula.com>2022-11-30 10:06:56 +0000
commit290c68795d8100cc97b8b53d80f331e536fc71b1 (patch)
treebf0068c4c9121406df9bc90f5c159fd93de8a61e
Added files to repositoryHEADmain
-rw-r--r--README.md11
-rw-r--r--src/neuralnetwork/clean_data.py59
-rw-r--r--src/neuralnetwork/data/ipa.csv174
-rwxr-xr-xsrc/neuralnetwork/data/names/English.txt3668
-rw-r--r--src/neuralnetwork/generate_alphabets.py44
-rw-r--r--src/neuralnetwork/rnn.py329
-rw-r--r--src/neuralnetwork/training.py373
-rw-r--r--src/neuralnetwork/util.py185
-rw-r--r--src/webserver/dist/index.html122
-rw-r--r--src/webserver/dist/map/afghanistan.pngbin0 -> 23211 bytes
-rw-r--r--src/webserver/dist/map/albania.pngbin0 -> 6638 bytes
-rw-r--r--src/webserver/dist/map/angolia.pngbin0 -> 27027 bytes
-rw-r--r--src/webserver/dist/map/argentina.pngbin0 -> 34781 bytes
-rw-r--r--src/webserver/dist/map/austria.pngbin0 -> 8881 bytes
-rw-r--r--src/webserver/dist/map/azerbaijan.pngbin0 -> 22534 bytes
-rw-r--r--src/webserver/dist/map/background.pngbin0 -> 685688 bytes
-rw-r--r--src/webserver/dist/map/bangladesh.pngbin0 -> 7277 bytes
-rw-r--r--src/webserver/dist/map/botswana.pngbin0 -> 24443 bytes
-rw-r--r--src/webserver/dist/map/brazil.pngbin0 -> 42215 bytes
-rw-r--r--src/webserver/dist/map/brunei.pngbin0 -> 6900 bytes
-rw-r--r--src/webserver/dist/map/bulgaria.pngbin0 -> 22835 bytes
-rw-r--r--src/webserver/dist/map/burundi.pngbin0 -> 7502 bytes
-rw-r--r--src/webserver/dist/map/cambodia.pngbin0 -> 8653 bytes
-rw-r--r--src/webserver/dist/map/cameroon.pngbin0 -> 25309 bytes
-rw-r--r--src/webserver/dist/map/canada.pngbin0 -> 116756 bytes
-rw-r--r--src/webserver/dist/map/chile.pngbin0 -> 19420 bytes
-rw-r--r--src/webserver/dist/map/colombia.pngbin0 -> 26597 bytes
-rw-r--r--src/webserver/dist/map/costa.pngbin0 -> 7024 bytes
-rw-r--r--src/webserver/dist/map/croatia.pngbin0 -> 8524 bytes
-rw-r--r--src/webserver/dist/map/cyprus.pngbin0 -> 8163 bytes
-rw-r--r--src/webserver/dist/map/czech.pngbin0 -> 8929 bytes
-rw-r--r--src/webserver/dist/map/denmark.pngbin0 -> 7671 bytes
-rw-r--r--src/webserver/dist/map/dibouti.pngbin0 -> 7311 bytes
-rw-r--r--src/webserver/dist/map/ecuador.pngbin0 -> 22644 bytes
-rw-r--r--src/webserver/dist/map/estonia.pngbin0 -> 8392 bytes
-rw-r--r--src/webserver/dist/map/ethopia.pngbin0 -> 26251 bytes
-rw-r--r--src/webserver/dist/map/fiji.pngbin0 -> 6769 bytes
-rw-r--r--src/webserver/dist/map/finland.pngbin0 -> 26057 bytes
-rw-r--r--src/webserver/dist/map/haiti.pngbin0 -> 8111 bytes
-rw-r--r--src/webserver/dist/map/honduras.pngbin0 -> 8963 bytes
-rw-r--r--src/webserver/dist/map/hungary.pngbin0 -> 8481 bytes
-rw-r--r--src/webserver/dist/map/iceland.pngbin0 -> 23212 bytes
-rw-r--r--src/webserver/dist/map/india.pngbin0 -> 32085 bytes
-rw-r--r--src/webserver/dist/map/indonesia.pngbin0 -> 36017 bytes
-rw-r--r--src/webserver/dist/map/iran.pngbin0 -> 29620 bytes
-rw-r--r--src/webserver/dist/map/ireland.pngbin0 -> 8956 bytes
-rw-r--r--src/webserver/dist/map/israel.pngbin0 -> 7630 bytes
-rw-r--r--src/webserver/dist/map/italy.pngbin0 -> 25329 bytes
-rw-r--r--src/webserver/dist/map/jamaica.pngbin0 -> 7326 bytes
-rw-r--r--src/webserver/dist/map/lithunia.pngbin0 -> 9185 bytes
-rw-r--r--src/webserver/dist/map/luxemburj.pngbin0 -> 6768 bytes
-rw-r--r--src/webserver/dist/map/macao.pngbin0 -> 6767 bytes
-rw-r--r--src/webserver/dist/map/malaysia.pngbin0 -> 24856 bytes
-rw-r--r--src/webserver/dist/map/malta.pngbin0 -> 6767 bytes
-rw-r--r--src/webserver/dist/map/mauritius.pngbin0 -> 6768 bytes
-rw-r--r--src/webserver/dist/map/mexico.pngbin0 -> 31649 bytes
-rw-r--r--src/webserver/dist/map/moldova.pngbin0 -> 7755 bytes
-rw-r--r--src/webserver/dist/map/namibia.pngbin0 -> 24016 bytes
-rw-r--r--src/webserver/dist/map/netherland.pngbin0 -> 7973 bytes
-rw-r--r--src/webserver/dist/map/nigeria.pngbin0 -> 24703 bytes
-rw-r--r--src/webserver/dist/map/norway.pngbin0 -> 25462 bytes
-rw-r--r--src/webserver/dist/map/panama.pngbin0 -> 8636 bytes
-rw-r--r--src/webserver/dist/map/peru.pngbin0 -> 27826 bytes
-rw-r--r--src/webserver/dist/map/philpine.pngbin0 -> 11118 bytes
-rw-r--r--src/webserver/dist/map/poland.pngbin0 -> 24402 bytes
-rw-r--r--src/webserver/dist/map/portugal.pngbin0 -> 8289 bytes
-rw-r--r--src/webserver/dist/map/puerto_rico.pngbin0 -> 8158 bytes
-rw-r--r--src/webserver/dist/map/russia.pngbin0 -> 98363 bytes
-rw-r--r--src/webserver/dist/map/serbia.pngbin0 -> 22624 bytes
-rw-r--r--src/webserver/dist/map/singapore.pngbin0 -> 7405 bytes
-rw-r--r--src/webserver/dist/map/slovenia.pngbin0 -> 7381 bytes
-rw-r--r--src/webserver/dist/map/south_africa.pngbin0 -> 28681 bytes
-rw-r--r--src/webserver/dist/map/spain.pngbin0 -> 23987 bytes
-rw-r--r--src/webserver/dist/map/sweden.pngbin0 -> 28746 bytes
-rw-r--r--src/webserver/dist/map/switzerland.pngbin0 -> 8243 bytes
-rw-r--r--src/webserver/dist/map/taiwan.pngbin0 -> 7412 bytes
-rw-r--r--src/webserver/dist/map/uk.pngbin0 -> 25004 bytes
-rw-r--r--src/webserver/dist/map/usa.pngbin0 -> 58284 bytes
-rw-r--r--src/webserver/dist/site.js129
-rw-r--r--src/webserver/dist/style.css138
-rw-r--r--src/webserver/dist/subdir/image.pngbin0 -> 226933 bytes
-rw-r--r--src/webserver/dist/subdir/index.html1
-rw-r--r--src/webserver/dist/subdir/test.txt1
-rw-r--r--src/webserver/pom.xml47
-rw-r--r--src/webserver/src/main/java/io/github/davidovski/names/APIRequestHandler.java112
-rw-r--r--src/webserver/src/main/java/io/github/davidovski/names/NameDatabaseManager.java75
-rw-r--r--src/webserver/src/main/java/io/github/davidovski/names/StaticRequestHandler.java85
-rw-r--r--src/webserver/src/main/java/io/github/davidovski/names/WebServer.java51
88 files changed, 5604 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6d70374
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+Hosted on: [name-generator.uk.to](https://name-generator.uk.to)
+
+# Name Generator
+
+This is the source code of the recurrent neural network name generator that I created for my NEA project. The idea behind the project was to generate unique looking names from an existing dataset of real names, with various categories for different genders and countries.
+
+This repository features two parts:
+- Python name generator, including the Recurrent Neural Network
+- Java webserver which hosts the javascript web app
+
+Unfortunately I have decided to not include the report that I wrote on this project to avoid any issues with collusion, however generally I have tried my best to document and explain the code itself.
diff --git a/src/neuralnetwork/clean_data.py b/src/neuralnetwork/clean_data.py
new file mode 100644
index 0000000..345218e
--- /dev/null
+++ b/src/neuralnetwork/clean_data.py
@@ -0,0 +1,59 @@
+import training
+import os
+import sys
+
+if __name__ == "__main__":
+
+ countries = training.get_countries()
+
+ for c, country in countries.items():
+ if c == "uk" or c == "usa":
+
+ # iterate through each dataset separately
+ for dataset, path in country.datasets.items():
+
+ # print information
+ print(f"filtering through {c}'s {dataset}...")
+ sys.stdout.flush()
+
+ names = country.get_names(dataset)
+
+ # store the names that are valid in a seperate list
+ names_output = []
+
+ # load the alphabet file for the country
+ alphabet = []
+
+ with open(os.path.join(country.path, "alphabet.txt"), "r") as file:
+ for l in file.read().split("\n"):
+ alphabet.append(l)
+
+ c = 0
+ t = len(names)
+ # iterate through names in the dataset
+ for name in names:
+ name = country.preprocess(name)
+
+ valid = True
+
+ # invalidate the name if a single letter is not in the alphabet
+ for letter in name:
+ if not letter in alphabet:
+ valid = False
+ break
+
+ if valid:
+ names_output.append(name)
+
+ c += 1
+ if c % 128 == 0:
+ print(f"\r{c}/{t}", end="")
+
+ # print how many names are left
+ print(f"kept {len(names_output)}/{len(names)} names")
+
+ # save dataset
+ with open(path, "w") as file:
+ for name in names_output:
+ file.write(name)
+ file.write("\n")
diff --git a/src/neuralnetwork/data/ipa.csv b/src/neuralnetwork/data/ipa.csv
new file mode 100644
index 0000000..6965ce5
--- /dev/null
+++ b/src/neuralnetwork/data/ipa.csv
@@ -0,0 +1,174 @@
+p,p,vl bilabial plosive
+b,b,vd bilabial plosive
+t,t,vl alveolar plosive
+d,d,vd alveolar plosive
+ʈ,t`,vl retroflex plosive
+ɖ,d`,vd retroflex plosive
+c,c,vl palatal plosive
+ɟ,J\,vd palatal plosive
+k,k,ld velar plosive
+ɡ,g,vd velar plosive
+q,q,vl uvular plosive
+ɢ,G\,vd uvular plosive
+ʔ,?,glottal plosive
+m,m,bilabial nasal
+ɱ,F,vl labiodental nasal
+n,n,alveolar nasal
+ɳ,n`,vl retroflex nasal
+ɲ,J,vl palatal nasal
+ŋ,N,vl velar nasal
+ɴ,N\,vl uvular nasal
+ʙ,B\,vd bilabial trill
+r,r,vd alveolar trill
+ʀ,R\,vl uvular trill
+ɾ,4,vl alveolar tap
+ɽ,r`,vl retroflex flap
+ɸ,p\,vl bilabial fricative
+β,B,vd bilabial fricative
+f,f,vl labiodental fricative
+v,v,vd labiodental fricative
+θ,T,vl dental fricative
+ð,D,vd dental fricative
+s,s,vl alveolar fricative
+z,z,vd alveolar fricative
+ʃ,S,vl postalveolar fricative
+ʒ,Z,vd postalveolar fricative
+ʂ,s`,vl retroflex fricative
+ʐ,z`,vd retroflex fricative
+ç,C,vl palatal fricative
+ʝ,j\,vd palatal fricative
+x,x,vl velar fricative
+ɣ,G,vd velar fricative
+χ,X,vl uvular fricative
+ʁ,R,vd uvular fricative
+ħ,X\,vl pharyngeal fricative
+ʕ,?\,vd pharyngeal fricative
+h,h,vl glottal fricative
+ʔ,?,glottal plosive
+ɬ,K,vl alveolar lateral fricative
+ɮ,K\,vd alveolar lateral fricative
+ʋ,P,vd labiodental approximant
+ɹ,r\,vd (post)alveolar approximant
+ɻ,r\`,vd retroflex approximant
+j,j,vd palatal approximant
+ɰ,M\,vd velar approximant
+l,l,vd alveolar lateral approximant
+ɭ,l`,vd retroflex lateral approximant
+ʎ,L,vd palatal lateral approximant
+ʟ,L\,vd velar lateral approximant
+pʼ,p_>,ejective
+tʼ,t_>,ejective
+ʈʼ,t`_>,ejective
+cʼ,c_>,ejective
+kʼ,k_>,ejective
+qʼ,q_>,ejective
+ɓ,b_<,vl bilabial implosive
+ɗ,d_<,vl alveolar implosive
+ƙ,k_<,vl velar implosive
+ɠ,g_<,vl velar implosive
+i,i,close front unrounded
+y,y,close front rounded
+ɨ,1,close central unrounded
+ʉ,},close central rounded
+ɯ,M,close back unrounded
+u,u,close back rounded
+ɪ,I,lax close front unrounded
+ʏ,Y,lax close front rounded
+ʊ,U,lax close back rounded
+e,e,close-mid front unrounded
+ø,2,front close-mid rounded
+ɤ,7,close-mid back unrounded
+o,o,close-mid back rounded
+ə,@,schwa
+ɘ,@\,close-mid central unrounded vowel
+ɵ,8,rounded schwa
+ɛ,E,open-mid front unrounded
+œ,9,front open-mid rounded
+ʌ,V,open-mid back unrounded
+ɔ,O,open-mid back rounded
+æ,{,mid-open front unrounded vowel
+ɐ,6,open-mid schwa
+a,a,open front unrounded
+ă,a_X,extra short open front unrounded
+ɶ,&,front open rounded
+ɑ,A,open back unrounded
+ɒ,Q,open back rounded
+̥,_0,voiceless
+̬,_v,voiced
+ʰ,_h,aspirated
+̤,_t,breathy voiced
+̰,_k,creaky voiced
+̼,_N,linguolabial
+̪,_d,dental
+̺,_a,apical
+̻,_m,laminal
+̹,_O,more rounded
+̜,_c,less rounded
+̟,_+,advanced
+̠,_-,retracted
+̈,"_""",centralized
+̽,_x,mid-centralized
+̩,=,syllabic
+̯,_^,non-syllabic
+ʷ,_w,labialized
+ʲ,',palatalized
+ˠ,_G,velarized
+ˤ,_?\,pharyngealized
+̴,_e,velarized or pharyngealized
+̝,_r,raised
+̞,_o,lowered
+̃,~,nasalized
+ⁿ,_n,nasal release
+ˡ,_l,lateral release
+̚,_},not audibly released
+̘,_A,advanced tongue root
+̙,_q,retracted tongue root
+̋,_T,extra high tone
+́,_H,high tone
+̄,_M,mid tone
+̀,_L,low tone
+̏,_B,extra low tone
+ˈ,"""",(primary) stress mark
+ˌ,%,secondary stress
+ː,:,length mark
+ˑ,:\,half-length
+̆,_X,extra-short
+.,.,syllable break
+ʍ,W,vl labial-velar fricative
+w,w,vd labio-velar approximant
+ɥ,H,labial-palatal approximant
+ʜ,H\,vl epiglottal fricative
+ʢ,<\,vl epiglottal fricative
+ʡ,>\,vl epiglottal plosive
+ɕ,s\,vl alveolopalatal fricative
+ʑ,z\,vl alveolopalatal fricative
+ʘ,O\,bilabial click
+ǀ,|\,dental click
+ǃ,!\,click
+ǂ,'=\,alveolar click
+ǁ,|\|\,alveolar lateral click
+ɺ,l\,vl alveolar lateral flap
+ɜ,3,open-mid central
+ʛ,G\_<,vl uvular implosive
+ɚ,@`,rhotacized schwa
+ɞ,3\,open-mid central rounded
+ɦ,h\,vd glottal fricative
+ɫ,5,velarized vl alveolar lateral
+ʄ,J\_<,vl palatal implosive
+ʼ,_>,ejective
+ɝ,3`,rhotacized open-mid central
+t͡ʃ,tS,vl postalveolar affricate
+d͡ʒ,dZ,vd postalveolar affricate
+t͡ɕ,ts\,vl alveolo-palatal affricate
+d͡ʑ,dz\,vd alveolo-palatal affricate
+t͡ɬ,tK,vl alveolar lateral affricate
+k͡p,kp,vl labial-velar plosive
+g͡b,gb,vd labial-velar plosive
+ŋ͡m,Nm,labial-velar nasal stop
+ʈ͡ʂ,ts`,vl retroflex affricate
+ɖ͡ʐ,tz`,vd retroflex affricate
+˩,_B,extra low tone
+˨,_L,low tone
+˧,_M,mid tone
+˦,_H,high tone
+˥,_T,extra high tone
diff --git a/src/neuralnetwork/data/names/English.txt b/src/neuralnetwork/data/names/English.txt
new file mode 100755
index 0000000..2b943a7
--- /dev/null
+++ b/src/neuralnetwork/data/names/English.txt
@@ -0,0 +1,3668 @@
+Abbas
+Abbey
+Abbott
+Abdi
+Abel
+Abraham
+Abrahams
+Abrams
+Ackary
+Ackroyd
+Acton
+Adair
+Adam
+Adams
+Adamson
+Adanet
+Addams
+Adderley
+Addinall
+Addis
+Addison
+Addley
+Aderson
+Adey
+Adkins
+Adlam
+Adler
+Adrol
+Adsett
+Agar
+Ahern
+Aherne
+Ahmad
+Ahmed
+Aikman
+Ainley
+Ainsworth
+Aird
+Airey
+Aitchison
+Aitken
+Akhtar
+Akram
+Alam
+Alanson
+Alber
+Albert
+Albrighton
+Albutt
+Alcock
+Alden
+Alder
+Aldersley
+Alderson
+Aldred
+Aldren
+Aldridge
+Aldworth
+Alesbury
+Alexandar
+Alexander
+Alexnader
+Alford
+Algar
+Ali
+Alker
+Alladee
+Allam
+Allan
+Allard
+Allaway
+Allcock
+Allcott
+Alldridge
+Alldritt
+Allen
+Allgood
+Allington
+Alliott
+Allison
+Allkins
+Allman
+Allport
+Allsop
+Allum
+Allwood
+Almond
+Alpin
+Alsop
+Altham
+Althoff
+Alves
+Alvey
+Alway
+Ambrose
+Amesbury
+Amin
+Amner
+Amod
+Amor
+Amos
+Anakin
+Anderson
+Andersson
+Anderton
+Andrew
+Andrews
+Angus
+Anker
+Anley
+Annan
+Anscombe
+Ansell
+Anstee
+Anthony
+Antic
+Anton
+Antony
+Antram
+Anwar
+Appleby
+Appleton
+Appleyard
+Apsley
+Arah
+Archer
+Ardern
+Arkins
+Armer
+Armitage
+Armour
+Armsden
+Armstrong
+Arnall
+Arnett
+Arnold
+Arnott
+Arrowsmith
+Arscott
+Arthur
+Artliff
+Ashbridge
+Ashbrook
+Ashby
+Ashcroft
+Ashdown
+Ashe
+Asher
+Ashford
+Ashley
+Ashman
+Ashton
+Ashurst
+Ashwell
+Ashworth
+Askew
+Aslam
+Asom
+Aspey
+Aspin
+Aspinall
+Astbury
+Astle
+Astley
+Aston
+Atherley
+Atherstone
+Atherton
+Atkin
+Atkins
+Atkinson
+Attard
+Atter
+Atterbury
+Atterton
+Attewell
+Attrill
+Attwood
+Auberton
+Auborn
+Aubrey
+Austen
+Austin
+Auton
+Avenue
+Avery
+Aves
+Avis
+Awad
+Axon
+Aylett
+Ayley
+Ayliffe
+Ayling
+Aylott
+Aylward
+Ayres
+Ayton
+Aziz
+Bacon
+Bailey
+Bain
+Bainbridge
+Baines
+Bains
+Baird
+Baker
+Baldwin
+Bale
+Ball
+Ballantyne
+Ballard
+Bamford
+Bancroft
+Banks
+Banner
+Bannister
+Barber
+Barclay
+Barker
+Barlow
+Barnard
+Barnes
+Barnett
+Baron
+Barr
+Barrett
+Barron
+Barrow
+Barry
+Bartlett
+Barton
+Bass
+Bassett
+Batchelor
+Bate
+Bateman
+Bates
+Batt
+Batten
+Batty
+Baxter
+Bayliss
+Beadle
+Beal
+Beale
+Beamish
+Bean
+Bear
+Beattie
+Beatty
+Beaumont
+Beck
+Bedford
+Beech
+Beer
+Begum
+Bell
+Bellamy
+Benfield
+Benjamin
+Bennett
+Benson
+Bentley
+Berger
+Bernard
+Berry
+Best
+Bethell
+Betts
+Bevan
+Beveridge
+Bickley
+Biddle
+Biggs
+Bill
+Bing
+Bingham
+Binnington
+Birch
+Bird
+Bishop
+Bithell
+Black
+Blackburn
+Blackman
+Blackmore
+Blackwell
+Blair
+Blake
+Blakeley
+Blakey
+Blanchard
+Bland
+Bloggs
+Bloom
+Blundell
+Blythe
+Bob
+Boden
+Boland
+Bolton
+Bond
+Bone
+Bonner
+Boon
+Booth
+Borland
+Bostock
+Boulton
+Bourne
+Bouvet
+Bowden
+Bowen
+Bower
+Bowers
+Bowes
+Bowler
+Bowles
+Bowman
+Boyce
+Boyd
+Boyle
+Bracey
+Bradbury
+Bradley
+Bradshaw
+Brady
+Brain
+Braithwaite
+Bramley
+Brandrick
+Bray
+Breen
+Brelsford
+Brennan
+Brett
+Brewer
+Bridges
+Briggs
+Bright
+Bristow
+Britton
+Broadbent
+Broadhurst
+Broadley
+Brock
+Brook
+Brooke
+Brooker
+Brookes
+Brookfield
+Brooks
+Broomfield
+Broughton
+Brown
+Browne
+Browning
+Bruce
+Brunet
+Brunton
+Bryan
+Bryant
+Bryson
+Buchan
+Buchanan
+Buck
+Buckingham
+Buckley
+Budd
+Bugg
+Bull
+Bullock
+Burch
+Burden
+Burdett
+Burford
+Burge
+Burgess
+Burke
+Burland
+Burman
+Burn
+Burnett
+Burns
+Burr
+Burrows
+Burt
+Burton
+Busby
+Bush
+Butcher
+Butler
+Butt
+Butter
+Butterworth
+Button
+Buxton
+Byrne
+Caddy
+Cadman
+Cahill
+Cain
+Cairns
+Caldwell
+Callaghan
+Callow
+Calveley
+Calvert
+Cameron
+Campbell
+Cann
+Cannon
+Caplan
+Capper
+Carey
+Carling
+Carmichael
+Carnegie
+Carney
+Carpenter
+Carr
+Carrington
+Carroll
+Carruthers
+Carson
+Carter
+Cartwright
+Carty
+Casey
+Cashmore
+Cassidy
+Caton
+Cavanagh
+Cawley
+Chadwick
+Chalmers
+Chamberlain
+Chambers
+Chan
+Chance
+Chandler
+Chantler
+Chaplin
+Chapman
+Chappell
+Chapple
+Charge
+Charles
+Charlton
+Charnock
+Chase
+Chatterton
+Chauhan
+Cheetham
+Chelmy
+Cherry
+Cheshire
+Chester
+Cheung
+Chidlow
+Child
+Childs
+Chilvers
+Chisholm
+Chong
+Christie
+Christy
+Chung
+Church
+Churchill
+Clamp
+Clancy
+Clark
+Clarke
+Clarkson
+Clay
+Clayton
+Cleary
+Cleaver
+Clegg
+Clements
+Cliff
+Clifford
+Clifton
+Close
+Clough
+Clowes
+Coates
+Coburn
+Cochrane
+Cockburn
+Cockle
+Coffey
+Cohen
+Cole
+Coleman
+Coles
+Coll
+Collard
+Collett
+Colley
+Collier
+Collingwood
+Collins
+Collinson
+Colman
+Compton
+Conneely
+Connell
+Connelly
+Connolly
+Connor
+Conrad
+Conroy
+Conway
+Cook
+Cooke
+Cookson
+Coomber
+Coombes
+Cooper
+Cope
+Copeland
+Copland
+Copley
+Corbett
+Corcoran
+Core
+Corlett
+Cormack
+Corner
+Cornish
+Cornock
+Corr
+Corrigan
+Cosgrove
+Costa
+Costello
+Cotter
+Cotterill
+Cotton
+Cottrell
+Couch
+Coulson
+Coulter
+Court
+Cousin
+Cousins
+Cove
+Cowan
+Coward
+Cowell
+Cowie
+Cowley
+Cox
+Coyle
+Crabb
+Crabtree
+Cracknell
+Craig
+Crane
+Craven
+Crawford
+Crawley
+Creasey
+Cresswell
+Crew
+Cripps
+Crisp
+Crocker
+Croft
+Crofts
+Cronin
+Crook
+Crosby
+Cross
+Crossland
+Crossley
+Crouch
+Croucher
+Crow
+Crowe
+Crowley
+Crown
+Crowther
+Crump
+Cullen
+Cumming
+Cummings
+Cummins
+Cunningham
+Curley
+Curran
+Currie
+Curry
+Curtis
+Curwood
+Cutts
+D arcy
+Dacey
+Dack
+Dalby
+Dale
+Daley
+Dallas
+Dalton
+Daly
+Dalzell
+Damon
+Danby
+Dandy
+Daniel
+Daniells
+Daniels
+Danks
+Dann
+Darby
+Darbyshire
+Darcy
+Dardenne
+Darlington
+Darr
+Daugherty
+Davenport
+Davey
+David
+Davidson
+Davie
+Davies
+Davis
+Davison
+Davy
+Dawe
+Dawes
+Dawkins
+Dawson
+Day
+Dayman
+De ath
+Deacon
+Deakin
+Dean
+Deane
+Deans
+Debenham
+Deegan
+Deeley
+Deighton
+Delamarre
+Delaney
+Dell
+Dempsey
+Dempster
+Denby
+Denham
+Denis
+Denney
+Dennis
+Dent
+Denton
+Depp
+Dermody
+Derrick
+Derrien
+Dervish
+Desai
+Devaney
+Devenish
+Deverell
+Devine
+Devlin
+Devon
+Devonport
+Dewar
+Dexter
+Diamond
+Dibble
+Dick
+Dickens
+Dickenson
+Dicker
+Dickinson
+Dickson
+Dillon
+Dimmock
+Dingle
+Dipper
+Dixon
+Dobbin
+Dobbins
+Doble
+Dobson
+Docherty
+Docker
+Dodd
+Dodds
+Dodson
+Doherty
+Dolan
+Dolcy
+Dolman
+Dolton
+Donald
+Donaldson
+Donkin
+Donlan
+Donn
+Donnachie
+Donnelly
+Donoghue
+Donohoe
+Donovan
+Dooley
+Doolin
+Doon
+Doors
+Dora
+Doran
+Dorman
+Dornan
+Dorrian
+Dorrington
+Dougal
+Dougherty
+Doughty
+Douglas
+Douthwaite
+Dove
+Dover
+Dowell
+Dowler
+Dowling
+Down
+Downer
+Downes
+Downey
+Downie
+Downing
+Downs
+Downton
+Dowson
+Doyle
+Drabble
+Drain
+Drake
+Draper
+Drew
+Drewett
+Dreyer
+Driffield
+Drinkwater
+Driscoll
+Driver
+Drummond
+Drury
+Drysdale
+Dubois
+Duck
+Duckworth
+Ducon
+Dudley
+Duff
+Duffield
+Duffin
+Duffy
+Dufour
+Duggan
+Duke
+Dukes
+Dumont
+Duncan
+Dundon
+Dunford
+Dunkley
+Dunlop
+Dunmore
+Dunn
+Dunne
+Dunnett
+Dunning
+Dunsford
+Dupont
+Durand
+Durant
+Durber
+Durham
+Durrant
+Dutt
+Duval
+Duvall
+Dwyer
+Dyde
+Dyer
+Dyerson
+Dykes
+Dymond
+Dymott
+Dyson
+Eade
+Eadie
+Eagle
+Eales
+Ealham
+Ealy
+Eames
+Eansworth
+Earing
+Earl
+Earle
+Earley
+Easdale
+Easdown
+Easen
+Eason
+East
+Eastaugh
+Eastaway
+Eastell
+Easterbrook
+Eastham
+Easton
+Eastwood
+Eatherington
+Eaton
+Eaves
+Ebbs
+Ebden
+Ebdon
+Ebeling
+Eburne
+Eccles
+Eccleston
+Ecclestone
+Eccott
+Eckersall
+Eckersley
+Eddison
+Eddleston
+Eddy
+Eden
+Edeson
+Edgar
+Edge
+Edgell
+Edgerton
+Edgley
+Edgson
+Edkins
+Edler
+Edley
+Edlington
+Edmond
+Edmonds
+Edmondson
+Edmunds
+Edmundson
+Edney
+Edon
+Edwards
+Edwick
+Eedie
+Egan
+Egerton
+Eggby
+Eggison
+Eggleston
+Eglan
+Egleton
+Eglin
+Eilers
+Ekin
+Elbutt
+Elcock
+Elder
+Eldeston
+Eldridge
+Eley
+Elfman
+Elford
+Elkin
+Elkington
+Ellam
+Ellans
+Ellard
+Elleray
+Ellerby
+Ellershaw
+Ellery
+Elliman
+Elling
+Ellingham
+Elliot
+Elliott
+Ellis
+Ellison
+Elliston
+Ellrott
+Ellwood
+Elmer
+Elmes
+Elmhirst
+Elmore
+Elms
+Elphick
+Elsdon
+Elsmore
+Elson
+Elston
+Elstone
+Eltis
+Elven
+Elvin
+Elvins
+Elwell
+Elwood
+Elworthy
+Elzer
+Emberey
+Emberson
+Embleton
+Emerick
+Emerson
+Emery
+Emmanuel
+Emmerson
+Emmery
+Emmett
+Emmings
+Emmins
+Emmons
+Emmott
+Emms
+Emsden
+Endroe
+England
+English
+Ennis
+Ennos
+Enright
+Enticott
+Entwistle
+Epsom
+Epton
+Ernest
+Erridge
+Errington
+Errity
+Esan
+Escott
+Eskins
+Eslick
+Espley
+Essam
+Essan
+Essop
+Estlick
+Etchells
+Etheridge
+Etherington
+Etherton
+Ettrick
+Evans
+Evason
+Evenden
+Everdell
+Everett
+Everill
+Everitt
+Everson
+Everton
+Eveson
+Evison
+Evrard
+Ewart
+Ewin
+Ewing
+Ewles
+Exley
+Exon
+Exton
+Eyett
+Eyles
+Eyre
+Eyres
+Fabb
+Fagan
+Fagon
+Fahy
+Fairbairn
+Fairbrace
+Fairbrother
+Fairchild
+Fairclough
+Fairhurst
+Fairley
+Fairlie
+Fairweather
+Falconer
+Falk
+Fall
+Fallon
+Fallows
+Falsh
+Farge
+Fargher
+Farhall
+Farley
+Farmer
+Farnsworth
+Farnum
+Farnworth
+Farr
+Farrant
+Farrar
+Farre
+Farrell
+Farrelly
+Farren
+Farrer
+Farrier
+Farrington
+Farrow
+Faulkner
+Faust
+Fawcett
+Fawn
+Faye
+Fearn
+Fearnley
+Fearns
+Fearon
+Featherstone
+Feeney
+Feetham
+Felix
+Fell
+Fellmen
+Fellows
+Feltham
+Felton
+Fenlon
+Fenn
+Fenton
+Fenwick
+Ferdinand
+Fereday
+Ferguson
+Fern
+Fernandez
+Ferns
+Fernyhough
+Ferreira
+Ferrier
+Ferris
+Ferry
+Fewtrell
+Field
+Fielder
+Fielding
+Fields
+Fifield
+Finan
+Finbow
+Finch
+Findlay
+Findley
+Finlay
+Finn
+Finnegan
+Finney
+Finnigan
+Finnimore
+Firth
+Fischer
+Fish
+Fisher
+Fishlock
+Fisk
+Fitch
+Fitchett
+Fitton
+Fitzgerald
+Fitzpatrick
+Fitzsimmons
+Flack
+Flaherty
+Flanagan
+Flanders
+Flannery
+Flavell
+Flaxman
+Fleetwood
+Fleming
+Fletcher
+Flett
+Florey
+Floss
+Flower
+Flowers
+Floyd
+Flynn
+Foden
+Fogg
+Foley
+Fontaine
+Foran
+Forbes
+Ford
+Forde
+Fordham
+Foreman
+Forester
+Forman
+Forrest
+Forrester
+Forshaw
+Forster
+Forsyth
+Forsythe
+Forth
+Fortin
+Foss
+Fossard
+Fosse
+Foster
+Foston
+Fothergill
+Fotheringham
+Foucher
+Foulkes
+Fountain
+Fowler
+Fowley
+Fox
+Foxall
+Foxley
+Frame
+Frampton
+France
+Francis
+Franco
+Frankish
+Frankland
+Franklin
+Franks
+Frary
+Fraser
+Frazer
+Frederick
+Frederikson
+Freeburn
+Freedman
+Freeman
+Freestone
+Freeth
+Freight
+French
+Fretwell
+Frey
+Fricker
+Friel
+Friend
+Frith
+Froggatt
+Froggett
+Frost
+Frostick
+Froy
+Frusher
+Fryer
+Fulker
+Fuller
+Fulleron
+Fullerton
+Fulton
+Funnell
+Furey
+Furlong
+Furnell
+Furness
+Furnish
+Furniss
+Furse
+Fyall
+Gadsden
+Gaffney
+Galbraith
+Gale
+Gales
+Gall
+Gallacher
+Gallagher
+Galliford
+Gallo
+Galloway
+Galvin
+Gamble
+Gammer
+Gammon
+Gander
+Gandham
+Ganivet
+Garber
+Garbett
+Garbutt
+Garcia
+Gardener
+Gardiner
+Gardner
+Garland
+Garner
+Garrard
+Garratt
+Garrett
+Garside
+Garvey
+Gascoyne
+Gaskell
+Gately
+Gates
+Gaudin
+Gaumont
+Gauntlett
+Gavin
+Gaynor
+Geaney
+Geary
+Geeson
+Geldard
+Geldart
+Gell
+Gemmell
+Gene
+George
+Gerard
+Gerrard
+Geyer
+Gibb
+Gibbins
+Gibbon
+Gibbons
+Gibbs
+Giblin
+Gibson
+Gifford
+Gilbert
+Gilbey
+Gilchrist
+Gilder
+Giles
+Gilfillan
+Gilks
+Gill
+Gillam
+Gillan
+Gillard
+Gillen
+Gillespie
+Gillett
+Gillies
+Gilmartin
+Gilmore
+Gilmour
+Ginty
+Girdwood
+Girling
+Given
+Gladwell
+Glaister
+Glasby
+Glasgow
+Glass
+Gleave
+Gledhill
+Gleeson
+Glen
+Glencross
+Glenn
+Glennie
+Glennon
+Glew
+Glossop
+Glover
+Glynn
+Goble
+Godby
+Goddard
+Godden
+Godfrey
+Godwin
+Goff
+Gold
+Goldberg
+Golding
+Goldman
+Goldsmith
+Goldsworthy
+Gomez
+Gonzalez
+Gooch
+Good
+Goodacre
+Goodall
+Goodchild
+Goode
+Gooding
+Goodman
+Goodridge
+Goodson
+Goodwin
+Goodyear
+Gordon
+Goring
+Gorman
+Gosden
+Gosling
+Gough
+Gould
+Goulden
+Goulding
+Gourlay
+Govender
+Govier
+Gower
+Gowing
+Grady
+Graham
+Grainger
+Grange
+Granger
+Grant
+Graves
+Gray
+Grayson
+Greaves
+Green
+Greenall
+Greenaway
+Greene
+Greener
+Greenhill
+Greening
+Greenleaf
+Greenshields
+Greenslade
+Greensmith
+Greenway
+Greenwood
+Greer
+Gregory
+Greig
+Grenard
+Grennan
+Gresham
+Grey
+Grierson
+Griff
+Griffin
+Griffith
+Griffiths
+Griggs
+Grimes
+Grimshaw
+Grinham
+Grivet
+Grogan
+Groom
+Grose
+Grosvenor
+Grout
+Groves
+Grundy
+Guest
+Guilmard
+Guinard
+Gulley
+Gunby
+Gunn
+Gunning
+Gunston
+Gunter
+Guthrie
+Gutteridge
+Guttridge
+Hackett
+Hadden
+Haddock
+Hadfield
+Hagan
+Haggett
+Haigh
+Haine
+Haines
+Hale
+Halford
+Hall
+Hallam
+Hallett
+Halliday
+Halliwell
+Halstead
+Hamer
+Hamill
+Hamilton
+Hammond
+Hamnett
+Hampson
+Hampton
+Hancock
+Hand
+Handley
+Hanlon
+Hannam
+Hansen
+Hanson
+Harden
+Harding
+Hardwick
+Hardy
+Hargreaves
+Harker
+Harkness
+Harley
+Harlow
+Harman
+Harness
+Harper
+Harries
+Harrington
+Harris
+Harrison
+Harrop
+Harry
+Hart
+Hartley
+Harvey
+Harwood
+Haslam
+Hassan
+Hassani
+Hastings
+Hatch
+Hatton
+Hawes
+Hawker
+Hawkes
+Hawkins
+Hawkridge
+Hawley
+Haworth
+Hawtin
+Hayes
+Haynes
+Hayward
+Head
+Healey
+Healy
+Heath
+Heathcote
+Heather
+Heatley
+Heaton
+Hedley
+Hegney
+Helley
+Hellier
+Helm
+Hemingway
+Hemmings
+Henderson
+Hendry
+Heneghan
+Hennessy
+Henry
+Hepburn
+Hepples
+Herbert
+Heritage
+Heron
+Herron
+Hetherington
+Hewitt
+Hewlett
+Heywood
+Hibbert
+Hickey
+Hickman
+Hicks
+Higgins
+Higginson
+Higgs
+Hill
+Hills
+Hilton
+Hind
+Hinde
+Hindle
+Hindley
+Hinds
+Hine
+Hinton
+Hirst
+Hiscocks
+Hitchcock
+Hoare
+Hobbs
+Hobson
+Hocking
+Hodder
+Hodge
+Hodges
+Hodgkins
+Hodgkinson
+Hodgson
+Hodkinson
+Hodson
+Hogan
+Hogg
+Holden
+Holder
+Holding
+Holdsworth
+Hole
+Holgate
+Holl
+Holland
+Hollis
+Holloway
+Holman
+Holmes
+Holt
+Homer
+Hood
+Hook
+Hooper
+Hooton
+Hope
+Hopes
+Hopkins
+Hopkinson
+Hopwood
+Horn
+Horne
+Horner
+Horrocks
+Horton
+Hough
+Houghton
+Hoult
+Houlton
+Houston
+Howard
+Howarth
+Howden
+Howe
+Howell
+Howells
+Howes
+Howie
+Hoyle
+Hubbard
+Hudson
+Huggins
+Hughes
+Hull
+Hulme
+Hume
+Humphrey
+Humphreys
+Humphries
+Hunt
+Hunter
+Hurley
+Hurrell
+Hurst
+Hussain
+Hussein
+Hussey
+Hutchings
+Hutchins
+Hutchinson
+Hutchison
+Hutton
+Hyde
+Ianson
+Ibbotson
+Ibbs
+Ibrahim
+Iddon
+Iggleden
+Iles
+Ilett
+Illing
+Illingworth
+Ilsley
+Impey
+Imran
+Ingermann
+Ingham
+Ingle
+Ingleby
+Ingledew
+Inglefield
+Ingles
+Inglethorpe
+Ingram
+Inker
+Inman
+Innalls
+Innes
+Inson
+Ireland
+Ireson
+Ironman
+Ironmonger
+Irvin
+Irvine
+Irving
+Irwin
+Isaac
+Isaacs
+Isbill
+Isbitt
+Isgate
+Isherwod
+Isherwood
+Islam
+Isman
+Isnard
+Issac
+Ivory
+Izzard
+Jackman
+Jacks
+Jackson
+Jacob
+Jacobs
+Jacobson
+Jacques
+Jaffray
+Jagger
+Jakeman
+James
+Jameson
+Jamieson
+Janes
+Jansen
+Jardine
+Jarman
+Jarram
+Jarratt
+Jarrett
+Jarrold
+Jarvis
+Jasper
+Jebson
+Jeffcock
+Jefferies
+Jeffers
+Jefferson
+Jeffery
+Jefford
+Jeffrey
+Jeffreys
+Jeffries
+Jeffs
+Jems
+Jenas
+Jenkin
+Jenkins
+Jenkinson
+Jenks
+Jenkyns
+Jenner
+Jennings
+Jennison
+Jennson
+Jensen
+Jepson
+Jermy
+Jerome
+Jerry
+Jervis
+Jesson
+Jessop
+Jevons
+Jewell
+Jewers
+Jewett
+Jewitt
+Jewkes
+Jewson
+Jiggens
+Jobson
+Johannson
+Johansen
+Johanson
+John
+Johns
+Johnson
+Johnston
+Johnstone
+Jolley
+Jolly
+Jonas
+Jones
+Jonhson
+Jopson
+Jordan
+Jordison
+Jordon
+Joseph
+Joss
+Jourdan
+Jowett
+Jowitt
+Joyce
+Joynson
+Jubb
+Judd
+Judge
+Jukes
+Jupp
+Jury
+Kacy
+Kaddour
+Kamara
+Kampfner
+Kane
+Kanes
+Kapoor
+Karim
+Karne
+Karras
+Kassell
+Kaufman
+Kaul
+Kaur
+Kavanagh
+Kay
+Kaye
+Kayes
+Keable
+Keal
+Kealey
+Keane
+Kearney
+Kearns
+Kearsley
+Kearton
+Keating
+Keaveney
+Keay
+Keeble
+Keefe
+Keegan
+Keelan
+Keeler
+Keeley
+Keeling
+Keenan
+Keene
+Keetley
+Keffler
+Kehoe
+Keighley
+Keight
+Keilty
+Keir
+Keith
+Kelk
+Kell
+Kelland
+Kellems
+Kellie
+Kelliher
+Kelly
+Kelsall
+Kelsey
+Kelso
+Kemp
+Kempson
+Kempster
+Kendall
+Kendell
+Kendrick
+Kenley
+Kennard
+Kennedy
+Kenneford
+Kennell
+Kenneth
+Kennett
+Kenney
+Kenning
+Kenny
+Kenrick
+Kensington
+Kent
+Kentwood
+Kenward
+Kenworthy
+Kenyon
+Keogh
+Kerby
+Kernick
+Kerr
+Kerrell
+Kerridge
+Kerrigan
+Kerrighen
+Kerrison
+Kershaw
+Ketley
+Kett
+Kettell
+Ketteringham
+Kettlewell
+Keward
+Kewley
+Keys
+Keyte
+Keywood
+Khalid
+Khalifa
+Khalil
+Khan
+Kibblewhite
+Kidd
+Kiddle
+Kidman
+Kidner
+Kiely
+Kiernan
+Kilb
+Kilbee
+Kilbey
+Kilbride
+Kilburn
+Kilford
+Kill
+Killeen
+Killen
+Killick
+Killock
+Kilminster
+Kilmurry
+Kilnan
+Kilner
+Kilroy
+Kilshaw
+Kimber
+Kimble
+Kinch
+Kinchin
+Kinder
+King
+Kingdon
+Kinghorn
+Kingman
+Kings
+Kingscott
+Kingsley
+Kingston
+Kinnaird
+Kinnear
+Kinnersley
+Kinniburgh
+Kinnison
+Kinrade
+Kinsella
+Kinsey
+Kinsley
+Kipling
+Kirby
+Kirk
+Kirkbride
+Kirkbright
+Kirkby
+Kirkland
+Kirkman
+Kirkpatrick
+Kirkwood
+Kirtley
+Kirwan
+Kirwin
+Kitchen
+Kitchin
+Kitching
+Kitson
+Kitt
+Klam
+Klein
+Knab
+Knappett
+Knibb
+Knigge
+Knight
+Knightley
+Knighton
+Knights
+Knott
+Knowler
+Knowles
+Knox
+Knoxville
+Knuckles
+Knutt
+Koban
+Kolt
+Kone
+Kore
+Kouma
+Kram
+Kreyling
+Kristensen
+Kromberg
+Kruger
+Kumar
+Kurian
+Kurray
+Kydd
+Kyle
+Kysel
+Labbe
+Lacey
+Lacy
+Laing
+Laird
+Lake
+Lakey
+Lakin
+Lamb
+Lambert
+Lambton
+Lame
+Lamond
+Lancaster
+Lander
+Lane
+Lang
+Langdon
+Lange
+Langford
+Langley
+Langridge
+Langston
+Langton
+Lanham
+Laraway
+Large
+Larkin
+Larkings
+Larsen
+Larsson
+Last
+Latham
+Lathan
+Lathey
+Lattimore
+Laurie
+Laver
+Laverick
+Lavery
+Lawal
+Lawler
+Lawlor
+Lawn
+Lawrance
+Lawrence
+Lawrie
+Laws
+Lawson
+Lawther
+Lawton
+Laycock
+Layton
+Le tissier
+Leach
+Leadley
+Leahy
+Leake
+Leal
+Leary
+Leaver
+Leck
+Leckie
+Ledger
+Lee
+Leech
+Leedham
+Leek
+Leeming
+Lees
+Leese
+Leeson
+Legg
+Legge
+Leggett
+Leigh
+Leighton
+Leitch
+Leith
+Lendon
+Lenihan
+Lennard
+Lennon
+Lennox
+Leonard
+Leroy
+Leslie
+Lester
+Lethbridge
+Levann
+Levett
+Levin
+Levine
+Levy
+Lewin
+Lewington
+Lewins
+Lewis
+Lewry
+Leyland
+Leys
+Leyshon
+Liddell
+Liddle
+Lightfoot
+Lilley
+Lilly
+Lilwall
+Lincoln
+Lind
+Linden
+Lindo
+Lindop
+Lindsay
+Line
+Lines
+Linford
+Ling
+Linley
+Linsby
+Linton
+Lister
+Litchfield
+Little
+Littlewood
+Livermore
+Livingstone
+Llewellyn
+Lloyd
+Loat
+Lobb
+Lock
+Locke
+Lockett
+Lockhart
+Lockie
+Lockwood
+Lockyer
+Lodge
+Loft
+Lofthouse
+Loftus
+Logan
+Lohan
+Lois
+Lomas
+Lomax
+London
+Long
+Longhurst
+Longley
+Longworth
+Lonsdale
+Lopes
+Lopez
+Lord
+Loudon
+Loughran
+Louth
+Lovatt
+Love
+Lovegrove
+Lovell
+Lovelock
+Lovett
+Lovey
+Lowbridge
+Lowdon
+Lowe
+Lowes
+Lowis
+Lowndes
+Lowrie
+Lowry
+Lucas
+Luce
+Lucey
+Luckhurst
+Ludgrove
+Ludkin
+Ludlow
+Luke
+Luker
+Lumb
+Lumley
+Lumsden
+Lunn
+Lunt
+Luscombe
+Luttrell
+Luxton
+Lyall
+Lyes
+Lyme
+Lynas
+Lynch
+Lynes
+Lynn
+Lyon
+Lyons
+Mac
+Macarthur
+Macaulay
+Macdonald
+Mace
+Macfarlane
+Macgregor
+Machin
+Macintyre
+Mack
+Mackay
+Mackenzie
+Mackie
+Maclean
+Macleod
+Macmillan
+Macpherson
+Macrae
+Madden
+Maddocks
+Magee
+Maguire
+Maher
+Mahoney
+Main
+Mair
+Major
+Makin
+Malley
+Mallinson
+Malone
+Maloney
+Mangnall
+Mann
+Manning
+Mansell
+Mansfield
+Manson
+Markham
+Marks
+Marlow
+Marr
+Marriott
+Marsden
+Marsh
+Marshall
+Martin
+Martinez
+Martins
+Mason
+Masters
+Mather
+Mathers
+Matheson
+Mathews
+Matthams
+Matthews
+Maughan
+Mawson
+Maxwell
+May
+Maynard
+Mcarthur
+Mcauley
+Mcavoy
+Mcbain
+Mccabe
+Mccaffrey
+Mccall
+Mccallum
+Mccann
+Mccarthy
+Mccartney
+Mccluskey
+Mcclymont
+Mcconnell
+Mccormack
+Mccormick
+Mccourt
+Mcculloch
+Mccullough
+Mcdermott
+Mcdonagh
+Mcdonald
+Mcdonnell
+Mcdougall
+Mcelroy
+Mcewan
+Mcfadden
+Mcfarlane
+Mcgee
+Mcghee
+Mcgill
+Mcginty
+Mcgowan
+Mcgrady
+Mcgrath
+Mcgregor
+Mcgrory
+Mcguinness
+Mcguire
+Mcintosh
+Mcintyre
+Mckay
+Mckee
+Mckenna
+Mckenzie
+Mckeown
+Mckie
+Mclaren
+Mclaughlin
+Mclean
+Mclellan
+Mcleod
+Mcloughlin
+Mcmahon
+Mcmanus
+Mcmillan
+Mcnally
+Mcnamara
+Mcnaught
+Mcneil
+Mcneill
+Mcnulty
+Mcphail
+Mcphee
+Mcpherson
+Mcrae
+Mcshane
+Mctaggart
+Meadows
+Meakin
+Mears
+Melia
+Mellor
+Meredith
+Merritt
+Metcalf
+Metcalfe
+Michael
+Michel
+Middleton
+Miles
+Milford
+Mill
+Millar
+Millard
+Miller
+Millett
+Milligan
+Millington
+Mills
+Millward
+Milne
+Milner
+Milward
+Mistry
+Mitchell
+Moffat
+Mohamed
+Mohammed
+Molloy
+Molyneux
+Monaghan
+Montague
+Montgomery
+Moody
+Moon
+Mooney
+Moore
+Moorhouse
+Moran
+More
+Moreno
+Moreton
+Morgan
+Moriarty
+Morley
+Moroney
+Morris
+Morrison
+Morrow
+Mortimer
+Morton
+Moseley
+Moss
+Mottram
+Mould
+Muir
+Mullen
+Mulligan
+Mullins
+Mundy
+Munro
+Murphy
+Murray
+Murrell
+Mustafa
+Myatt
+Myers
+Nair
+Nairn
+Nandi
+Nanson
+Nanton
+Napier
+Napper
+Nartey
+Nash
+Nason
+Naughton
+Naumann
+Nayler
+Naylor
+Naysmith
+Neal
+Neale
+Neary
+Neave
+Neaverson
+Nedd
+Needham
+Neeson
+Negros
+Neighbour
+Neill
+Neilsen
+Neilson
+Neish
+Nelmes
+Nelms
+Nelson
+Nemeth
+Nero
+Nesbitt
+Ness
+Nessbert
+Nettleton
+Neville
+Nevins
+Nevis
+Newall
+Newberry
+Newbold
+Newbury
+Newby
+Newcombe
+Newell
+Newey
+Newham
+Newill
+Newington
+Newland
+Newlands
+Newman
+Newsham
+Newsome
+Newson
+Newstead
+Newton
+Neyland
+Nichol
+Nicholas
+Nicholl
+Nicholls
+Nichols
+Nicholson
+Nickel
+Nickolls
+Nicks
+Nicol
+Nicolas
+Nicoll
+Nicolson
+Nield
+Nielsen
+Nielson
+Nightingale
+Niles
+Nilsen
+Nineham
+Nisbet
+Nixon
+Noach
+Noakes
+Nobbs
+Noble
+Noggins
+Nokes
+Nolan
+Nood
+Noon
+Noonan
+Norbert
+Norburn
+Norbury
+Norcross
+Nord
+Norgate
+Norgrove
+Norm
+Norman
+Normington
+Norris
+Norsworthy
+North
+Northcott
+Norton
+Norville
+Norwood
+Notman
+Nott
+Nourse
+Nova
+Nowak
+Nowell
+Noyce
+Noyes
+Nugent
+Number
+Nunn
+Nurse
+Nurton
+Nutella
+Nutman
+Nutt
+Nuttall
+Oakes
+Oakey
+Oakley
+Oaks
+Oakton
+Oates
+Oatridge
+Oatway
+Obrien
+Ocallaghan
+Oconnell
+Oconnor
+Odam
+Oddie
+Oddy
+Odea
+Odell
+Odling
+Odonnell
+Odonoghue
+Odriscoll
+Oflynn
+Ogden
+Ogilvie
+Ogilvy
+Ogrady
+Ohalloran
+Ohara
+Okeefe
+Okey
+Okten
+Olan
+Oldfield
+Oldham
+Olding
+Oldland
+Oldroyd
+Olds
+Oleary
+Oliver
+Olivier
+Ollerhead
+Olley
+Oloughlin
+Olsen
+Olson
+Omalley
+Oman
+Oneil
+Oneill
+Opayne
+Openshaw
+Oram
+Orbell
+Orchard
+Oreilly
+Oriley
+Orman
+Orme
+Ormiston
+Ormond
+Ormsby
+Ormston
+Orrell
+Orritt
+Orton
+Orvis
+Orwin
+Osborn
+Osborne
+Osman
+Osmond
+Ostcliffe
+Ostler
+Osullivan
+Oswald
+Otoole
+Otten
+Otter
+Ottey
+Ottley
+Otton
+Ould
+Oulton
+Overall
+Overett
+Overfield
+Overing
+Overson
+Overton
+Owen
+Owens
+Owings
+Oxby
+Oxenham
+Oxley
+Oxtoby
+Pack
+Packard
+Packer
+Pagan
+Page
+Paige
+Pailing
+Paine
+Painter
+Paisley
+Palfrey
+Palfreyman
+Palin
+Pallett
+Palmer
+Panesar
+Pankhurst
+Pannell
+Parish
+Park
+Parker
+Parkes
+Parkin
+Parkins
+Parkinson
+Parks
+Parmar
+Parnaby
+Parnell
+Parr
+Parratt
+Parrott
+Parry
+Parsons
+Partington
+Partlett
+Partridge
+Pascoe
+Pasfield
+Paskell
+Passmore
+Patchett
+Patel
+Pateman
+Paterson
+Paton
+Patrick
+Patten
+Patterson
+Pattinson
+Pattison
+Patton
+Paul
+Pavot
+Pawson
+Payne
+Peace
+Peach
+Peacock
+Peake
+Peal
+Peaper
+Pearce
+Pears
+Pearson
+Peat
+Peck
+Pedley
+Peebles
+Peel
+Peers
+Pegg
+Peigne
+Pell
+Pelling
+Pemberton
+Pender
+Pendlebury
+Pendleton
+Penfold
+Penn
+Pennell
+Penney
+Pennington
+Percival
+Pereira
+Perez
+Perkin
+Perkins
+Perks
+Perowne
+Perrett
+Perrin
+Perrins
+Perry
+Peters
+Petersen
+Peterson
+Petrova
+Pett
+Petticrew
+Peyton
+Phelan
+Phelps
+Philip
+Philips
+Phillips
+Philpott
+Phipps
+Phoenix
+Pick
+Pickard
+Pickering
+Pickersgill
+Pickett
+Pickford
+Pickthall
+Picot
+Pierce
+Piercey
+Pierre
+Pigott
+Pike
+Pilkington
+Pillay
+Pinder
+Pine
+Pinkney
+Pinner
+Pinnock
+Pinsmail
+Pipe
+Piper
+Pitcher
+Pitchford
+Pitt
+Pitts
+Plant
+Plastow
+Platt
+Platts
+Pledger
+Plouvin
+Plumb
+Plummer
+Pocock
+Pointer
+Pole
+Pollard
+Pollock
+Polson
+Pomeroy
+Pomphrey
+Pond
+Pooke
+Poole
+Poon
+Pope
+Porter
+Potter
+Potts
+Poulter
+Poulton
+Pounder
+Povey
+Powell
+Power
+Powers
+Powis
+Powles
+Poyser
+Pratt
+Preece
+Prendergast
+Prentice
+Prescott
+Preston
+Prevost
+Price
+Prime
+Prince
+Pringle
+Prior
+Pritchard
+Privett
+Probert
+Procter
+Proctor
+Prosser
+Provan
+Pryor
+Pugh
+Pullen
+Purcell
+Purkis
+Purnell
+Purse
+Purvis
+Putt
+Pyle
+Quigley
+Quinlivan
+Quinn
+Quinnell
+Quinton
+Quirk
+Quirke
+Rackham
+Radcliffe
+Radford
+Radley
+Raeburn
+Rafferty
+Rahman
+Raine
+Rainey
+Rainford
+Ralph
+Ralston
+Ramm
+Rampling
+Ramsay
+Ramsden
+Ramsey
+Rand
+Randall
+Randle
+Ranger
+Rankin
+Ranks
+Rann
+Ransom
+Ranson
+Rapson
+Rashid
+Ratcliffe
+Raval
+Raven
+Ravenscroft
+Rawlings
+Rawlinson
+Rawsthorne
+Raymond
+Rayner
+Read
+Reade
+Reader
+Reading
+Readle
+Readman
+Reardon
+Reasbeck
+Reay
+Redden
+Redding
+Reddy
+Redfern
+Redhead
+Redin
+Redman
+Redmond
+Redwood
+Reed
+Rees
+Reese
+Reeve
+Reeves
+Regan
+Regent
+Rehman
+Reid
+Reilly
+Reisser
+Render
+Renna
+Rennalls
+Rennie
+Renshaw
+Renwick
+Reveley
+Reyes
+Reygan
+Reynolds
+Rhoades
+Rhodes
+Rhys
+Ricci
+Rice
+Rich
+Richards
+Richardson
+Riches
+Richman
+Richmond
+Richter
+Rick
+Rickard
+Rickards
+Rickett
+Ricketts
+Riddell
+Riddle
+Riddler
+Ridge
+Ridgway
+Ridgwell
+Ridle
+Ridley
+Rigby
+Rigg
+Rigley
+Riley
+Ring
+Ripley
+Rippin
+Riseborough
+Ritchie
+Rivers
+Rixon
+Roach
+Robb
+Robbins
+Robe
+Robert
+Roberts
+Robertson
+Robin
+Robins
+Robinson
+Robishaw
+Robotham
+Robson
+Roche
+Rochford
+Rockliffe
+Rodden
+Roden
+Rodger
+Rodgers
+Rodham
+Rodrigues
+Rodriguez
+Rodwell
+Roebuck
+Roff
+Roffey
+Rogan
+Rogers
+Rogerson
+Roles
+Rolfe
+Rollinson
+Roman
+Romans
+Ronald
+Ronflard
+Rook
+Rooke
+Roome
+Rooney
+Rootham
+Roper
+Ropple
+Roscoe
+Rose
+Rosenblatt
+Rosenbloom
+Ross
+Rosser
+Rossi
+Rosso
+Roth
+Rothery
+Rothwell
+Rouse
+Roussel
+Rousset
+Routledge
+Rowan
+Rowe
+Rowland
+Rowlands
+Rowley
+Rowlinson
+Rowson
+Royall
+Royle
+Rudd
+Ruff
+Rugg
+Rumbold
+Rumsey
+Ruscoe
+Rush
+Rushbrooke
+Rushby
+Rushton
+Russel
+Russell
+Russon
+Rust
+Rutherford
+Rutter
+Ryan
+Ryans
+Rycroft
+Ryder
+Sadiq
+Sadler
+Said
+Saleh
+Salisbury
+Sallis
+Salmon
+Salt
+Salter
+Sampson
+Samuel
+Samuels
+Sanchez
+Sanders
+Sanderson
+Sandison
+Sands
+Santos
+Sargent
+Saunders
+Savage
+Saville
+Sawyer
+Saxton
+Sayers
+Schmid
+Schmidt
+Schofield
+Scott
+Searle
+Seddon
+Seer
+Selby
+Sellars
+Sellers
+Senior
+Sewell
+Sexton
+Seymour
+Shackleton
+Shah
+Shakespeare
+Shand
+Shanks
+Shannon
+Sharkey
+Sharma
+Sharp
+Sharpe
+Sharples
+Shaughnessy
+Shaw
+Shea
+Shearer
+Sheehan
+Sheldon
+Shelton
+Shepherd
+Sheppard
+Sheridan
+Sherman
+Sherriff
+Sherry
+Sherwood
+Shields
+Shipley
+Short
+Shotton
+Showell
+Shuttleworth
+Silcock
+Silva
+Simmonds
+Simmons
+Simms
+Simon
+Simons
+Simpson
+Sims
+Sinclair
+Singh
+Singleton
+Sinha
+Sisson
+Sissons
+Skelly
+Skelton
+Skinner
+Skipper
+Slade
+Slater
+Slattery
+Sloan
+Slocombe
+Small
+Smallwood
+Smart
+Smit
+Smith
+Smithson
+Smullen
+Smyth
+Smythe
+Sneddon
+Snell
+Snelling
+Snow
+Snowden
+Snowdon
+Somerville
+South
+Southern
+Southgate
+Southwick
+Sparkes
+Sparrow
+Spears
+Speed
+Speight
+Spence
+Spencer
+Spicer
+Spiller
+Spinks
+Spooner
+Squire
+Squires
+Stacey
+Stack
+Staff
+Stafford
+Stainton
+Stamp
+Stanfield
+Stanford
+Stanley
+Stannard
+Stanton
+Stark
+Steadman
+Stedman
+Steel
+Steele
+Steer
+Steere
+Stenhouse
+Stephen
+Stephens
+Stephenson
+Sterling
+Stevens
+Stevenson
+Steward
+Stewart
+Stock
+Stocker
+Stockley
+Stoddart
+Stokes
+Stokoe
+Stone
+Stoppard
+Storer
+Storey
+Storr
+Stott
+Stout
+Strachan
+Strange
+Street
+Stretton
+Strickland
+Stringer
+Strong
+Stroud
+Stuart
+Stubbs
+Stuckey
+Sturgess
+Sturrock
+Styles
+Sugden
+Sullivan
+Summers
+Sumner
+Sunderland
+Sutherland
+Sutton
+Swain
+Swales
+Swan
+Swann
+Swanson
+Sweeney
+Sweeting
+Swift
+Sykes
+Sylvester
+Symes
+Symonds
+Taggart
+Tailor
+Tait
+Talbot
+Tallett
+Tamber
+Tang
+Tanner
+Tansey
+Tansley
+Tappin
+Tapping
+Tapscott
+Tarr
+Tarrant
+Tasker
+Tate
+Tatlock
+Tatlow
+Tatnell
+Taurel
+Tayler
+Taylor
+Teague
+Teal
+Teale
+Teasdale
+Tedd
+Telford
+Tell
+Tellis
+Tempest
+Templar
+Temple
+Templeman
+Templeton
+Tennant
+Terry
+Thackeray
+Thackray
+Thake
+Thatcher
+Thelwell
+Thirlwall
+Thirlway
+Thirlwell
+Thistlethwaite
+Thom
+Thomas
+Thomason
+Thompson
+Thoms
+Thomson
+Thonon
+Thorley
+Thorndyke
+Thorne
+Thornes
+Thornhill
+Thornley
+Thornton
+Thorp
+Thorpe
+Thurbon
+Thurgood
+Thurling
+Thurlow
+Thurman
+Thurston
+Tickner
+Tidmarsh
+Tierney
+Till
+Tillett
+Tilley
+Tilson
+Tilston
+Timberlake
+Timmins
+Timms
+Timney
+Timson
+Tindall
+Tindell
+Tinker
+Tinkler
+Tinsley
+Tipping
+Tippins
+Tips
+Tisdall
+Titmarsh
+Titmus
+Titmuss
+Titterington
+Toal
+Tobin
+Tocher
+Todd
+Tohill
+Toland
+Tolley
+Tollis
+Tolmay
+Tomas
+Tombs
+Tomes
+Tomkins
+Tomlin
+Tomlinson
+Tompkin
+Tompkins
+Toms
+Tong
+Tonge
+Tonks
+Tonner
+Toomer
+Toomey
+Topham
+Topley
+Topliss
+Topp
+Torney
+Torrance
+Torrens
+Torres
+Tosh
+Totten
+Toucet
+Tovar
+Tovey
+Towell
+Towers
+Towle
+Townend
+Towns
+Townsend
+Townsley
+Tozer
+Trafford
+Train
+Trainor
+Trattles
+Travers
+Travill
+Travis
+Traynor
+Treble
+Trennery
+Trent
+Treseder
+Trevor
+Trew
+Trickett
+Trigg
+Trimble
+Trinder
+Trollope
+Troon
+Trotman
+Trott
+Trueman
+Truman
+Trump
+Truscott
+Tuck
+Tucker
+Tuckey
+Tudor
+Tuffnell
+Tufnall
+Tugwell
+Tully
+Tunks
+Tunstall
+Turford
+Turke
+Turkington
+Turland
+Turnbull
+Turner
+Turney
+Turnham
+Turnock
+Turrell
+Turton
+Turvey
+Tuthill
+Tuttle
+Tutton
+Tweddle
+Twigg
+Twiggs
+Twine
+Tyler
+Tyman
+Tyne
+Tyrer
+Tyrrell
+Uddin
+Ullman
+Ullmann
+Ulyatt
+Umney
+Underdown
+Underhill
+Underwood
+Unsworth
+Unwin
+Upfield
+Upjohn
+Upsdell
+Upson
+Upton
+Urwin
+Utley
+Utterson
+Uttley
+Utton
+Uttridge
+Vale
+Valentine
+Vallance
+Vallins
+Vallory
+Valmary
+Vancoller
+Vane
+Vann
+Vanstone
+Vanwell
+Vardy
+Varey
+Varley
+Varndell
+Vass
+Vaughan
+Vaughn
+Veale
+Veasey
+Veevers
+Veitch
+Velds
+Venables
+Ventura
+Verdon
+Verell
+Verney
+Vernon
+Vicary
+Vicens
+Vickars
+Vickerman
+Vickers
+Vickery
+Victor
+Vikers
+Villiger
+Villis
+Vince
+Vincent
+Vine
+Viner
+Vines
+Viney
+Vinicombe
+Vinny
+Vinton
+Virgo
+Voakes
+Vockins
+Vodden
+Vollans
+Voyse
+Vyner
+Wade
+Wadham
+Waghorn
+Wagstaff
+Wain
+Wainwright
+Waite
+Wakefield
+Wakeford
+Wakeham
+Wakelin
+Waldron
+Wale
+Wales
+Walkden
+Walker
+Wall
+Wallace
+Waller
+Walling
+Wallis
+Walls
+Walmsley
+Walpole
+Walsh
+Walshe
+Walter
+Walters
+Walton
+Wane
+Wang
+Warburton
+Warby
+Ward
+Warden
+Wardle
+Ware
+Wareing
+Waring
+Warn
+Warner
+Warren
+Warriner
+Warrington
+Warwick
+Water
+Waterfield
+Waterhouse
+Wateridge
+Waterman
+Waters
+Waterson
+Watkins
+Watkinson
+Watling
+Watson
+Watt
+Watters
+Watts
+Waugh
+Wears
+Weasley
+Weaver
+Webb
+Webber
+Webster
+Weeks
+Weir
+Welch
+Weldon
+Weller
+Wellington
+Wellman
+Wells
+Welsh
+Welton
+Were
+Werner
+Werrett
+West
+Western
+Westgate
+Westlake
+Weston
+Westwell
+Westwood
+Whalley
+Wharton
+Wheatcroft
+Wheatley
+Wheeldon
+Wheeler
+Whelan
+Whitaker
+Whitby
+White
+Whiteford
+Whitehead
+Whitehouse
+Whitelaw
+Whiteley
+Whitfield
+Whitham
+Whiting
+Whitley
+Whitlock
+Whitmore
+Whittaker
+Whittingham
+Whittington
+Whittle
+Whittley
+Whitworth
+Whyte
+Wickens
+Wickham
+Wicks
+Widdows
+Widdowson
+Wiggins
+Wigley
+Wilcox
+Wild
+Wilde
+Wildman
+Wileman
+Wiles
+Wilkes
+Wilkie
+Wilkin
+Wilkins
+Wilkinson
+Wilks
+Wilkshire
+Will
+Willett
+Willetts
+Williams
+Williamson
+Willis
+Wills
+Willson
+Wilmot
+Wilson
+Wilton
+Wiltshire
+Winder
+Windsor
+Winfer
+Winfield
+Winman
+Winn
+Winship
+Winstanley
+Winter
+Wintersgill
+Winward
+Wise
+Wiseman
+Wither
+Withers
+Wolf
+Wolfe
+Wolstencroft
+Wong
+Wood
+Woodcock
+Woodford
+Woodhall
+Woodham
+Woodhams
+Woodhead
+Woodhouse
+Woodland
+Woodley
+Woods
+Woodward
+Wooldridge
+Woollard
+Woolley
+Woolnough
+Wootton
+Worgan
+Wormald
+Worrall
+Worsnop
+Worth
+Worthington
+Wotherspoon
+Wragg
+Wraight
+Wray
+ren
+Wrench
+Wrenn
+Wrigglesworth
+Wright
+Wrightson
+Wyatt
+Wyer
+Yabsley
+Yallop
+Yang
+Yapp
+Yard
+Yardley
+Yarker
+Yarlett
+Yarnall
+Yarnold
+Yarwood
+Yasmin
+Yates
+Yeadon
+Yeardley
+Yeardsley
+Yeates
+Yeatman
+Yeldon
+Yeoman
+Yeomans
+Yetman
+Yeung
+Yoman
+Yomkins
+York
+Yorke
+Yorston
+Youlden
+Young
+Younge
+Younis
+Youssouf
+Yule
+Yusuf
+Zaoui
diff --git a/src/neuralnetwork/generate_alphabets.py b/src/neuralnetwork/generate_alphabets.py
new file mode 100644
index 0000000..0b1e18e
--- /dev/null
+++ b/src/neuralnetwork/generate_alphabets.py
@@ -0,0 +1,44 @@
+import training
+import os
+import sys
+
+if __name__ == "__main__":
+
+ # List all the directories containing country datasets to populate the countries dictionary
+ countries = training.get_countries()
+
+ for c, country in countries.items():
+ print(f"processing {c}...", end="")
+ sys.stdout.flush()
+
+ letters = {}
+
+ # get all the names in a country's dataset
+ all_names = country.get_all()
+
+ # iterate through all letters in the all of the names
+ for name in all_names:
+
+ # preprocess the name
+ name = country.preprocess(name)
+
+ for letter in name:
+ if letter in letters:
+ letters[letter] += 1
+ else:
+ letters[letter] = 1
+
+ print(f" found {len(letters)} in {len(all_names)} names... ", end="")
+ sys.stdout.flush()
+
+ # sort the letters by occurrence
+ letters_sorted = [l for l in letters]
+ letters_sorted.sort()
+ # output sorted letters to a file
+ with open(os.path.join(country.path, "alphabet.txt"), "w") as file:
+ for letter in letters_sorted:
+ file.write(letter)
+ file.write("\n")
+
+ print("saved!")
+ sys.stdout.flush()
diff --git a/src/neuralnetwork/rnn.py b/src/neuralnetwork/rnn.py
new file mode 100644
index 0000000..0662394
--- /dev/null
+++ b/src/neuralnetwork/rnn.py
@@ -0,0 +1,329 @@
+import random
+import time
+import math
+import torch
+import torch.nn as nn
+
+import warnings
+import sys
+
+import copy
+
+from util import AtomicNumber
+
+PRINT_INFORMATION_SECONDS = 2
+num_processes = 12
+
+#ignore warnings
+warnings.filterwarnings('ignore')
+
+
+if "--disable-cuda" in sys.argv:
+ cuda = False
+else:
+ cuda = torch.cuda.is_available()
+
+print(f"CUDA is {'enabled' if cuda else 'disabled'}")
+if cuda:
+ print("CUDA devices:")
+ for device_index in range(torch.cuda.device_count()):
+ print(f"{device_index}|\t{torch.cuda.get_device_name(device_index)}")
+
+device = torch.device("cuda") if cuda else torch.device("cpu")
+
+class RNN(nn.Module):
+ def __init__(self, input_size, hidden_size, output_size):
+ super(RNN, self).__init__()
+ self.hidden_size = hidden_size
+ self.output_size = output_size
+
+ # create the input, hidden and output linear transformation branches
+ self.input_to_hidden = nn.Linear(input_size + hidden_size, hidden_size, device=device)
+ self.input_to_output = nn.Linear(input_size + hidden_size, output_size, device=device)
+ self.output_to_output = nn.Linear(hidden_size + output_size, output_size, device=device)
+
+ # initialise a dropout function to be used on output data
+ self.dropout = nn.Dropout(0.1)
+
+ # initialise the softmax function to be used on output data
+ self.softmax = nn.LogSoftmax(dim=1)
+
+ # do not introduce any randomness by default
+ self.random_factor = 0
+
+
+ def forward(self, inputs, hidden):
+ # combine the input layer with the hidden layer to create the output layer and new hidden layer
+ input_combined = torch.cat((inputs, hidden), 1)
+ hidden = self.input_to_hidden(input_combined)
+ output = self.input_to_output(input_combined)
+ output_combined = torch.cat((hidden, output), 1)
+
+ output = self.output_to_output(output_combined)
+ # apply the functions to the output data
+ output = self.dropout(output)
+ output = self.softmax(output)
+
+ # add noise to the output, based on self.random_factor
+ if self.random_factor > 0:
+ # create a fully random tensor
+ random_tensor = torch.randn(self.output_size)
+ output = torch.add(output, random_tensor, alpha=self.random_factor)
+
+ return output, hidden
+
+ def initHidden(self):
+ # The hidden layer should be tensor with the length that we've specified
+ return torch.zeros(1, self.hidden_size, device=device)
+
+# instantiate the function to use to calculate loss
+# we will use Mean Squared Error between the
+criterion = nn.NLLLoss()
+
+# define the learning rate, to begin with, we can use 0.0005
+learning_rate = 0.0005
+
+"""Train a neural network on a single input name
+ Args:
+ rnn: (RNN) the rnn to train
+ input_tensors: (tensor) The input tensor: a one-hot-encoding from the first letter to the last letter, excluding the end of string marker
+ output_tensors: (tensor) The input tensor: a one-hot-encoding from the second letter to the end of the input data
+ Returns:
+ output: (tensor) the output of the training
+ loss: (float) the loss of the training
+"""
+def train_rnn(rnn, input_tensor, target_tensor):
+ # unsqueeze the target tensor,
+ target_tensor.unsqueeze_(-1)
+
+ # reset the parameters of the neural network
+ hidden = rnn.initHidden()
+ rnn.zero_grad()
+
+ # initiate an float called loss, this will store the error between each iteration output and its target
+ loss = 0
+ for i in range(input_tensor.size(0)):
+ output, hidden = rnn(input_tensor[i], hidden)
+
+ # calculate the error and add it to the overall loss
+ l = criterion(output, target_tensor[i])
+ loss += l
+
+ loss.backward()
+
+ # adjust the parameters of the rnn accordingly
+ for p in rnn.parameters():
+ p.data.add_(-learning_rate, p.grad.data)
+
+ return output, loss.item() / input_tensor.size(0)
+
+"""Create the input tensor for a name, a one hot matrix from the first letter to last letter (excluding EOS)
+ Args:
+ name: (str[]) an array of the letters in the name, can also be supplied as a string literal
+ alphabet: (str[]) The alphabet to use while encoding the name, an array starting with a "NULL" character and ending in an "EOS" character
+ value: (float) (default=1) The value to use for the "1" representing the letter
+ Returns:
+ tensor: (tensor) the input tensor for the given name
+"""
+def input_tensor(name, alphabet, value=1):
+ tensor = torch.zeros(len(name), 1, len(alphabet), device=device)
+
+ #iterate through each letter in the name
+ for li in range(len(name)):
+ letter = name[li]
+ # If the letter isn't in the alphabet, use the first "NULL" character
+ index = alphabet.index(letter) if letter in alphabet else 0
+
+ tensor[li][0][index] = value
+
+ return tensor
+
+"""Create the target tensor for a name, a long tensor from the second letter to the EOS
+ Args:
+ name: (str[]) an array of the letters in the name, can also be supplied as a string literal
+ alphabet: (str[]) The alphabet to use while encoding the name, an array starting with a "NULL" character and ending in an "EOS" character
+ Returns:
+ tensor: (tensor) the input tensor for the given name
+"""
+def target_tensor(name, alphabet):
+ indexes = []
+ for li in range(1, len(name)):
+ letter = name[li]
+ index = alphabet.index(letter) if letter in alphabet else 0
+ indexes.append(index)
+
+ # and add the end of string character
+ indexes.append(len(alphabet) - 1)
+
+ #legacy tensor needs to be made this way
+ if cuda:
+ return torch.cuda.LongTensor(indexes)
+ else:
+ return torch.LongTensor(indexes)
+
+
+"""Train a neural network on a list of names with a given alphabet
+ Args:
+ rnn (RNN): the neural network to train on
+ names: (str[]) the list of names to train on
+ alphabet: (str[]) the alphabet to use to encode characters
+ iterations: (int) (default=10000) The number of iterations of training that should be done
+"""
+def learn_names(rnn, names, alphabet, iterations=100000, num_processes=12):
+
+ # keep track of total time spent training by knowing when we started training
+ start = time.time()
+
+ # define the number of iterations per process
+ iters_per_process = int(iterations/num_processes)
+
+ processes = []
+
+ # keep track of the total loss
+ total_loss = AtomicNumber()
+
+ # keep track of total number of completed iterations
+ completed_iterations = AtomicNumber()
+
+ # keep track of the last time that the information was printed
+ # this way we can print every x seconds
+ last_print = AtomicNumber()
+
+ print(f"Training on {len(names)} names...")
+
+ # spawn processes, each running the _train function
+ torch.multiprocessing.spawn(_train, args=(rnn, names, alphabet, iters_per_process,
+ total_loss, completed_iterations, last_print, start, iterations),
+ nprocs=num_processes,
+ join=True)
+ print()
+
+"""Thread function to use when multiprocessing learn_names
+
+"""
+def _train(rank, rnn, names, alphabet, iterations,
+ total_loss, completed_iterations, last_print,
+ start, total_iterations):
+ for i in range(1, iterations+1):
+ try:
+ # choose a random name to train on
+ name = random.choice(names)
+
+ # create the input and trainint tensors
+ input_name_tensor = input_tensor(name, alphabet)
+ target_name_tensor = target_tensor(name, alphabet)
+
+ # train the rnn on the input and target tensors
+ output, loss = train_rnn(rnn, input_name_tensor, target_name_tensor)
+ total_loss.increment(loss)
+
+ # increment number of completed iterations
+ completed_iterations.increment()
+
+ # to prevent overloading the console, potentially slowing down the training process,
+ # only print information every PRINT_INFORMATION_SECONDS
+ if time.time() - last_print.get() > PRINT_INFORMATION_SECONDS:
+ # set last print to now to prevent other threads from also printing
+ last_print.set(time.time())
+
+ # calculate and display information
+ seconds_elapsed = time.time() - start
+ time_elapsed = "%dm %ds" % (math.floor(seconds_elapsed / 60), seconds_elapsed % 60)
+
+ percentage = completed_iterations.get() / total_iterations * 100
+
+ # print information on the same line as before
+ print("\r%s (%d %d%%) %.4f" % (time_elapsed, completed_iterations.get(), percentage, loss), end="")
+ except:
+ pass
+
+
+"""Sample a random name from the network using a starting letter
+ Args:
+ rnn: (RNN) the neural network to sample from
+ alphabet: (str[]) the alphabet to use to decode the outputs from the network
+ start_letter: (str) the letter to use to start the neural network
+ max_length: (int) (default=50) the maximum length for a name
+ Returns:
+ output_name: (str) the characters that the rnn has generated from the starting letter
+"""
+def sample(rnn, alphabet, start_letter, max_length=50):
+ # disable gradient calculation
+ #with torch.no_grad():
+ # create the input tensor from the start letter, using a randomized value
+ #random_value = random.random()
+ random_value = 1
+ sample_input = input_tensor(start_letter, alphabet, value=random_value)
+
+ rnn.dropout(sample_input)
+
+ # reset hidden layer
+ hidden = rnn.initHidden()
+
+ output_name = [start_letter]
+
+ # use a max length to prevent names from being too long
+ for i in range(max_length):
+ # call the rnn for the next letter
+ output, hidden = rnn(sample_input[0], hidden)
+
+ top_v, top_i = output.topk(1)
+ top_i = top_i[0][0]
+
+ if top_i == len(alphabet)-1: # EOS has been reached
+ break;
+ else:
+ # append next letter to output
+
+ letter = alphabet[top_i]
+ output_name.append(letter)
+
+ sample_input = input_tensor(letter, alphabet)
+
+ return output_name
+
+
+import warnings
+# testing
+if __name__ == "__main__":
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+
+ english_alphabet = [c for c in " abcdefghijklmnopqrstuvwxyz"]
+ english_alphabet.append("") # add the EOS character
+
+
+ option = input("(t)rain or (s)ample?")
+ if option == "t":
+
+ names = []
+ with open("data/datasets/usa/surname.txt", "r") as datafile:
+ # convert all names to lowercase and remove newline character
+ names = [name[:-1].lower() for name in datafile.readlines()]
+
+ # create the neural network with a hidden layer of size 128
+ rnn = RNN(len(english_alphabet), 128, len(english_alphabet))
+
+ # transfer to cuda if cuda is enabled
+ if cuda:
+ rnn.cuda()
+
+ def provide_name():
+ return random.choice(names)
+
+ learn_names(rnn, names, english_alphabet, iterations=100000, num_processes=12)
+ print()
+
+
+ torch.save(rnn, "data/english_names.pt")
+ elif option == "s":
+ rnn = torch.load("data/english_names.pt")
+ if cuda:
+ rnn.cuda()
+ rnn.eval()
+ rnn.random_factor = 0.7
+
+ for start_letter in [i for i in "abcdefghijklmnopqrstuvwxyz"]:
+ print(sample(rnn, english_alphabet, start_letter))
+ else:
+ print("invalid option!")
diff --git a/src/neuralnetwork/training.py b/src/neuralnetwork/training.py
new file mode 100644
index 0000000..85c6dbd
--- /dev/null
+++ b/src/neuralnetwork/training.py
@@ -0,0 +1,373 @@
+import os
+import types
+import json
+import random
+
+from util import *
+from rnn import *
+
+cuda = False
+num_processes = 12
+
+
+class Country:
+ def __init__(self, path):
+ self.path = path
+ self.datasets = {
+ "female": os.path.join(path, "female.txt"),
+ "male": os.path.join(path, "male.txt"),
+ "surname": os.path.join(path, "surname.txt"),
+ }
+
+ # initialise the pre and post proccess function lists
+ self.pre_process = []
+ self.post_process = []
+
+ # load the data file
+ self.load_data()
+
+ # load the alphabet file
+ self.alphabet = self.load_alphabet()
+
+ # initialise the rnn models
+ hidden_size = 128
+ self.rnn = {}
+
+ for dataset in self.datasets:
+ self.rnn[dataset] = RNN(
+ len(self.alphabet), hidden_size, len(self.alphabet))
+
+ """ Load the alphabet from the alphabet file
+ Returns:
+ alphabet: (str[]) the list of the letters/characters to use while training
+ """
+
+ def load_alphabet(self):
+ alphabet_path = os.path.join(self.path, "alphabet.txt")
+
+ # check if the alphabet file exists, if not, raise an exception
+ if os.path.exists(alphabet_path):
+ with open(alphabet_path, "r") as alphabet_file:
+ # Split the file by lines: on letter/character should be on each line
+ letters = alphabet_file.read().split("\n")
+ return letters
+ else:
+ raise Exception(
+ f"The alphabet file {alphabet_path} could not be found")
+ return []
+
+ """ load the data from the data file
+ """
+
+ def load_data(self):
+ data_path = os.path.join(self.path, "data.json")
+ if os.path.exists(data_path):
+ with open(data_path, "r") as data_file:
+ j = json.loads(data_file.read())
+
+ # match the imported global function with the ones listed in the json file
+ for pre in j["pre"]:
+ if pre in globals():
+ func = globals()[pre]
+
+ # check if the requested object is a function
+ if type(func) is types.FunctionType:
+ self.pre_process.append(func)
+ else:
+ raise Exception(
+ f"The function '{pre}' is not a function")
+ else:
+ # If the function was not loaded, throw an exception
+ raise Exception(
+ f"The function '{pre}' was not loaded or does not exist")
+
+ for post in j["post"]:
+ if post in globals():
+ func = globals()[post]
+
+ # check if the requested object is a function
+ if type(func) is types.FunctionType:
+ self.post_process.append(func)
+ else:
+ raise Exception(
+ f"The function '{post}' is not a function")
+ else:
+ # If the function was not loaded, throw an exception
+ raise Exception(
+ f"The function '{post}' was not loaded or does not exist")
+
+ else:
+ # load the default pre and post processing functions
+ self.pre_process = [uncapitalise]
+ self.post_process = [deserialise, capitalise]
+
+ """ List all the names from a given category file
+ Args:
+ category: (str) the category to select names from
+ Returns:
+ data: (str[]) an array containing all of the names from the given category file
+ """
+
+ def get_names(self, category):
+ with open(self.datasets[category], "r") as datafile:
+ return [name for name in datafile.read().split("\n")]
+
+ """ List all names in all categories
+ Returns:
+ data: (str[]) an array with all of the names in this country's datasets
+ """
+
+ def get_all(self):
+ return [name for k in self.datasets for name in self.get_names(k)]
+
+ """ Pre-process a name for training
+ Args:
+ name: the name loaded from the dataset
+ Returns:
+ name: the name after being processed
+ """
+
+ def postprocess(self, name):
+ for f in self.post_process:
+ name = f(name)
+ return name
+
+ """ Post-process a name after sampling
+ Args:
+ name: the name output from the recurrent neural network
+ Returns:
+ name: the name after being processed
+ """
+
+ def preprocess(self, name):
+ for f in self.pre_process:
+ name = f(name)
+ return name
+
+ """ Train a neural network on the given dataset
+ Args:
+ category: (str) the category to sample training names from
+ """
+
+ def train(self, category):
+ # select the RNN model to be training on
+ rnn = self.rnn[category]
+
+ # load names from that dataset and pre proccess them
+ print("preprocessing names...")
+ names = [self.preprocess(name) for name in self.get_names(category)]
+ print(f"processed {len(names)} names!")
+
+ # calculate optimum number of iterations (using 80% of whole dataset)
+ iters = int(len(names) * 0.8)
+
+ # start training
+ learn_names(rnn, names, self.alphabet, iterations=iters,
+ num_processes=num_processes)
+
+ """ Sample a name from the neural network with a given starting letter
+ Args:
+ category: (str) the category to sample generated names from
+ Returns:
+ name: the output from the neural network
+ """
+
+ def sample(self, category, start_letter):
+
+ # select the RNN model to be sampling from
+ rnn = self.rnn[category]
+
+ # set the random factor of the RNN to randomise names that are generated
+ rnn.random_factor = 0.7
+
+ # call the rnn sample function to generate a single name
+ name = sample(rnn, self.alphabet, start_letter)
+
+ # post process the name and return
+ return self.postprocess(name)
+
+ """ Load the rnn from its file
+ Args:
+ category: (str) the category to load
+ parent_directory: (str) where to find the model
+ """
+
+ def load_rnn(self, category, parent_directory):
+ model_file = os.path.join(parent_directory, f"{category}.pt")
+ self.rnn[category] = torch.load(model_file)
+
+ """ Save the rnn of a given category to its file
+ Args:
+ category: (str) the category to save
+ parent_directory: (str) the directory to save the model file to
+ """
+
+ def save_rnn(self, category, parent_directory):
+ rnn = self.rnn[category]
+ model_file = os.path.join(parent_directory, f"{category}.pt")
+ torch.save(rnn, model_file)
+
+
+def get_countries():
+ return {
+ country: Country(os.path.join(countries_path, country)) for country in os.listdir(countries_path) if os.path.isdir(os.path.join(countries_path, country))
+ }
+
+
+""" train all of the datasets from a specific country
+ Args:
+ country: (Country)
+"""
+
+
+def train_country(country, name):
+ datasets = country.datasets
+ for dataset in datasets:
+ print(f"Training {dataset} in {name}")
+ country.train(dataset)
+
+ print(f"Finished training on {dataset}... saving...", end="")
+ path = os.path.join("data", "models", name)
+
+ # check if the path already exists before trying to make directories
+ if not os.path.exists(path):
+ os.makedirs(path)
+
+ country.save_rnn(dataset, path)
+ print("saved!")
+
+
+def sample_country(country, country_name, number_of_samples=10000):
+
+ datasets = country.datasets
+ for dataset in datasets:
+
+ # ensure that the model exists before sampling
+ path = os.path.join("data", "models", country_name)
+ if os.path.exists(os.path.join(path, dataset + ".pt")):
+
+ # load the country's rnn
+ country.load_rnn(dataset, path)
+
+ # load the names from the country's dataset, and pre-process them
+ names = [country.preprocess(name)
+ for name in country.get_names(dataset)]
+
+ # make a dictionary full of start letters and their frequency
+ start_letters = {}
+
+ for name in names:
+ if len(name) > 0:
+ start_letter = name[0]
+
+ # if the start letter isn't already in the dictionary, add it with value 1
+ if start_letter in start_letters:
+ start_letters[start_letter] += 1
+ else:
+ start_letters[start_letter] = 1
+
+ # turn each integer count into a float where: letter_weight=frequency/total_names
+ total = len(names)
+
+ for letter in start_letters:
+ weight = float(start_letters[letter] / total)
+ start_letters[letter] = weight
+
+ # sample names from the RNN
+ sampled_names = []
+
+ for i in range(number_of_samples):
+ try:
+ letter = weighted_choice(start_letters)
+ sample = country.sample(dataset, letter)
+ sampled_names.append(sample)
+ except:
+ pass
+
+ # remove duplicate names
+ sampled_names = list(dict.fromkeys(sampled_names))
+
+ # create a sqlite connection
+ connection = sqlite3.connect(database)
+
+ # always close the connection when finished
+ with connection:
+ cursor = connection.cursor()
+ for name in sampled_names:
+ sql = "INSERT INTO names(Name, Origin, Category) VALUES(?, ?, ?)"
+
+ # insert the current name and options into the database
+ cursor.execute(sql, (name, country_name, dataset))
+
+ # commit changes and save the database
+ connection.commit()
+
+ print(
+ f"Saved {len(sampled_names)} names for {country_name}/{dataset}")
+
+ else:
+ print(f"the model: {country_name}/{dataset} was not found.")
+
+
+countries_path = "data/datasets"
+database = os.path.join("data", "names.db")
+if __name__ == "__main__":
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+
+ # allow processes on this model to share memory
+ torch.multiprocessing.set_start_method('spawn')
+
+ # List all the directories containing country datasets to populate the countries dictionary
+ countries = get_countries()
+
+ country_count = len(countries)
+ # Display debug information
+ print(f"Loaded {country_count} countries!")
+
+ # list all countries in neat collumns
+ collumns = 4
+ width = 14
+ i = 0
+ for country in countries:
+ i += 1
+
+ # print the country and then its index
+ print(country, end="")
+
+ # organise into rows and collumns
+ if i % collumns == 0:
+ print("")
+ else:
+ # separate collumns with spaces
+ print(" " * (width - len(country)), end="")
+
+ # keep asking until the country selection is valid
+ good_selection = False
+ while not good_selection:
+ # prompt user to select a country to train, or train all
+ country_selection = input(
+ "select the name of a country to train on, or (all) to train on all countries: ")
+
+ good_selection = True
+ selected_countries = []
+
+ # if the user selected all, then add all countries to list, if not, add the selected country
+ if country_selection.lower() == "all":
+ [selected_countries.append(country) for country in countries]
+ elif country_selection.lower() in countries:
+ selected_countries.append(country_selection)
+ else:
+ print("Country not found, try again")
+ good_selection = False
+
+ choice = input("(t)rain on data, or (s)ample from weights?")
+
+ if choice.lower()[0] == "t":
+ for country in selected_countries:
+ train_country(countries[country], country)
+
+ elif choice.lower()[0] == "s":
+ create_table(database)
+ for country in selected_countries:
+ sample_country(countries[country], country)
diff --git a/src/neuralnetwork/util.py b/src/neuralnetwork/util.py
new file mode 100644
index 0000000..268819e
--- /dev/null
+++ b/src/neuralnetwork/util.py
@@ -0,0 +1,185 @@
+import sqlite3
+import tensorflow as tf
+from pincelate import Pincelate
+from multiprocessing import Value
+import random
+
+"""Atomic Number
+A multiprocessing-safe Number that you can increment and set a value to.
+"""
+class AtomicNumber:
+ def __init__(self, initial=0):
+ # set the initial value of the number
+ self._v = Value("d", initial)
+
+ # use python thread locks
+ self._lock = self._v.get_lock()
+
+ def set(self, num):
+ with self._lock:
+ # set the value of the number and return it
+ self._v.value = num
+ return self._v.value
+
+ def increment(self, num=1):
+ with self._lock:
+ # increase the value of the number and then return it
+ self._v.value += num
+ return self._v.value
+
+ def get(self):
+ return self._v.value
+
+
+""" Capitalise the first letter of a name
+ Args:
+ name: (str) the name to transform
+ Returns:
+ name: (str) the output name
+"""
+
+
+def capitalise(name):
+ return str(name).capitalize()
+
+
+""" uncapitalise the first letter of a name
+ Args:
+ name: (str) the name to transform
+ Returns:
+ name: (str) the output name
+"""
+
+
+def uncapitalise(name):
+ return str(name).lower()
+
+
+# import pincelate
+
+# tell tensorflow to only log ERRORS rather than all problems
+tf.get_logger().setLevel('ERROR')
+
+pin = Pincelate()
+
+""" Transliterate names into their phonetic spelling
+ Args:
+ name: (str) the name to transliterate
+ Returns:
+ name: (str) the pronunciation of the name as an arpabet list
+"""
+
+
+def transliterate(name):
+ output = []
+
+ # iterate through each word and sound out separately
+ for word in name.split(" "):
+ try:
+ for phoneme in pin.soundout(word):
+ output.append(phoneme)
+ except Exception as e:
+ output.append(word)
+ output.append(" ")
+
+ # remove the trailing " "
+ output.pop()
+
+ return output
+
+
+""" Transliterate phonetic spellings back into names
+ Args:
+ name: (str) the pronunciation of the name as an arpabet list
+ Returns:
+ name: (str) a guessed spelling of the given phoneme list
+"""
+
+
+def reverse_transliterate(name):
+ words = []
+
+ current_word = []
+ # iterate through all phonemes, spliting groups between " " into words
+ for phoneme in name:
+ if phoneme == " ":
+ words.append(current_word)
+
+ # reset current word
+ current_word = []
+ else:
+ current_word.append(phoneme)
+
+ # add whats left of the current word to the list of words
+ words.append(current_word)
+
+ # spell each word separately
+ spellings = [pin.spell(word) for word in words]
+ return " ".join(spellings)
+
+
+"""Load random line from a given filename
+"""
+
+
+def get_random_line(file_name):
+ total_bytes = os.stat(file_name).st_size
+ random_point = random.randint(0, total_bytes)
+ file = open(file_name)
+ file.seek(random_point)
+ file.readline() # skip this line to clear the partial line
+ return file.readline()
+
+
+""" Concatenate elemnts of a list
+ Args:
+ name: (str[]) the letters of a name in an array
+ Returns:
+ name: (str)
+"""
+
+
+def deserialise(name):
+ return "".join(name)
+
+
+""" return a random item from a dictionary of weighted items
+ Args:
+ weights: (dict) a dictionary containing the items as keys and float values as weights
+ Returns:
+ item: a random item selected from the dictionary
+"""
+
+
+def weighted_choice(weights):
+ # choose a randm number between 0 and 1
+ choice = random.uniform(0.0, 1.0)
+
+ # iterate through weights, subtracting the weight from the choice each time
+ for item, weight in weights.items():
+ choice -= weight
+
+ # if we are currently on the chosen item, return it
+ if choice < 0:
+ return item
+
+ # in case the input dictionary is incorrectly setup, return the last item
+ return list(weights)[-1]
+
+
+def create_table(database_path):
+ # connect to the database and get the cursor
+ connection = sqlite3.connect(database_path)
+
+ # always close the connection at the end
+ with connection:
+ cursor = connection.cursor()
+
+ cursor.execute("CREATE TABLE IF NOT EXISTS names (\
+ Name TEXT,\
+ Origin TEXT,\
+ Category TEXT\
+ )")
+
+ # commit the changes, saving them
+ connection.commit()
diff --git a/src/webserver/dist/index.html b/src/webserver/dist/index.html
new file mode 100644
index 0000000..cd016b2
--- /dev/null
+++ b/src/webserver/dist/index.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Name Generator</title>
+
+ <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/>
+
+ <script type="text/javascript" src="/site.js"></script>
+ <link rel="stylesheet" href="style.css">
+
+
+</head>
+<body onload="updateMap()">
+ <div class="main">
+ <h1 class="title box-item">Name Generator</h1>
+
+ <div id="namearea" class="box-item">
+ <!-- Area for generated names to show-->
+ </div>
+
+ <button class="box-item" id="generate" onclick="generateName()">Generate Name</button>
+
+ <div class="options box-item">
+ <select id="gender">
+ <option value="male">Male</option>
+ <option value="female">Female</option>
+ </select>
+
+ <select id="origin" onchange="if (this.selectedIndex) updateMap();">
+ <option value="uk">UK</option>
+ <option value="usa">USA</option>
+ <option value="taiwan">Taiwan</option>
+ <option value="switzerland">Switzerland</option>
+ <option value="sweden">Sweden</option>
+ <option value="spain">Spain</option>
+ <option value="south_africa">South Africa</option>
+ <option value="slovenia">Slovenia</option>
+ <option value="singapore">Singapore</option>
+ <option value="serbia">Serbia</option>
+ <option value="russia">Russia</option>
+ <option value="puerto_rico">Puerto Rico</option>
+ <option value="portugal">Portugal</option>
+ <option value="poland">Poland</option>
+ <option value="philpine">Philippines</option>
+ <option value="peru">Peru</option>
+ <option value="panama">Panama</option>
+ <option value="norway">Norway</option>
+ <option value="nigeria">Nigeria</option>
+ <option value="netherland">Netherlands</option>
+ <option value="namibia">Namibia</option>
+ <option value="moldova">Moldova</option>
+ <option value="mexico">Mexico</option>
+ <option value="mauritius">Mauritius</option>
+ <option value="malta">Malta</option>
+ <option value="malaysia">Malaysia</option>
+ <option value="macao">Macao</option>
+ <option value="luxemburj">Luxembourg</option>
+ <option value="lithunia">Lithuania</option>
+ <option value="jamaica">Jamaica</option>
+ <option value="italy">Italy</option>
+ <option value="israel">Israel</option>
+ <option value="ireland">Ireland</option>
+ <option value="iran">Iran</option>
+ <option value="indonesia">Indonesia</option>
+ <option value="india">India</option>
+ <option value="iceland">Iceland</option>
+ <option value="hungary">Hungary</option>
+ <option value="honduras">Honduras</option>
+ <option value="haiti">Haiti</option>
+ <option value="finland">Finland</option>
+ <option value="fiji">Fiji</option>
+ <option value="ethopia">Ethiopia</option>
+ <option value="estonia">Estonia</option>
+ <option value="ecuador">Ecuador</option>
+ <option value="dibouti">Djibouti</option>
+ <option value="denmark">Denmark</option>
+ <option value="czech">Czech</option>
+ <option value="cyprus">Cyprus</option>
+ <option value="croatia">Croatia</option>
+ <option value="costa">Costa Rica</option>
+ <option value="colombia">Colombia</option>
+ <option value="chile">Chile</option>
+ <option value="canada">Canada</option>
+ <option value="cameroon">Cameroon</option>
+ <option value="cambodia">Cambodia</option>
+ <option value="burundi">Burundi</option>
+ <option value="bulgaria">Bulgaria</option>
+ <option value="brunei">Brunei</option>
+ <option value="brazil">Brazil</option>
+ <option value="botswana">Botswana</option>
+ <option value="bangladesh">Bangladesh</option>
+ <option value="azerbaijan">Azerbaijan</option>
+ <option value="austria">Austria</option>
+ <option value="argentina">Argentina</option>
+ <option value="angolia">Angola</option>
+ <option value="albania">Albania</option>
+ <option value="afghanistan">Afghanistan</option>
+ </select>
+ </div>
+
+ <canvas id="worldmap" width="2947" height="1601"></canvas>
+
+
+ <div>
+ <button id="advanced" onclick="toggleAdvanced()">Advanced Options</button>
+ </div>
+
+ <div id="advancedoptions" style="display: none">
+ <p></p>
+ <label>number of names: </label><input id="number" type="number" value="1" min="1" max="99" maxlength="2" onkeyup="forceCount()">
+ <p></p>
+
+ <label>generate surnames: </label><input id="surnames" type="checkbox" checked="true">
+ <span class="indicator" onclick="checkBox(document.getElementById('surnames'))"></span>
+ </input>
+ </div>
+ </div>
+
+
+</body>
+</html>
diff --git a/src/webserver/dist/map/afghanistan.png b/src/webserver/dist/map/afghanistan.png
new file mode 100644
index 0000000..49abc92
--- /dev/null
+++ b/src/webserver/dist/map/afghanistan.png
Binary files differ
diff --git a/src/webserver/dist/map/albania.png b/src/webserver/dist/map/albania.png
new file mode 100644
index 0000000..c045c57
--- /dev/null
+++ b/src/webserver/dist/map/albania.png
Binary files differ
diff --git a/src/webserver/dist/map/angolia.png b/src/webserver/dist/map/angolia.png
new file mode 100644
index 0000000..2e415e5
--- /dev/null
+++ b/src/webserver/dist/map/angolia.png
Binary files differ
diff --git a/src/webserver/dist/map/argentina.png b/src/webserver/dist/map/argentina.png
new file mode 100644
index 0000000..458376c
--- /dev/null
+++ b/src/webserver/dist/map/argentina.png
Binary files differ
diff --git a/src/webserver/dist/map/austria.png b/src/webserver/dist/map/austria.png
new file mode 100644
index 0000000..f3ed00e
--- /dev/null
+++ b/src/webserver/dist/map/austria.png
Binary files differ
diff --git a/src/webserver/dist/map/azerbaijan.png b/src/webserver/dist/map/azerbaijan.png
new file mode 100644
index 0000000..430b9d6
--- /dev/null
+++ b/src/webserver/dist/map/azerbaijan.png
Binary files differ
diff --git a/src/webserver/dist/map/background.png b/src/webserver/dist/map/background.png
new file mode 100644
index 0000000..77802f6
--- /dev/null
+++ b/src/webserver/dist/map/background.png
Binary files differ
diff --git a/src/webserver/dist/map/bangladesh.png b/src/webserver/dist/map/bangladesh.png
new file mode 100644
index 0000000..8fdf65a
--- /dev/null
+++ b/src/webserver/dist/map/bangladesh.png
Binary files differ
diff --git a/src/webserver/dist/map/botswana.png b/src/webserver/dist/map/botswana.png
new file mode 100644
index 0000000..ec58775
--- /dev/null
+++ b/src/webserver/dist/map/botswana.png
Binary files differ
diff --git a/src/webserver/dist/map/brazil.png b/src/webserver/dist/map/brazil.png
new file mode 100644
index 0000000..8ba32c5
--- /dev/null
+++ b/src/webserver/dist/map/brazil.png
Binary files differ
diff --git a/src/webserver/dist/map/brunei.png b/src/webserver/dist/map/brunei.png
new file mode 100644
index 0000000..c518380
--- /dev/null
+++ b/src/webserver/dist/map/brunei.png
Binary files differ
diff --git a/src/webserver/dist/map/bulgaria.png b/src/webserver/dist/map/bulgaria.png
new file mode 100644
index 0000000..c8edece
--- /dev/null
+++ b/src/webserver/dist/map/bulgaria.png
Binary files differ
diff --git a/src/webserver/dist/map/burundi.png b/src/webserver/dist/map/burundi.png
new file mode 100644
index 0000000..3f8d894
--- /dev/null
+++ b/src/webserver/dist/map/burundi.png
Binary files differ
diff --git a/src/webserver/dist/map/cambodia.png b/src/webserver/dist/map/cambodia.png
new file mode 100644
index 0000000..c92c6f3
--- /dev/null
+++ b/src/webserver/dist/map/cambodia.png
Binary files differ
diff --git a/src/webserver/dist/map/cameroon.png b/src/webserver/dist/map/cameroon.png
new file mode 100644
index 0000000..b5b57ea
--- /dev/null
+++ b/src/webserver/dist/map/cameroon.png
Binary files differ
diff --git a/src/webserver/dist/map/canada.png b/src/webserver/dist/map/canada.png
new file mode 100644
index 0000000..7358637
--- /dev/null
+++ b/src/webserver/dist/map/canada.png
Binary files differ
diff --git a/src/webserver/dist/map/chile.png b/src/webserver/dist/map/chile.png
new file mode 100644
index 0000000..e0a3c50
--- /dev/null
+++ b/src/webserver/dist/map/chile.png
Binary files differ
diff --git a/src/webserver/dist/map/colombia.png b/src/webserver/dist/map/colombia.png
new file mode 100644
index 0000000..3859cc0
--- /dev/null
+++ b/src/webserver/dist/map/colombia.png
Binary files differ
diff --git a/src/webserver/dist/map/costa.png b/src/webserver/dist/map/costa.png
new file mode 100644
index 0000000..035badd
--- /dev/null
+++ b/src/webserver/dist/map/costa.png
Binary files differ
diff --git a/src/webserver/dist/map/croatia.png b/src/webserver/dist/map/croatia.png
new file mode 100644
index 0000000..1ce5650
--- /dev/null
+++ b/src/webserver/dist/map/croatia.png
Binary files differ
diff --git a/src/webserver/dist/map/cyprus.png b/src/webserver/dist/map/cyprus.png
new file mode 100644
index 0000000..ea90e2a
--- /dev/null
+++ b/src/webserver/dist/map/cyprus.png
Binary files differ
diff --git a/src/webserver/dist/map/czech.png b/src/webserver/dist/map/czech.png
new file mode 100644
index 0000000..9f89545
--- /dev/null
+++ b/src/webserver/dist/map/czech.png
Binary files differ
diff --git a/src/webserver/dist/map/denmark.png b/src/webserver/dist/map/denmark.png
new file mode 100644
index 0000000..bef926b
--- /dev/null
+++ b/src/webserver/dist/map/denmark.png
Binary files differ
diff --git a/src/webserver/dist/map/dibouti.png b/src/webserver/dist/map/dibouti.png
new file mode 100644
index 0000000..43015e8
--- /dev/null
+++ b/src/webserver/dist/map/dibouti.png
Binary files differ
diff --git a/src/webserver/dist/map/ecuador.png b/src/webserver/dist/map/ecuador.png
new file mode 100644
index 0000000..bf2e9d4
--- /dev/null
+++ b/src/webserver/dist/map/ecuador.png
Binary files differ
diff --git a/src/webserver/dist/map/estonia.png b/src/webserver/dist/map/estonia.png
new file mode 100644
index 0000000..fa6ac33
--- /dev/null
+++ b/src/webserver/dist/map/estonia.png
Binary files differ
diff --git a/src/webserver/dist/map/ethopia.png b/src/webserver/dist/map/ethopia.png
new file mode 100644
index 0000000..207b3e7
--- /dev/null
+++ b/src/webserver/dist/map/ethopia.png
Binary files differ
diff --git a/src/webserver/dist/map/fiji.png b/src/webserver/dist/map/fiji.png
new file mode 100644
index 0000000..b574fc9
--- /dev/null
+++ b/src/webserver/dist/map/fiji.png
Binary files differ
diff --git a/src/webserver/dist/map/finland.png b/src/webserver/dist/map/finland.png
new file mode 100644
index 0000000..1a2754e
--- /dev/null
+++ b/src/webserver/dist/map/finland.png
Binary files differ
diff --git a/src/webserver/dist/map/haiti.png b/src/webserver/dist/map/haiti.png
new file mode 100644
index 0000000..c0773a6
--- /dev/null
+++ b/src/webserver/dist/map/haiti.png
Binary files differ
diff --git a/src/webserver/dist/map/honduras.png b/src/webserver/dist/map/honduras.png
new file mode 100644
index 0000000..fa7442d
--- /dev/null
+++ b/src/webserver/dist/map/honduras.png
Binary files differ
diff --git a/src/webserver/dist/map/hungary.png b/src/webserver/dist/map/hungary.png
new file mode 100644
index 0000000..97d9e91
--- /dev/null
+++ b/src/webserver/dist/map/hungary.png
Binary files differ
diff --git a/src/webserver/dist/map/iceland.png b/src/webserver/dist/map/iceland.png
new file mode 100644
index 0000000..d1f0e7d
--- /dev/null
+++ b/src/webserver/dist/map/iceland.png
Binary files differ
diff --git a/src/webserver/dist/map/india.png b/src/webserver/dist/map/india.png
new file mode 100644
index 0000000..e0fc17b
--- /dev/null
+++ b/src/webserver/dist/map/india.png
Binary files differ
diff --git a/src/webserver/dist/map/indonesia.png b/src/webserver/dist/map/indonesia.png
new file mode 100644
index 0000000..c31bf40
--- /dev/null
+++ b/src/webserver/dist/map/indonesia.png
Binary files differ
diff --git a/src/webserver/dist/map/iran.png b/src/webserver/dist/map/iran.png
new file mode 100644
index 0000000..2ee5b2b
--- /dev/null
+++ b/src/webserver/dist/map/iran.png
Binary files differ
diff --git a/src/webserver/dist/map/ireland.png b/src/webserver/dist/map/ireland.png
new file mode 100644
index 0000000..92fd6dc
--- /dev/null
+++ b/src/webserver/dist/map/ireland.png
Binary files differ
diff --git a/src/webserver/dist/map/israel.png b/src/webserver/dist/map/israel.png
new file mode 100644
index 0000000..3c350f2
--- /dev/null
+++ b/src/webserver/dist/map/israel.png
Binary files differ
diff --git a/src/webserver/dist/map/italy.png b/src/webserver/dist/map/italy.png
new file mode 100644
index 0000000..fb18140
--- /dev/null
+++ b/src/webserver/dist/map/italy.png
Binary files differ
diff --git a/src/webserver/dist/map/jamaica.png b/src/webserver/dist/map/jamaica.png
new file mode 100644
index 0000000..0cc2224
--- /dev/null
+++ b/src/webserver/dist/map/jamaica.png
Binary files differ
diff --git a/src/webserver/dist/map/lithunia.png b/src/webserver/dist/map/lithunia.png
new file mode 100644
index 0000000..633fc4b
--- /dev/null
+++ b/src/webserver/dist/map/lithunia.png
Binary files differ
diff --git a/src/webserver/dist/map/luxemburj.png b/src/webserver/dist/map/luxemburj.png
new file mode 100644
index 0000000..c296bd0
--- /dev/null
+++ b/src/webserver/dist/map/luxemburj.png
Binary files differ
diff --git a/src/webserver/dist/map/macao.png b/src/webserver/dist/map/macao.png
new file mode 100644
index 0000000..8c3017b
--- /dev/null
+++ b/src/webserver/dist/map/macao.png
Binary files differ
diff --git a/src/webserver/dist/map/malaysia.png b/src/webserver/dist/map/malaysia.png
new file mode 100644
index 0000000..7c14b5a
--- /dev/null
+++ b/src/webserver/dist/map/malaysia.png
Binary files differ
diff --git a/src/webserver/dist/map/malta.png b/src/webserver/dist/map/malta.png
new file mode 100644
index 0000000..bdbfc49
--- /dev/null
+++ b/src/webserver/dist/map/malta.png
Binary files differ
diff --git a/src/webserver/dist/map/mauritius.png b/src/webserver/dist/map/mauritius.png
new file mode 100644
index 0000000..6e39be8
--- /dev/null
+++ b/src/webserver/dist/map/mauritius.png
Binary files differ
diff --git a/src/webserver/dist/map/mexico.png b/src/webserver/dist/map/mexico.png
new file mode 100644
index 0000000..ac7d570
--- /dev/null
+++ b/src/webserver/dist/map/mexico.png
Binary files differ
diff --git a/src/webserver/dist/map/moldova.png b/src/webserver/dist/map/moldova.png
new file mode 100644
index 0000000..aea9bb2
--- /dev/null
+++ b/src/webserver/dist/map/moldova.png
Binary files differ
diff --git a/src/webserver/dist/map/namibia.png b/src/webserver/dist/map/namibia.png
new file mode 100644
index 0000000..013eb0c
--- /dev/null
+++ b/src/webserver/dist/map/namibia.png
Binary files differ
diff --git a/src/webserver/dist/map/netherland.png b/src/webserver/dist/map/netherland.png
new file mode 100644
index 0000000..ea929f8
--- /dev/null
+++ b/src/webserver/dist/map/netherland.png
Binary files differ
diff --git a/src/webserver/dist/map/nigeria.png b/src/webserver/dist/map/nigeria.png
new file mode 100644
index 0000000..fbb8c81
--- /dev/null
+++ b/src/webserver/dist/map/nigeria.png
Binary files differ
diff --git a/src/webserver/dist/map/norway.png b/src/webserver/dist/map/norway.png
new file mode 100644
index 0000000..8246c98
--- /dev/null
+++ b/src/webserver/dist/map/norway.png
Binary files differ
diff --git a/src/webserver/dist/map/panama.png b/src/webserver/dist/map/panama.png
new file mode 100644
index 0000000..2a080cb
--- /dev/null
+++ b/src/webserver/dist/map/panama.png
Binary files differ
diff --git a/src/webserver/dist/map/peru.png b/src/webserver/dist/map/peru.png
new file mode 100644
index 0000000..c0c759b
--- /dev/null
+++ b/src/webserver/dist/map/peru.png
Binary files differ
diff --git a/src/webserver/dist/map/philpine.png b/src/webserver/dist/map/philpine.png
new file mode 100644
index 0000000..4301c70
--- /dev/null
+++ b/src/webserver/dist/map/philpine.png
Binary files differ
diff --git a/src/webserver/dist/map/poland.png b/src/webserver/dist/map/poland.png
new file mode 100644
index 0000000..f16d57f
--- /dev/null
+++ b/src/webserver/dist/map/poland.png
Binary files differ
diff --git a/src/webserver/dist/map/portugal.png b/src/webserver/dist/map/portugal.png
new file mode 100644
index 0000000..bdec23d
--- /dev/null
+++ b/src/webserver/dist/map/portugal.png
Binary files differ
diff --git a/src/webserver/dist/map/puerto_rico.png b/src/webserver/dist/map/puerto_rico.png
new file mode 100644
index 0000000..e7c0b10
--- /dev/null
+++ b/src/webserver/dist/map/puerto_rico.png
Binary files differ
diff --git a/src/webserver/dist/map/russia.png b/src/webserver/dist/map/russia.png
new file mode 100644
index 0000000..e77b48f
--- /dev/null
+++ b/src/webserver/dist/map/russia.png
Binary files differ
diff --git a/src/webserver/dist/map/serbia.png b/src/webserver/dist/map/serbia.png
new file mode 100644
index 0000000..c3debe2
--- /dev/null
+++ b/src/webserver/dist/map/serbia.png
Binary files differ
diff --git a/src/webserver/dist/map/singapore.png b/src/webserver/dist/map/singapore.png
new file mode 100644
index 0000000..59e3aec
--- /dev/null
+++ b/src/webserver/dist/map/singapore.png
Binary files differ
diff --git a/src/webserver/dist/map/slovenia.png b/src/webserver/dist/map/slovenia.png
new file mode 100644
index 0000000..1dd5ebf
--- /dev/null
+++ b/src/webserver/dist/map/slovenia.png
Binary files differ
diff --git a/src/webserver/dist/map/south_africa.png b/src/webserver/dist/map/south_africa.png
new file mode 100644
index 0000000..ecdccb7
--- /dev/null
+++ b/src/webserver/dist/map/south_africa.png
Binary files differ
diff --git a/src/webserver/dist/map/spain.png b/src/webserver/dist/map/spain.png
new file mode 100644
index 0000000..b6947e3
--- /dev/null
+++ b/src/webserver/dist/map/spain.png
Binary files differ
diff --git a/src/webserver/dist/map/sweden.png b/src/webserver/dist/map/sweden.png
new file mode 100644
index 0000000..6b7ba10
--- /dev/null
+++ b/src/webserver/dist/map/sweden.png
Binary files differ
diff --git a/src/webserver/dist/map/switzerland.png b/src/webserver/dist/map/switzerland.png
new file mode 100644
index 0000000..6a8180a
--- /dev/null
+++ b/src/webserver/dist/map/switzerland.png
Binary files differ
diff --git a/src/webserver/dist/map/taiwan.png b/src/webserver/dist/map/taiwan.png
new file mode 100644
index 0000000..ca08390
--- /dev/null
+++ b/src/webserver/dist/map/taiwan.png
Binary files differ
diff --git a/src/webserver/dist/map/uk.png b/src/webserver/dist/map/uk.png
new file mode 100644
index 0000000..51f6e4d
--- /dev/null
+++ b/src/webserver/dist/map/uk.png
Binary files differ
diff --git a/src/webserver/dist/map/usa.png b/src/webserver/dist/map/usa.png
new file mode 100644
index 0000000..06cabde
--- /dev/null
+++ b/src/webserver/dist/map/usa.png
Binary files differ
diff --git a/src/webserver/dist/site.js b/src/webserver/dist/site.js
new file mode 100644
index 0000000..2e9bac3
--- /dev/null
+++ b/src/webserver/dist/site.js
@@ -0,0 +1,129 @@
+
+
+// the function to be called when the generate name button is pressed
+function generateName() {
+
+ // get the values of the various buttons and selectors and store them as variables
+ var gender = document.getElementById("gender").value;
+ var origin = document.getElementById("origin").value;
+
+ var count = parseInt(document.getElementById("number").value);
+ var surnames = document.getElementById("surnames").checked;
+
+ // create a request object
+ var request = {
+ "origin": origin,
+ "gender": gender,
+ "count": count,
+ "surname": surnames
+ };
+
+ // turn the request json into a string
+ request = JSON.stringify(request)
+
+ console.log(request)
+
+ // Make the http request using ajax
+ const http = new XMLHttpRequest();
+ const url = "/api/name";
+ http.open("POST", url);
+ http.send(request);
+
+ // create a listener for when the request is returned
+ http.onreadystatechange = (e) => {
+ showNames(JSON.parse(http.responseText))
+ }
+}
+
+// Change the page's html to show the generated names
+function showNames(names) {
+
+ // get the name area element
+ var nameArea = document.getElementById("namearea");
+
+ // delete all existing name entries
+ while (nameArea.lastChild) {
+ nameArea.removeChild(nameArea.lastChild);
+ }
+
+ // iterate through all of the given names
+ names.names.forEach((name) => {
+
+ // create the name element
+ var nameElement = document.createElement("p");
+ nameElement.textContent = name;
+ nameElement.classList.add("name");
+
+ // add the name element to the name area
+ nameArea.appendChild(nameElement);
+ });
+}
+
+function updateMap() {
+ // Load the current country of origin
+ var origin = document.getElementById("origin").value;
+
+ console.log(origin)
+
+ // get the canvas context for the worldmap
+ var canvas = document.getElementById("worldmap");
+ var context = canvas.getContext("2d");
+
+ // clear the canvas
+ context.clearRect(0, 0, canvas.width, canvas.height);
+
+ // create the background image
+ var background = new Image();
+ background.src = "/map/background.png"
+
+ // wait for the image to load before trying to draw it
+ background.onload = function() {
+ context.drawImage(background, 0, 0)
+
+ // only draw the country after the background has been drawn
+ var country = new Image();
+ country.src = "/map/" + origin + ".png"
+
+ // wait for the image to load before trying to draw it
+ country.onload = function() {
+ context.drawImage(country, 0, 0)
+ };
+
+ };
+
+}
+
+// force the count to be between 1 and 99
+function forceCount() {
+ var count = parseInt(document.getElementById("number").value);
+ if (count > 99) {
+ document.getElementById("number").value = 99
+ } else if (count < 1) {
+ document.getElementById("number").value = 1
+ }
+}
+
+// toggle the visibility of advanced options
+function toggleAdvanced() {
+ // get the advanced options div
+ var element = document.getElementById("advancedoptions");
+
+ // if display: none, make it visible and vice versa
+ if (element.style.display == "none") {
+ element.style.display = ""
+ } else {
+ element.style.display = "none"
+ }
+
+}
+
+// toggle the checkbox from checked to unchecked and vice versa
+function checkBox(checkbox) {
+ if (checkbox.getAttribute("checked")) {
+ // uncheck the box
+ checkbox.removeAttribute("checked");
+ } else {
+ // set the box to checked
+ checkbox.setAttribute("checked", true);
+ }
+}
diff --git a/src/webserver/dist/style.css b/src/webserver/dist/style.css
new file mode 100644
index 0000000..1ba596d
--- /dev/null
+++ b/src/webserver/dist/style.css
@@ -0,0 +1,138 @@
+#worldmap {
+ width: 50%;
+}
+
+body {
+ font-family: sans-serif;
+ background-color: #191919;
+ color: #f0c674;
+ width:auto;
+}
+
+.main {
+ padding: 0;
+ margin-left: 10%;
+ margin-right: 10%;
+ width: 80%;
+ font-size: 2rem;
+}
+
+#worldmap {
+ width: 50%;
+}
+
+@media (orientation : portrait) {
+ .main {
+ padding: 0;
+ margin: 0;
+ width: 100%;
+ font-size: 1.5rem;
+ }
+
+ #worldmap {
+ width: 100%;
+ }
+}
+
+
+.main {
+ display: inline-block;
+ text-align: center;
+
+}
+
+.box-item {
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+}
+
+
+button,select,input[type=number] {
+ font-size: inherit;
+ background-color: #191919;
+ color: #f0c674;
+ border-radius: 0.5em;
+ text-decoration: none;
+ padding: 0.5rem;
+ border: 0.05rem solid #373b41;
+ box-shadow: 0 0.2em #373b41;
+}
+
+#number {
+ width: 3rem;
+}
+img {
+ width: 2rem;
+}
+
+button:active {
+ background-color: #373b41;
+ box-shadow: 0 0.1em #373b41;
+ transform: translateY(0.1em);
+}
+
+
+.box {
+ margin-top: 10%;
+ padding: 1%;
+ text-align: center;
+ display: inline-block;
+ box-sizing: border-box;
+ margin-left:15%;
+ width: 70%;
+ height: 50%;
+
+ background: transparent;
+ border: 0;
+ outline: 0;
+ border-bottom: 0.25rem solid #0f7d7b;
+ color: #e8922a;
+ font-size: 100%;
+ outline: none !important;
+
+}
+input:focus, textarea {
+ outline: none !important;
+}
+button:focus, textarea {
+ outline: none !important;
+}
+
+
+input[type=checkbox]{
+ position:absolute;
+ opacity: 0;
+ z-index: -9999;
+}
+
+input[type=checkbox] + .indicator {
+ width: 1.5rem;
+ height: 1.5rem;
+ margin-left: 0.5rem;
+ position: absolute;
+
+
+ font-size: inherit;
+ background-color: #191919;
+ color: #191919;
+ border-radius: 0.5em;
+ text-decoration: none;
+ padding: 0.5rem;
+ border: 0.05rem solid #373b41;
+ box-shadow: 0 0.2em #373b41;
+
+ content: "";
+}
+
+input[type=checkbox]:checked + .indicator {
+ background-color: #373b41;
+ box-shadow: 0 0.1em #373b41;
+ transform: translateY(0.1em);
+}
+
+input[type=checkbox]+ .indicator::after {
+ content: "";
+}
+input[type=checkbox]:checked + .indicator::after {
+ content: "X";
+}
diff --git a/src/webserver/dist/subdir/image.png b/src/webserver/dist/subdir/image.png
new file mode 100644
index 0000000..80fb972
--- /dev/null
+++ b/src/webserver/dist/subdir/image.png
Binary files differ
diff --git a/src/webserver/dist/subdir/index.html b/src/webserver/dist/subdir/index.html
new file mode 100644
index 0000000..b7e784a
--- /dev/null
+++ b/src/webserver/dist/subdir/index.html
@@ -0,0 +1 @@
+index of the sub dir
diff --git a/src/webserver/dist/subdir/test.txt b/src/webserver/dist/subdir/test.txt
new file mode 100644
index 0000000..6ee329a
--- /dev/null
+++ b/src/webserver/dist/subdir/test.txt
@@ -0,0 +1 @@
+this is text.txt
diff --git a/src/webserver/pom.xml b/src/webserver/pom.xml
new file mode 100644
index 0000000..c4a716d
--- /dev/null
+++ b/src/webserver/pom.xml
@@ -0,0 +1,47 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>io.github.davidovski.names</groupId>
+ <artifactId>NameGenerator</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.xerial</groupId>
+ <artifactId>sqlite-jdbc</artifactId>
+ <version>3.32.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20210307</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <transformers>
+ <transformer implementation=
+ "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <mainClass>io.github.davidovski.names.WebServer</mainClass>
+ </transformer>
+ </transformers>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+</project>
diff --git a/src/webserver/src/main/java/io/github/davidovski/names/APIRequestHandler.java b/src/webserver/src/main/java/io/github/davidovski/names/APIRequestHandler.java
new file mode 100644
index 0000000..5ef2cfe
--- /dev/null
+++ b/src/webserver/src/main/java/io/github/davidovski/names/APIRequestHandler.java
@@ -0,0 +1,112 @@
+package io.github.davidovski.names;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+@SuppressWarnings("restriction")
+public class APIRequestHandler implements HttpHandler {
+
+ private NameDatabaseManager nameDatabaseManager;
+
+ public APIRequestHandler() {
+ nameDatabaseManager = new NameDatabaseManager(new File("names.db"));
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ // get the requested path
+ String path = exchange.getRequestURI().getPath();
+
+ System.out.println(path);
+
+ if (path.startsWith("/api/name")) {
+
+ // parse the body as a json object
+ JSONTokener jsonParser = new JSONTokener(exchange.getRequestBody());
+ JSONObject json = (JSONObject) jsonParser.nextValue();
+
+ System.out.println(json.toString(2));
+
+ if (json == null) {
+ // Malformed JSON request, 400
+ sendJSON(exchange, (new JSONObject()).put("message", "Malformed JSON body"), 400);
+ return;
+ }
+
+ // generate name(s) and send it
+ generateName(exchange, json);
+ } else {
+ sendJSON(exchange, (new JSONObject()).put("message", "Not Found"), 404);
+ }
+ }
+
+ public void generateName(HttpExchange exchange, JSONObject options) throws JSONException, IOException {
+ String origin = options.optString("origin", "none").toLowerCase();
+
+ int count = options.optInt("count", 1);
+
+ // ensure that the count is between 1-100
+ if (count < 1 || count > 100) {
+ sendJSON(exchange, (new JSONObject()).put("message", "Name count is out of range: Ensure that the request is between 1 and 100 names"), 400);
+ return;
+ }
+
+ String gender = options.optString("gender", "female");
+
+ // ensure that the gender is either male or female
+ if (!gender.equals("male") && !gender.equals("female")) {
+ sendJSON(exchange, (new JSONObject()).put("message", "Requested gender is invalid"), 400);
+ return;
+ }
+
+ // Store the names in an array
+ List<String> names = nameDatabaseManager.getRandomNames(origin, gender, count);
+
+ if (options.optBoolean("surname")) {
+ List<String> surnames = nameDatabaseManager.getRandomNames(origin, "surname", count);
+
+ // Add surnames to the end of each firstname in the names list
+ for (int i = 0; i < count; i++) {
+ String fullname = names.get(i) + " " + surnames.get(i);
+ names.set(i, fullname);
+ }
+ }
+
+ // Create the response json object
+ JSONObject response = new JSONObject();
+ response.put("message", "Generated " + count + " names");
+ response.put("names", new JSONArray(names));
+
+ // send the json back to the client
+ sendJSON(exchange, response, 200);
+ }
+
+ public void sendJSON(HttpExchange exchange, JSONObject json, int responseCode) throws IOException {
+ // convert the json to a string
+ String response = json.toString(2);
+
+ // calculate the response content size
+ int contentSize = response.toString().getBytes().length;
+
+ // set the response headers
+ exchange.getResponseHeaders().add("Content-Type", "text/json");
+ exchange.sendResponseHeaders(responseCode, contentSize);
+
+ // write the response to the output stream
+ OutputStream outputStream = exchange.getResponseBody();
+
+ outputStream.write(response.toString().getBytes());
+ outputStream.close();
+ }
+
+}
diff --git a/src/webserver/src/main/java/io/github/davidovski/names/NameDatabaseManager.java b/src/webserver/src/main/java/io/github/davidovski/names/NameDatabaseManager.java
new file mode 100644
index 0000000..8822d9c
--- /dev/null
+++ b/src/webserver/src/main/java/io/github/davidovski/names/NameDatabaseManager.java
@@ -0,0 +1,75 @@
+package io.github.davidovski.names;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class NameDatabaseManager {
+ private File databaseFile;
+ private Connection connection;
+
+ private static final String TABLE = "names";
+
+ public NameDatabaseManager(File databaseFile) {
+ this.databaseFile = databaseFile;
+
+ connect();
+ }
+
+ /**
+ * Creates a connection to the database. If one could not be created, the connection will remain as null
+ */
+ private void connect() {
+ connection = null;
+ try {
+ connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getPath());
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public List<String> getRandomNames(String origin, String category, int quantitiy) {
+ // create the set to return, even if empty
+ List<String> names = new ArrayList<String>();
+
+ if (connection != null) {
+ // Create an sql statement
+ String sql = "SELECT Name FROM " + TABLE + " WHERE Origin=? AND Category=? ORDER BY RANDOM() LIMIT ?;";
+ PreparedStatement statement;
+ try {
+ statement = connection.prepareStatement(sql);
+
+ // insert relevant values into the statement
+ statement.setString(1, origin);
+ statement.setString(2, category);
+ statement.setInt(3, quantitiy);
+
+ // execute the query and get the result
+ ResultSet result = statement.executeQuery();
+
+ // Add all of the results to the names set
+ while (result.next()) {
+ String name = result.getString("Name");
+ names.add(name);
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ }
+ return names;
+ }
+
+ public static void main(String[] args) {
+ NameDatabaseManager dbManager = new NameDatabaseManager(new File("names.db"));
+ List<String> names = dbManager.getRandomNames("spain", "female", 10);
+ for (String name : names) {
+ System.out.println(name);
+ }
+ }
+}
diff --git a/src/webserver/src/main/java/io/github/davidovski/names/StaticRequestHandler.java b/src/webserver/src/main/java/io/github/davidovski/names/StaticRequestHandler.java
new file mode 100644
index 0000000..d10d0b1
--- /dev/null
+++ b/src/webserver/src/main/java/io/github/davidovski/names/StaticRequestHandler.java
@@ -0,0 +1,85 @@
+package io.github.davidovski.names;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+@SuppressWarnings("restriction")
+public class StaticRequestHandler implements HttpHandler {
+ private File root;
+
+ public StaticRequestHandler(File root) {
+ this.root = root;
+ }
+
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ // get the requested path
+ String path = exchange.getRequestURI().getPath();
+
+ File requested = new File(root, path);
+
+ // automatically send the index of a directory
+ if (requested.isDirectory()) {
+ requested = new File(requested, "index.html");
+ }
+
+ // ensure that the file is in the intended document root
+ if (!isInRoot(requested, root)) {
+ sendText(exchange, "Access Denied", 403);
+ } else if (requested.exists()) {
+ sendFile(exchange, requested);
+ } else {
+ // send 404 if the file isnt found
+ sendText(exchange, "File Not Found", 404);
+ }
+ }
+
+ private void sendFile(HttpExchange exchange, File file) throws IOException {
+ // read the file as into an array of bytes
+ byte[] bytes = Files.readAllBytes(file.toPath());
+
+ // send the file headers
+ exchange.sendResponseHeaders(200, bytes.length);
+
+ // send the file body
+ OutputStream os = exchange.getResponseBody();
+ os.write(bytes);
+ os.close();
+ }
+
+ private void sendText(HttpExchange exchange, String response, int responseCode) throws IOException {
+ // calculate the response content size
+ int contentSize = response.toString().getBytes().length;
+
+ // set the response headers
+ exchange.getResponseHeaders().add("Content-Type", "text/json");
+ exchange.sendResponseHeaders(responseCode, contentSize);
+
+ // write the response to the output stream
+ OutputStream outputStream = exchange.getResponseBody();
+
+ outputStream.write(response.toString().getBytes());
+ outputStream.close();
+ }
+
+ public static boolean isInRoot(File request, File root) {
+ File parentFile = request;
+
+ // start from the requested file and traverse upwards until reaching the root directory
+ while (parentFile != null) {
+ if (root.equals(parentFile)) {
+ return true;
+ }
+ parentFile = parentFile.getParentFile();
+ }
+
+ // If there isn't a parent file, return false
+ return false;
+ }
+
+}
diff --git a/src/webserver/src/main/java/io/github/davidovski/names/WebServer.java b/src/webserver/src/main/java/io/github/davidovski/names/WebServer.java
new file mode 100644
index 0000000..60b311f
--- /dev/null
+++ b/src/webserver/src/main/java/io/github/davidovski/names/WebServer.java
@@ -0,0 +1,51 @@
+package io.github.davidovski.names;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import com.sun.net.httpserver.HttpServer;
+
+@SuppressWarnings("restriction")
+public class WebServer {
+ private int port;
+ private HttpServer server;
+
+ public WebServer(int port) throws IOException {
+ this.port = port;
+
+ // create an HTTP server instance
+ InetSocketAddress socketAddress = new InetSocketAddress(port);
+ server = HttpServer.create(socketAddress, 0);
+
+ // create a context for the static request handler
+ server.createContext("/", new StaticRequestHandler(new File("dist")));
+ // create a context for the api request handler
+ server.createContext("/api", new APIRequestHandler());
+
+ }
+
+ public void start() {
+ server.start();
+
+ // tell the user that the webserver has been started, and the port used
+ System.out.println("Webserver started on port " + port);
+ }
+
+
+ public static void main(String[] args) throws IOException {
+
+ // set a default port number
+ int port = 8080;
+
+ // parse the arguments for a port number
+ if (args.length > 0)
+ port = Integer.parseInt(args[0]);
+
+ // Create the webserver instance
+ WebServer webServer = new WebServer(port);
+
+ // start the webserver
+ webServer.start();
+ }
+}