/*
 * 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
 */
/* jshint node:true */
'use strict';

var mathematical = require('../lib/index');
var CommonTestData = require('./data/common-data');
var ValidationTestData = require('./data/validation-data');

describe('The mathematical module should be able to ...', function() {
  var mathService_;
  var mathArrayCompressor_;
  var mathRandomGenerator_;
  var mathGroupHandler_;

  var g_;
  var p_;
  var q_;
  var group_;
  var qrGroup_;
  var elementValue_;
  var element_;
  var exponentValue_;
  var exponent_;
  var elements_;
  var exponents_;
  var elementFromAnotherGroup_;
  var exponentFromAnotherGroup_;
  var multiGroupElementValues_;
  var minCertaintyLevel_;

  var nonObject_;
  var emptyObject_;
  var nonSecureRandomService_;
  var nonBoolean_;
  var nonNumber_;
  var nonPositiveNumber_;
  var nonString_;
  var nonJsonString_;
  var nonArray_;
  var nonMathObject_;
  var nonMathObjectArray_;
  var tooSmallModulus_;
  var tooSmallOrder_;
  var tooLargeOrder_;
  var tooSmallGenerator_;
  var tooLargeGenerator_;
  var tooSmallElementValue_;
  var tooLargeElementValue_;
  var tooSmallModulusBitLength_;
  var tooLowCertaintyLevel_;
  var tooLargeNumMembersRequired_;

  beforeAll(function() {
    mathService_ = mathematical.newService();
    mathArrayCompressor_ = mathService_.newArrayCompressor();
    mathRandomGenerator_ = mathService_.newRandomGenerator();
    mathGroupHandler_ = mathService_.newGroupHandler();

    var commonTestData = new CommonTestData();
    p_ = commonTestData.getP();
    q_ = commonTestData.getQ();
    g_ = commonTestData.getG();
    group_ = commonTestData.getGroup();
    qrGroup_ = commonTestData.getQuadraticResidueGroup();
    elementValue_ = commonTestData.getElementValue();
    element_ = commonTestData.getElement();
    exponentValue_ = commonTestData.getExponentValue();
    exponent_ = commonTestData.getExponent();
    elements_ = commonTestData.getElements();
    exponents_ = commonTestData.getExponents();
    elementFromAnotherGroup_ = commonTestData.getElementFromAnotherGroup();
    exponentFromAnotherGroup_ = commonTestData.getExponentFromAnotherGroup();
    multiGroupElementValues_ = commonTestData.multiGroupElementValues();
    minCertaintyLevel_ = commonTestData.getMinimumCertaintyLevel();

    var validationTestData = new ValidationTestData();
    nonObject_ = validationTestData.getNonObject();
    emptyObject_ = validationTestData.getEmptyObject();
    nonSecureRandomService_ = validationTestData.getNonSecureRandomService();
    nonBoolean_ = validationTestData.getNonBoolean();
    nonNumber_ = validationTestData.getNonNumber();
    nonPositiveNumber_ = validationTestData.getNonPositiveNumber();
    nonString_ = validationTestData.getNonString();
    nonJsonString_ = validationTestData.getNonJsonString();
    nonArray_ = validationTestData.getNonArray();
    nonMathObject_ = validationTestData.getNonMathObject();
    nonMathObjectArray_ = validationTestData.getNonMathObjectArray();
    tooSmallModulus_ = validationTestData.getTooSmallModulus();
    tooSmallOrder_ = validationTestData.getTooSmallOrder();
    tooLargeOrder_ = validationTestData.getTooLargeOrder();
    tooSmallGenerator_ = validationTestData.getTooSmallGenerator();
    tooLargeGenerator_ = validationTestData.getTooLargeGenerator();
    tooSmallElementValue_ = validationTestData.getTooSmallElementValue();
    tooLargeElementValue_ = validationTestData.getTooLargeElementValue();
    tooSmallModulusBitLength_ =
        validationTestData.getTooSmallModulusBitLength();
    tooLowCertaintyLevel_ = validationTestData.getTooLowCertaintyLevel();
    tooLargeNumMembersRequired_ =
        validationTestData.getTooLargeNumMembersRequired();
  });

  describe('create a mathematical service that should be able to ..', function() {
    it('throw an error when being created, using an invalid secure random service object',
       function() {
         expect(function() {
           Object.create(mathematical.newService({secureRandomService: null}));
         }).toThrow();

         expect(function() {
           Object.create(
               mathematical.newService({secureRandomService: nonObject_}));
         }).toThrow();

         expect(function() {
           Object.create(
               mathematical.newService({secureRandomService: emptyObject_}));
         }).toThrow();
       });

    it('throw an error when creating a new ZpSubgroup object, using an invalid modulus p or JSON representation',
       function() {
         expect(function() {
           mathService_.newZpSubgroup(undefined, q_, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(null, q_, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(nonMathObject_, q_, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(tooSmallModulus_, q_, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(nonJsonString_);
         }).toThrow();
       });

    it('throw an error when creating a new ZpSubgroup object, using an invalid order q',
       function() {
         expect(function() {
           mathService_.newZpSubgroup(p_, undefined, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, null, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, nonMathObject_, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, tooSmallOrder_, g_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, tooLargeOrder_, g_);
         }).toThrow();
       });

    it('throw an error when creating a new ZpSubgroup object, using an invalid generator',
       function() {
         expect(function() {
           mathService_.newZpSubgroup(p_, q_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, q_, undefined);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, q_, null);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, q_, nonMathObject_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, q_, tooSmallGenerator_);
         }).toThrow();

         expect(function() {
           mathService_.newZpSubgroup(p_, q_, tooLargeGenerator_);
         }).toThrow();
       });

    it('throw an error when creating a new ZpGroupElement object, using an invalid modulus p or JSON representation',
       function() {
         expect(function() {
           mathService_.newZpGroupElement(undefined, q_, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(null, q_, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(nonMathObject_, q_, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(tooSmallModulus_, q_, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(nonJsonString_);
         }).toThrow();
       });

    it('throw an error when creating a new ZpGroupElement object, using an invalid order q',
       function() {
         expect(function() {
           mathService_.newZpGroupElement(p_, undefined, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, null, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, nonMathObject_, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, tooSmallOrder_, elementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, tooLargeOrder_, elementValue_);
         }).toThrow();
       });

    it('throw an error when creating a new ZpGroupElement object, using an invalid element value',
       function() {
         expect(function() {
           mathService_.newZpGroupElement(p_, q_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, q_, undefined);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, q_, null);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, q_, nonMathObject_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, q_, tooSmallElementValue_);
         }).toThrow();

         expect(function() {
           mathService_.newZpGroupElement(p_, q_, tooLargeElementValue_);
         }).toThrow();
       });

    it('throw an error when creating a new Exponent object, using an invalid order q or JSON representation',
       function() {
         expect(function() {
           mathService_.newExponent(undefined, exponentValue_);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(null, exponentValue_);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(nonMathObject_, exponentValue_);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(tooSmallOrder_, exponentValue_);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(nonJsonString_);
         }).toThrow();
       });

    it('throw an error when creating a new Exponent object, using an invalid exponent value',
       function() {
         expect(function() {
           mathService_.newExponent(q_);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(q_, undefined);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(q_, null);
         }).toThrow();

         expect(function() {
           mathService_.newExponent(q_, nonMathObject_);
         }).toThrow();
       });

    it('throw an error when creating a new quadratic residue Zp subgroup, using invalid input data',
       function() {
         expect(function() {
           mathService_.newQuadraticResidueGroup(p_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(undefined, q_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(null, q_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(nonMathObject_, q_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(tooSmallModulus_, q_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(p_, undefined);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(p_, null);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(p_, nonMathObject_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(p_, tooSmallOrder_);
         }).toThrow();

         expect(function() {
           mathService_.newQuadraticResidueGroup(p_, tooLargeOrder_);
         }).toThrow();
       });

    describe('create a new ZpSubgroup object that should ..', function() {
      it('throw an error when checking for group membership, using invalid input data',
         function() {
           expect(function() {
             group_.isGroupMember();
           }).toThrow();

           expect(function() {
             group_.isGroupMember(undefined);
           }).toThrow();

           expect(function() {
             group_.isGroupMember(null);
           }).toThrow();

           expect(function() {
             group_.isGroupMember(nonMathObject_);
           }).toThrow();
         });

      it('throw an error when checking for equality with another ZpSubgroup object, using invalid input data',
         function() {
           expect(function() {
             group_.equals();
           }).toThrow();

           expect(function() {
             group_.equals(undefined);
           }).toThrow();

           expect(function() {
             group_.equals(null);
           }).toThrow();

           expect(function() {
             group_.equals(nonMathObject_);
           }).toThrow();
         });
    });

    describe('create a new ZpGroupElement object that should ..', function() {
      it('throw an error when multiplying itself with another ZpGroupElement object, using invalid input data',
         function() {
           expect(function() {
             element_.multiply();
           }).toThrow();

           expect(function() {
             element_.multiply(undefined);
           }).toThrow();

           expect(function() {
             element_.multiply(null);
           }).toThrow();

           expect(function() {
             element_.multiply(nonMathObject_);
           }).toThrow();

           expect(function() {
             element_.multiply(elementFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when exponentiating itself with an Exponent object, using invalid input data',
         function() {
           expect(function() {
             element_.exponentiate();
           }).toThrow();

           expect(function() {
             element_.exponentiate(undefined);
           }).toThrow();

           expect(function() {
             element_.exponentiate(null);
           }).toThrow();

           expect(function() {
             element_.exponentiate(nonMathObject_);
           }).toThrow();

           expect(function() {
             element_.exponentiate(exponentFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when comparing itself to another ZpGroupElement object, using invalid input data',
         function() {
           expect(function() {
             element_.equals();
           }).toThrow();

           expect(function() {
             element_.equals(undefined);
           }).toThrow();

           expect(function() {
             element_.equals(null);
           }).toThrow();

           expect(function() {
             element_.equals(nonMathObject_);
           }).toThrow();
         });
    });

    describe('create a new Exponent object that should ..', function() {
      it('throw an error when adding itself to another Exponent object, using invalid input data',
         function() {
           expect(function() {
             exponent_.add();
           }).toThrow();

           expect(function() {
             exponent_.add(undefined);
           }).toThrow();

           expect(function() {
             exponent_.add(null);
           }).toThrow();

           expect(function() {
             exponent_.add(nonMathObject_);
           }).toThrow();

           expect(function() {
             exponent_.add(exponentFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when subtracting another Exponent object from itself, using invalid input data',
         function() {
           expect(function() {
             exponent_.subtract();
           }).toThrow();

           expect(function() {
             exponent_.subtract(undefined);
           }).toThrow();

           expect(function() {
             exponent_.subtract(null);
           }).toThrow();

           expect(function() {
             exponent_.subtract(nonMathObject_);
           }).toThrow();

           expect(function() {
             exponent_.add(exponentFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when multiplying itself with another Exponent object, using invalid input data',
         function() {
           expect(function() {
             exponent_.multiply();
           }).toThrow();

           expect(function() {
             exponent_.multiply(undefined);
           }).toThrow();

           expect(function() {
             exponent_.multiply(null);
           }).toThrow();

           expect(function() {
             exponent_.multiply(nonMathObject_);
           }).toThrow();

           expect(function() {
             exponent_.add(exponentFromAnotherGroup_);
           }).toThrow();
         });

      it('throw an error when comparing itself to another Exponent object, using invalid input data',
         function() {
           expect(function() {
             exponent_.equals();
           }).toThrow();

           expect(function() {
             exponent_.equals(undefined);
           }).toThrow();

           expect(function() {
             exponent_.equals(null);
           }).toThrow();

           expect(function() {
             exponent_.equals(nonMathObject_);
           }).toThrow();
         });
    });

    describe(
        'create a new MathematicalArrayCompressor object that should ..',
        function() {
          it('throw an error when compressing an array of Zp group elements, using invalid input data',
             function() {
               expect(function() {
                 mathArrayCompressor_.compressZpGroupElements();
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressZpGroupElements(undefined);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressZpGroupElements(null);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressZpGroupElements(nonArray_);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressZpGroupElements(
                     nonMathObjectArray_);
               }).toThrow();
             });

          it('throw an error when compressing an array of exponents, using invalid input data',
             function() {
               expect(function() {
                 mathArrayCompressor_.compressExponents();
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressExponents(undefined);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressExponents(null);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressExponents(nonArray_);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressExponents(nonMathObjectArray_);
               }).toThrow();
             });

          it('throw an error when compressing the trailing elements of an array of Zp group elements, using invalid input data',
             function() {
               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     elements_);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     undefined, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(null, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     nonArray_, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     nonMathObjectArray_, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     elements_, undefined);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     elements_, null);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     elements_, nonNumber_);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingZpGroupElements(
                     elements_, nonPositiveNumber_);
               }).toThrow();
             });

          it('throw an error when compressing the trailing exponents of an array of exponents, using invalid input data',
             function() {
               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(exponents_);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(undefined, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(null, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(nonArray_, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(
                     nonMathObjectArray_, 2);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(
                     exponents_, undefined);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(
                     exponents_, null);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(
                     exponents_, nonNumber_);
               }).toThrow();

               expect(function() {
                 mathArrayCompressor_.compressTrailingExponents(
                     exponents_, nonPositiveNumber_);
               }).toThrow();
             });
        });

    describe(
        'create a new MathematicalRandomGenerator object that should ..',
        function() {
          it('throw an error when generating a random exponent, using invalid input data',
             function() {
               expect(function() {
                 mathRandomGenerator_.nextExponent();
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextExponent(undefined);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextExponent(null);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextExponent(nonMathObject_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextExponent(
                     group_, {secureRandomGenerator: nonObject_});
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextExponent(
                     group_, {secureRandomGenerator: emptyObject_});
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextExponent(
                     group_, {secureRandomGenerator: nonMathObject_});
               }).toThrow();
             });

          it('throw an error when generating a random Zp group element, using invalid input data',
             function() {
               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement();
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement(undefined);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement(null);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement(nonMathObject_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement(
                     group_, {secureRandomGenerator: nonObject_});
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement(
                     group_, {secureRandomGenerator: emptyObject_});
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextZpGroupElement(
                     group_, {secureRandomGenerator: nonMathObject_});
               }).toThrow();
             });

          it('throw an error when generating a random quadratic residue group, using invalid input data',
             function() {
               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(16);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     undefined, minCertaintyLevel_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     null, minCertaintyLevel_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     nonNumber_, minCertaintyLevel_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     nonPositiveNumber_, minCertaintyLevel_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     tooSmallModulusBitLength_, minCertaintyLevel_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(16, undefined);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(16, null);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(16, nonNumber_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     16, nonPositiveNumber_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     16, tooLowCertaintyLevel_);
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     16, minCertaintyLevel_,
                     {secureRandomGenerator: nonObject_});
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     16, minCertaintyLevel_,
                     {secureRandomGenerator: emptyObject_});
               }).toThrow();

               expect(function() {
                 mathRandomGenerator_.nextQuadraticResidueGroup(
                     16, minCertaintyLevel_,
                     {secureRandomGenerator: nonMathObject_});
               }).toThrow();
             });
        });

    describe('create a new MathematicalGroupHandler object that should ..', function() {
      it('throw an error when exponentiating an array of ZpGroupElement objects, using invalid input data',
         function() {
           expect(function() {
             mathGroupHandler_.exponentiateElements(group_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(group_, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 undefined, elements_, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(null, elements_, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 nonMathObject_, elements_, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, undefined, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(group_, null, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, nonArray_, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, nonMathObjectArray_, exponent_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, elements_, undefined);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, elements_, nonMathObject_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, elements_, exponentFromAnotherGroup_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, elements_, exponent_, null);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.exponentiateElements(
                 group_, elements_, exponent_, nonBoolean_);
           }).toThrow();
         });

      it('throw an error when dividing one array of ZpGroupElement objects with another, using invalid input data',
         function() {
           expect(function() {
             mathGroupHandler_.divideElements(elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(undefined, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(null, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(nonArray_, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(nonMathObjectArray_, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(elements_, undefined);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(elements_, null);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(elements_, nonArray_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(elements_, nonMathObjectArray_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.divideElements(elements_, [element_]);
           }).toThrow();
         });

      it('throw an error when checking for group membership, using invalid input data',
         function() {
           expect(function() {
             mathGroupHandler_.checkGroupMembership(undefined, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.checkGroupMembership(null, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.checkGroupMembership(nonMathObject_, elements_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.checkGroupMembership(group_, undefined);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.checkGroupMembership(group_, null);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.checkGroupMembership(group_, nonArray_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.checkGroupMembership(
                 group_, nonMathObjectArray_);
           }).toThrow();
         });

      it('throw an error when extracting Zp group elements from an array of BigIntegers, using invalid input data',
         function() {
           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, multiGroupElementValues_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 undefined, multiGroupElementValues_, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 null, multiGroupElementValues_, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 nonMathObject_, multiGroupElementValues_, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(group_, undefined, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(group_, null, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(group_, nonArray_, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, nonMathObjectArray_, 2);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, multiGroupElementValues_, undefined);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, multiGroupElementValues_, null);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, multiGroupElementValues_, nonNumber_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, multiGroupElementValues_, nonPositiveNumber_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.extractGroupMembers(
                 group_, multiGroupElementValues_, tooLargeNumMembersRequired_);
           }).toThrow();
         });

      it('throw an error when finding the smallest generator for a given Zp subgroup, using invalid input data',
         function() {
           expect(function() {
             mathGroupHandler_.findMinGenerator(p_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(undefined, q_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(null, q_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(nonMathObject_, q_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(tooSmallModulus_, q_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(p_, undefined);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(p_, null);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(p_, nonMathObject_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(p_, tooSmallOrder_);
           }).toThrow();

           expect(function() {
             mathGroupHandler_.findMinGenerator(p_, tooLargeOrder_);
           }).toThrow();
         });
    });
  });
});
