/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
/* global forge */
/* global OV */
/* global TD */

describe('WriteIn api', function() {

    'use strict';

    var p_50_bits = new forge.jsbn.BigInteger("632675155738559");
    var q_50_bits = new forge.jsbn.BigInteger("316337577869279");

    var p_1024_bits = new forge.jsbn.BigInteger("127221604274531153456037811894129045050707427287018126451033610258225414570293456088114804760360499690320725081015613357713860609352611107211595355579330171109403165025745399040683371804753828679517508339451541325761038070090645847220458502400371988111691904960115084260054586776319747845360881674068179759939");
    var q_1024_bits = new forge.jsbn.BigInteger("63610802137265576728018905947064522525353713643509063225516805129112707285146728044057402380180249845160362540507806678856930304676305553605797677789665085554701582512872699520341685902376914339758754169725770662880519035045322923610229251200185994055845952480057542130027293388159873922680440837034089879969");

    var p_2048_bits = new forge.jsbn.BigInteger("16370518994319586760319791526293535327576438646782139419846004180837103527129035954742043590609421369665944746587885814920851694546456891767644945459124422553763416586515339978014154452159687109161090635367600349264934924141746082060353483306855352192358732451955232000593777554431798981574529854314651092086488426390776811367125009551346089319315111509277347117467107914073639456805159094562593954195960531136052208019343392906816001017488051366518122404819967204601427304267380238263913892658950281593755894747339126531018026798982785331079065126375455293409065540731646939808640273393855256230820509217411510058759");
    var q_2048_bits = new forge.jsbn.BigInteger("8185259497159793380159895763146767663788219323391069709923002090418551763564517977371021795304710684832972373293942907460425847273228445883822472729562211276881708293257669989007077226079843554580545317683800174632467462070873041030176741653427676096179366225977616000296888777215899490787264927157325546043244213195388405683562504775673044659657555754638673558733553957036819728402579547281296977097980265568026104009671696453408000508744025683259061202409983602300713652133690119131956946329475140796877947373669563265509013399491392665539532563187727646704532770365823469904320136696927628115410254608705755029379");

    var p_3072_bits = new forge.jsbn.BigInteger("3878981618363851403422785607049427732736469525765273964840494068543041817727859944747888587785804791336126334735655317568383325026575396502302408923900497193860574810106417647833293156361473237473851593342226591737416569274097190298794282094798839851447612748747028941226550444236500328284988232883708279903371633902500527667563618336033732682721663174483318675000290047145413356364086211940765433079496799025339463611957630842302927071374023550249041067140503160804595502533730751141852343308661226499943816174201254213525974778805126213286835381698767182787838530784771675962353210590958127004175441983883147456311021868868237863493150560311147255716532038537882133905836196893266079070881906364328006937745533954805623786263044779903135192824449840823134335864515601600386396870002243889016433896192256236768999995365293442167853292116446399454096548352527896137561043092342974345122064784985329026450531982131124347170447");
    var q_3072_bits = new forge.jsbn.BigInteger("1939490809181925701711392803524713866368234762882636982420247034271520908863929972373944293892902395668063167367827658784191662513287698251151204461950248596930287405053208823916646578180736618736925796671113295868708284637048595149397141047399419925723806374373514470613275222118250164142494116441854139951685816951250263833781809168016866341360831587241659337500145023572706678182043105970382716539748399512669731805978815421151463535687011775124520533570251580402297751266865375570926171654330613249971908087100627106762987389402563106643417690849383591393919265392385837981176605295479063502087720991941573728155510934434118931746575280155573627858266019268941066952918098446633039535440953182164003468872766977402811893131522389951567596412224920411567167932257800800193198435001121944508216948096128118384499997682646721083926646058223199727048274176263948068780521546171487172561032392492664513225265991065562173585223");

    var alphabet = '# ()0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ`abcdefghijklmnopqrstuvwxyz ¡¢ŠšŽžŒœŸÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ';

    it('should produce expected encoding size when no writeins and no representations', function() {

        var fields = [];
        var usableRepresentationsEncode = [];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = [];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a single character', function() {

        var fields = ['2#a'];
        var usableRepresentationsEncode = ['2'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a list of short strings, using p of size 50 bits', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];

        var numDesiredElementsInEncoding = 12;

        var encoded = OV.encode(fields, alphabet, p_50_bits, q_50_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67'];
        var decoded = OV.decode(encoded, alphabet, p_50_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a list of short strings, using p of size 1024 bits', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_1024_bits, q_1024_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67'];
        var decoded = OV.decode(encoded, alphabet, p_1024_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a list of short strings, using p of size 2048 bits', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a list of short strings, using p of size 3072 bits', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_3072_bits, q_3072_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67'];
        var decoded = OV.decode(encoded, alphabet, p_3072_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a string that includes spaces', function() {

        var fields = ['2#Fred Flintstone'];
        var usableRepresentationsEncode = ['2'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode multiple strings that include spaces', function() {

        var fields = ['2#Elvis Presley', '17#Micky Mouse', '67#Pope John Paul II', '107#Fred Flintstone'];
        var usableRepresentationsEncode = ['2', '17', '67', '107'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a list of strings that contain a single character', function() {

        var fields = ['2#a', '17#b', '67#c', '107#d', '139#e', '151#f', '163#g', '197#h', '229#i', '241#j', '367#k'];
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a list of strings that only includes spaces', function() {

        var fields = ['2#                ',
                      '17#                ',
                      '67#                ',
                      '107#                ',
                      '139#                '];
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a large amount of data', function() {

        var fields = get8StringsOf100Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a very large amount of data, using p of size 50 bits', function() {

        var fields = generate20StringsOf120Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];

        var numDesiredElementsInEncoding = 1248;

        var encoded = OV.encode(fields, alphabet, p_50_bits, q_50_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];
        var decoded = OV.decode(encoded, alphabet, p_50_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a very large amount of data, using p of size 1024 bits', function() {

        var fields = generate20StringsOf120Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];

        var numDesiredElementsInEncoding = 49;

        var encoded = OV.encode(fields, alphabet, p_1024_bits, q_1024_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];
        var decoded = OV.decode(encoded, alphabet, p_1024_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a very large amount of data, and the desired size of the encoding is one less than the actual size, using p of size 2048 bits', function() {

        var fields = generate20StringsOf120Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];

        var numDesiredElementsInEncoding = 24;

        expect(function() {
            var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('The encoding has more elements than the desired number. Desired number: 24, actual number: 25'));
    });

    it('should encode and decode a very large amount of data, and the desired size of the encoding is exactly equal to the actual size, using p of size 2048 bits', function() {

        var fields = generate20StringsOf120Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];

        var numDesiredElementsInEncoding = 25;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a very large amount of data, and the desired size of the encoding is one more than the actual size, using p of size 2048 bits', function() {

        var fields = generate20StringsOf120Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];

        var numDesiredElementsInEncoding = 26;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode a very large amount of data, using p of size 3072 bits', function() {

        var fields = generate20StringsOf120Characters();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];

        var numDesiredElementsInEncoding = 17;

        var encoded = OV.encode(fields, alphabet, p_3072_bits, q_3072_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757', '701', '821'];
        var decoded = OV.decode(encoded, alphabet, p_3072_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode strings of various lengths', function() {

        var fields = generateStringsOfVariousLengths();
        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '373', '431', '463', '601', '683', '719', '757'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode strings that contain all the alphabet characters', function() {

        var alphabetWithoutSeparator = alphabet.substring(1);
        var representation = '2';
        var bothPartsOfWritinString = representation + '#' + alphabetWithoutSeparator;
        var fields = [bothPartsOfWritinString];
        var usableRepresentationsEncode = ['2'];

        var numDesiredElementsInEncoding = 10;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['2'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode 102 chars can be encoded in one element regardless of chars', function() {
    
        ///////////////////////////////////
        //
        // test one of the initial characters in alphabet
        //
        ///////////////////////////////////

        var stringWith102InitialChars = '1#((((((((((';
        for (var i = 0; i < 9; i++) {
            stringWith102InitialChars += '((((((((((';
        }
        var fields = [stringWith102InitialChars];

        var usableRepresentationsEncode = ['1'];

        var numDesiredElementsInEncoding = 1;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['1'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);

        ///////////////////////////////////
        //
        // test last character in alphabet
        //
        ///////////////////////////////////

        var stringWith102LastChars = '1#ÿÿÿÿÿÿÿÿÿÿ';
        for (var i = 0; i < 9; i++) {
            stringWith102LastChars += 'ÿÿÿÿÿÿÿÿÿÿ';
        }
        var fields = [stringWith102LastChars];

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode 6 char prime plus seperator plus 95 char string', function() {

        var composedOf6charPrime1charSeperator95CharString = '478453#34567890';
        for (var i = 0; i < 8; i++) {
            composedOf6charPrime1charSeperator95CharString += '1234567890';
        }

        var fields = [composedOf6charPrime1charSeperator95CharString];

        var usableRepresentationsEncode = ['478453'];

        var numDesiredElementsInEncoding = 1;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['478453'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    it('should encode and decode 6 char prime plus seperator plus 95 char string', function() {

        var stringWith150Chars = '1#34567890';
        for (var i = 0; i < 9; i++) {
            stringWith150Chars += '1234567890';
        }

        var stringWith20Chars = "2#11111111";
        stringWith20Chars += "1111111111";

        var fields = [stringWith150Chars, stringWith20Chars];

        var usableRepresentationsEncode = ['1', '2'];

        var numDesiredElementsInEncoding = 2;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        expect(encoded.length).toEqual(numDesiredElementsInEncoding);

        var usableRepresentationsDecode = ['1', '2'];
        var decoded = OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);

        expect(fields).toEqual(decoded);
    });

    //////////////////////////////////////////////////////
    //
    // Exception handling
    //
    //////////////////////////////////////////////////////
	
	it('should refuse to decode empty write-ins', function() {
		
		// Prepare encoding of the following write in: 1#A#2#
        // With numeric representation: 008 003 022 003 009 003
		var encodedWriteIns = new forge.jsbn.BigInteger('008003022003009003').pow(2);
        var encoded = [ encodedWriteIns ];
		
		var usableRepresentationsDecode = ['1', '2'];

        expect(function() {
            OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);
        }).toThrow(new Error('Exception during decoding - empty write-in is not valid'));
		
	});

    it('should refuse to encode strings that include invalid characters', function() {

        var fields = ['2#eeny',
                      '27#meeny',
                      '67#mi/ny'];
        var usableRepresentationsEncode = ['2', '27', '67'];

        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Invalid character for writein encoding: /'));
    });

    it('should refuse to encode strings including the reserved separator char', function() {

        var fields = ['2#eeny',
                      '17#m#eeny',
                      '67#miny'];
        var usableRepresentationsEncode = ['2', '27', '67'];

        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('There should be exactly one separator character in each writein string, but this was not the case for: 17#m#eeny'));
    });

    it('should refuse to encode if list of strings is null', function() {

        var fields;

        var usableRepresentationsEncode = ['2', '27', '67'];

        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Exception during encoding - there are valid representations but no write-ins'));
    });

    it('should refuse to decode if list of strings is null', function() {

        var numDesiredElementsInEncoding = 10;

        var encoded = [];
        for(var i = 0; i < numDesiredElementsInEncoding; i++) {
            encoded.push(new forge.jsbn.BigInteger('2'));
        }

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367'];

        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.decode(encoded, alphabet, p_2048_bits, q_2048_bits, usableRepresentationsDecode);
        }).toThrow(new Error('Exception during decoding - there are valid representations but no write-ins'));
    });

    it('should refuse to encode list of strings is empty', function() {

        var fields = [];
        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367'];

        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsDecode);
        }).toThrow(new Error('Exception during encoding - there are valid representations but no write-ins'));
    });

    it('should refuse to encode if the alphabet is null', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];
        var alphabetNull;
        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabetNull, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Exception during encoding - the alphabet was not initialized'));
    });

    it('should refuse to encode if the p parameter is null', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];
        var pNull;
        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabet, pNull, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('The p parameter was not initialized'));
    });

    it('should refuse to encode if the q parameter is null', function() {

        var fields = ['2#eeny', '17#meeny', '67#miny'];
        var usableRepresentationsEncode = ['2', '17', '67'];
        var qNull;
        var numDesiredElementsInEncoding = 10;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, qNull, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('The q parameter was not initialized'));
    });

    it('should refuse to encode if the p parameter is too small', function() {

        var fields = ['2#eeny'];
        var usableRepresentationsEncode = ['2'];
        var numDesiredElementsInEncoding = 10;

        var pSmall = new forge.jsbn.BigInteger("23");
        var qSmall = new forge.jsbn.BigInteger("11");

        expect(function() {
            OV.encode(fields, alphabet, pSmall, qSmall, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Encoded writein too big for the mathematical group. Value: 9003048048057068, p: 23'));
    });

    it('should refuse to encode if the num desired elements is null', function() {

        var fields = ['2#eeny'];
        var usableRepresentationsEncode = ['2'];
        var numDesiredElementsInEncoding;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('The number of desired elements was not initialized'));
    });

    it('should refuse to encode if the num desired elements is less than one', function() {

        var fields = ['2#eeny'];
        var usableRepresentationsEncode = ['2'];
        var numDesiredElementsInEncoding = 0;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('The number of desired elements was 0'));
    });

    it('should refuse to encode if a writeins has non-existing representation', function() {

        var fields = ['1#34567890', '2#11111111'];

        var usableRepresentationsEncode = ['1', '3'];
        var numDesiredElementsInEncoding = 2;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Exception during encoding - invalid representation found in encoded write-in: 2'));
    });

    it('should refuse to decode if a writeins has non-existing representation', function() {

        var element1 = new forge.jsbn.BigInteger('64048169236358500663849057129246379287397362373369349412557608601668511434281136976800608560576496512448384320256192128064');
        var element2 = new forge.jsbn.BigInteger('2');
        var encoded = [element1, element2];

        var usableRepresentationsDecode = ['1'];

        expect(function() {
            OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);
        }).toThrow(new Error('Exception during decoding - invalid representation found in encoded write-in: 2'));
    });

    it('should refuse to encode if two writeins have the same representation', function() {

        var fields = ['1#34567890', '1#11111111'];

        var usableRepresentationsEncode = ['1', '2'];
        var numDesiredElementsInEncoding = 2;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Exception during encoding - repeated representation: 1'));
    });

    it('should refuse to decode if two writeins have the same representation', function() {

        var element1 = new forge.jsbn.BigInteger('64048169236358500663849057129246363281377340349343321382525594595651505418265120960784592544560496512448384320256192128064');
        var element2 = new forge.jsbn.BigInteger('2');
        var encoded = [element1, element2];

        var usableRepresentationsDecode = ['1'];
        var numDesiredElementsInEncoding = 2;

        expect(function() {
            OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);
        }).toThrow(new Error('Exception during decoding - repeated representation: 1'));
    });

    it('should refuse to encode unused representation', function() {

        var fields = ['2#a',
                      '17#b',
                      '67#c',
                      '107#d',
                      '139#e',
                      '151#f',
                      '163#g',
                      '197#h',
                      '229#i',
                      '241#j',
                      '367#k'];

        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '51545'];
        var numDesiredElementsInEncoding = 2;

        expect(function() {
            OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);
        }).toThrow(new Error('Exception during encoding - representations not used: 51545'));
    });

    it('should refuse to decode unused representation', function() {

        var fields = ['2#a',
                      '17#b',
                      '67#c',
                      '107#d',
                      '139#e',
                      '151#f',
                      '163#g',
                      '197#h',
                      '229#i',
                      '241#j',
                      '367#k'];

        var usableRepresentationsEncode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367'];
        var numDesiredElementsInEncoding = 2;

        var encoded = OV.encode(fields, alphabet, p_2048_bits, q_2048_bits, numDesiredElementsInEncoding, usableRepresentationsEncode);

        var usableRepresentationsDecode = ['2', '17', '67', '107', '139', '151', '163', '197', '229', '241', '367', '51545'];

        expect(function() {
            OV.decode(encoded, alphabet, p_2048_bits, usableRepresentationsDecode);
        }).toThrow(new Error('Exception during decoding - representations not used: 51545'));
    });

    it('should convert a writein encoding into a group member', function() {

        var number = new forge.jsbn.BigInteger('1234567');
        expect(OV.makeGroupElement(number, p_2048_bits).toString())
            .toBe('1524155677489');
    });

    it('should refuse to convert a writein into a group memmber if not smaller than p', function() {

        var number = p_2048_bits.add(forge.jsbn.BigInteger.ONE);

        expect(function name() {
            OV.makeGroupElement(number, p_2048_bits);
        }).toThrow();

    });

    //////////////////////////////////////////////////////
    //
    // Square and squareroot
    //
    //////////////////////////////////////////////////////

    it('square and squareroot recovers original value 10', function() {

        var p = p_2048_bits;
        var q = q_2048_bits;
        var value = new forge.jsbn.BigInteger('10');

        var square = OV.squareToEnsureIsGroupElement(value, p, q);

        var result = OV.sqrtN(square);

        expect(value.equals(result)).toBe(true);
    });

    it('square and squareroot recovers original value 10 digits', function() {

        var p = p_2048_bits;
        var q = q_2048_bits;
        var value = new forge.jsbn.BigInteger('1234567890');

        var square = OV.squareToEnsureIsGroupElement(value, p, q);

        var result = OV.sqrtN(square);

        expect(value.toString()).toEqual(result.toString());
    });

    it('square and squareroot recovers original value 100 digits', function() {

        var p = p_2048_bits;
        var q = q_2048_bits;
        var value = new forge.jsbn.BigInteger('1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

        var square = OV.squareToEnsureIsGroupElement(value, p, q);

        var result = OV.sqrtN(square);

        expect(value.toString()).toEqual(result.toString());
    });

    it('square and squareroot recovers original value 200 digits', function() {

        var p = p_2048_bits;
        var q = q_2048_bits;
        var value = new forge.jsbn.BigInteger('12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

        var square = OV.squareToEnsureIsGroupElement(value, p, q);

        var result = OV.sqrtN(square);

        expect(value.toString()).toEqual(result.toString());
    });

    it('square and squareroot recovers original value 300 digits', function() {

        var p = p_2048_bits;
        var q = q_2048_bits;
        var value = new forge.jsbn.BigInteger('123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

        var square = OV.squareToEnsureIsGroupElement(value, p, q);

        var result = OV.sqrtN(square);

        expect(value.toString()).toEqual(result.toString());
    });

    var get8StringsOf100Characters = function() {


        var fields = ['2#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
        '17#BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB',
        '67#CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC',
        '107#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD',
        '139#EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE',
        '151#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
        '163#GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG',
        '197#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH'];

        return fields;
    };

    var generate20StringsOf120Characters = function() {


        var fields = ['2#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
        '17#bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
        '67#cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
        '107#dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd',
        '139#eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
        '151#ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
        '163#gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg',
        '197#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh',
        '229#iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii',
        '241#jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj',
        '367#kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkZZZZZZZZZZZZZZZZZZZZZZZ',
        '373#llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll',
        '431#mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm',
        '463#n n n n n n n n n n n n n n n n n n n n n n n n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn',
        '601#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooopppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp',
        '683#ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ',
        '719#ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ',
        '701#ææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææææ',
        '757#start of spaces                                                                                            end of spaces',
        '821#çççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççççç'];

        return fields;
    };

    var generateStringsOfVariousLengths = function() {


        var fields = ['2#A',
        '17#BB',
        '67#CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC',
        '107#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD',
        '139#EEEEEEEEEEEEEEEEEEEEEEEEEE',
        '151#FFFF',
        '163#GGGGGGGGGGGGGGGGGG',
        '197#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH',
        '229#IIIIIIIIIIIIIIIIIIIIIIIIII',
        '241#JJJJJJJJJJJJJ',
        '367#KKKKKKK',
        '373#LLL',
        '431#MM',
        '463#N',
        '601#OO',
        '683#PPPPPP',
        '719#QQQQQQQQQQQ',
        '757#RRRRRRRRRRRRRRRRRR'];

        return fields;
    };
});
