text: 'use strict';
  
  /*********************************************************************************************
   *                                                                                           *
   * Plese read the following tutorial before implementing tasks:                              *
   * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array    *
   *                                                                                           *
   * NOTE : Please do not use loops! All tasks can be implmeneted using standard Array methods *
   *                                                                                           *
   *********************************************************************************************/
  
   
  /**
   * Returns an index of the specified element in array or -1 if element is not found
   * 
   * @param {array} arr
   * @param {any} value
   * @return {number}
   * 
   * @example
   *    ['Ace', 10, true], 10    => 1 
   *    ['Array', 'Number', 'string'], 'Date'    => -1 
   *    [0, 1, 2, 3, 4, 5], 5    => 5
   */
  function findElement(arr, value) {
     return arr.indexOf(value) ? arr.indexOf(value) : -1;
  }
  
  /**
   * Generates an array of odd numbers of the specified length
   * 
   * @param {number} len
   * @return {array}
   * 
   * @example
   *    1 => [ 1 ] 
   *    2 => [ 1, 3 ] 
   *    5 => [ 1, 3, 5, 7, 9 ]
   */
  function generateOdds(len) {
    let count = -1;
     let arr = new Array(len).fill(0).map(() => {
      count = count + 2;
      return count;
     });
     return arr;
  }
  
  
  /**
   * Returns the doubled array - elements of the specified array are repeated twice using original order
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    ['Ace', 10, true]  => ['Ace', 10, true,   'Ace', 10, true]  
   *    [0, 1, 2, 3, 4, 5] => [0, 1, 2, 3, 4, 5,   0, 1, 2, 3, 4, 5]
   *    [] => [] 
   */
  function doubleArray(arr) {
     return arr.concat(arr);
  }
  
  
  /**
   * Returns an array of positive numbers from the specified array in original order
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    [ 0, 1, 2, 3, 4, 5 ] => [ 1, 2, 3, 4, 5 ]
   *    [-1, 2, -5, -4, 0] => [ 2 ]
   *    [] => [] 
   */
  function getArrayOfPositives(arr) {
     return arr.filter(value =>{return value > 0})
  }
  
  /**
   * Returns the array with strings only in the specified array (in original order)
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    [ 0, 1, 'cat', 3, true, 'dog' ] => [ 'cat', 'dog' ]
   *    [ 1, 2, 3, 4, 5 ] => []
   *    [ 'cat, 'dog', 'raccon' ] => [ 'cat', 'dog', 'racoon' ]
   */
  function getArrayOfStrings(arr) {
     return arr.filter(value => {return typeof value == 'string'} )
  }
  
  /**
   * Removes falsy values from the specified array
   * Falsy values: false, null, 0, "", undefined, and NaN.
   * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean#Description)
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    [ 0, false, 'cat', NaN, true, '' ] => [ 'cat', true ]
   *    [ 1, 2, 3, 4, 5, 'false' ]         => [ 1, 2, 3, 4, 5, 'false' ]
   *    [ false, 0, NaN, '', undefined ]   => [ ]
   */
  function removeFalsyValues(arr) {
     return arr.filter(value => {return value} )
  }
  
  /**
   * Returns the array of useprcase strings from the specified array
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    [ 'permanent-internship', 'glutinous-shriek', 'multiplicative-elevation' ] => [ 'PERMANENT-INTERNSHIP', 'GLUTINOUS-SHRIEK', 'MULTIPLICATIVE-ELEVATION' ]
   *    [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ]  => [ 'A', 'B', 'C', 'D', 'E', 'F', 'G' ]
   */
  function getUpperCaseStrings(arr) {
     return arr.map(value => value.toUpperCase());
  }
  
  
  /**
   * Returns the array of string lengths from the specified string array.
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    [ '', 'a', 'bc', 'def', 'ghij' ]  => [ 0, 1, 2, 3, 4 ]
   *    [ 'angular', 'react', 'ember' ] => [ 7, 5, 5 ]
   */
  function getStringsLength(arr) {
     return arr.map(value => value.length);
  }
  
  /**
   * Inserts the item into specified array at specified index
   * 
   * @param {array} arr
   * @param {any} item
   * @param {number} index 
   * 
   * @example
   *    [ 1, 3, 4, 5 ], 2, 1  => [ 1, 2, 3, 4, 5 ]
   *    [ 1, 'b', 'c'], 0, 'x'  => [ 'x', 1, 'b', 'c' ]
   */
  function insertItem(arr, item, index) {
     return arr.splice(index,0,item);
  }
  
  /**
   * Returns the n first items of the specified array
   * 
   * @param {array} arr
   * @param {number} n 
   * 
   * @example
   *    [ 1, 3, 4, 5 ], 2  => [ 1, 2 ]
   *    [ 'a', 'b', 'c', 'd'], 3  => [ 'a', 'b', 'c' ]
   */
  function getHead(arr, n) {
     return arr.slice(0,n);
  }
  
  
  /**
   * Returns the n last items of the specified array
   * 
   * @param {array} arr
   * @param {number} n 
   * 
   * @example
   *    [ 1, 3, 4, 5 ], 2  => [ 4, 5 ]
   *    [ 'a', 'b', 'c', 'd'], 3  => [ 'b', 'c', 'd' ]
   */
  function getTail(arr, n) {
     return arr.slice(arr.length-n,arr.length);
  }
  
  
  /**
   * Returns CSV represebtation of two-dimentional numeric array.
   * https://en.wikipedia.org/wiki/Comma-separated_values
   * 
   * @param {array} arr
   * @return {string}
   * 
   * @example
   *    [
   *       [  0, 1, 2, 3, 4 ],
   *       [ 10,11,12,13,14 ],
   *       [ 20,21,22,23,24 ],
   *       [ 30,31,32,33,34 ]
   *    ] 
   *           => 
   *     '0,1,2,3,4\n'
   *    +'10,11,12,13,14\n'
   *    +'20,21,22,23,24\n'
   *    +'30,31,32,33,34'
   */
  function toCsvText(arr) {
     return arr.map(insideArray => insideArray.join(',')).join('\n');
  }
  
  /**
   * Transforms the numeric array into the according array of squares:
   *   f(x) = x * x
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *   [ 0, 1, 2, 3, 4, 5 ] => [ 0, 1, 4, 9, 16, 25 ]
   *   [ 10, 100, -1 ]      => [ 100, 10000, 1 ]
   */
  function toArrayOfSquares(arr) {
     return arr.map(value => value*value);
  }
  
  
  /**
   * Transforms the numeric array to the according moving sum array:
   *     f[n] = x[0] + x[1] + x[2] +...+ x[n] 
   *  or f[n] = f[n-1] + x[n]
   * 
   * @param {array} arr
   * @return {array}
   * 
   * Example :
   *   [ 1, 1, 1, 1, 1 ]        => [ 1, 2, 3, 4, 5 ]
   *   [ 10, -10, 10, -10, 10 ] => [ 10, 0, 10, 0, 10 ]
   *   [ 0, 0, 0, 0, 0]         => [ 0, 0, 0, 0, 0] 
   *   [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] => [ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55 ]
   */
  function getMovingSum(arr) {
     let sum = 0;
     return arr.map(value => {
      sum += value;
      return sum;
     })
  }
  
  /**
   * Returns every second item from the specified array:
   * 
   * @param {array} arr
   * @return {array}
   * 
   * Example :
   * [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] => [ 2, 4, 6, 8, 10 ]
   * [ 'a', 'b', 'c' , null ]  => [ "b", null ]
   * [ "a" ] => []
   */
  function getSecondItems(arr) {
     return arr.filter(value => {return (arr.indexOf(value)+1) % 2 ==0})
  }
  
  
  /**
   * Propagates every item in sequence its position times
   * Returns an array that consists of: one first item, two second items, tree third items etc. 
   * 
   * @param {array} arr 
   * @return {array}
   * 
   * @example :
   *  [] => []
   *  [ 1 ] => [ 1 ]
   *  [ 'a', 'b' ] => [ 'a', 'b','b' ]
   *  [ 'a', 'b', 'c', null ] => [ 'a', 'b','b', 'c','c','c',  null,null,null,null ]
   *  [ 1,2,3,4,5 ] => [ 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5 ]
   */
  function propagateItemsByPositionIndex(arr) {
      let result = []; 
      arr.map(function(v, i) { 
       result = result.concat(Array(i + 1).fill(v)); 
      }); 
      return result; 
  }
  
  
  /** 
   * Returns the 3 largest numbers from the specified array
   * 
   * @param {array} arr
   * @return {array}
   *
   * @example
   *   [] => []
   *   [ 1, 2 ] => [ 2, 1 ]
   *   [ 1, 2, 3 ] => [ 3, 2, 1 ]
   *   [ 1,2,3,4,5,6,7,8,9,10 ] => [ 10, 9, 8 ]
   *   [ 10, 10, 10, 10 ] => [ 10, 10, 10 ]
   */
  function get3TopItems(arr) {
     return arr.reverse().slice(0, 3);
  }
   
   
  /**  
   * Returns the number of positive numbers from specified array
   * 
   * @param {array} arr
   * @return {number}
   * 
   * @example
   *   [ ]          => 0
   *   [ -1, 0, 1 ] => 1
   *   [ 1, 2, 3]   => 3
   *   [ null, 1, 'elephant' ] => 1
   *   [ 1, '2' ] => 1
   */
  function getPositivesCount(arr) {
     let count = 0;
     arr.map(value => {
      if(typeof value === "number" && value > 0)
        count++;
     })
     return count;
  }
   
  /** 
   * Sorts digit names
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *   [] => []
   *   [ 'nine','one' ]                 => [ 'one', 'nine' ]
   *   [ 'one','two','three' ]          => [ 'one','two', 'three' ]
   *   [ 'nine','eight','nine','eight'] => [ 'eight','eight','nine','nine']
   *   [ 'one','one','one','zero' ]     => [ 'zero','one','one','one' ]
   */
  function sortDigitNamesByNumericOrder(arr) {
     return arr.sort((a, b) => {
          let items = {zero: 0,one: 1,two: 2,three: 3,four: 4,five: 5,six: 6,seven: 7,eight: 8,nine: 9};
  
          a = items[a];
          b = items[b];
  
          return a > b ? 1 : -1;
      });
  }
  
  /** 
   * Returns the sum of all items in the specified array of numbers
   * 
   * @param {array} arr
   * @return {number}
   * 
   * @example
   *   [] => 0
   *   [ 1, 2, 3 ]           => 6
   *   [ -1, 1, -1, 1 ]      => 0
   *   [ 1, 10, 100, 1000 ]  => 1111
   */
  function getItemsSum(arr) {
    return arr.reduce((prev,curr)=>{
      return prev + curr;
    },0)
  }
   
  /** 
   * Returns the number of all falsy value in the specified array
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *  [] => 0
   *  [ 1, '', 3 ] => 1
   *  [ -1, 'false', null, 0 ] => 2
   *  [ null, undefined, NaN, false, 0, '' ]  => 6
   */
  function getFalsyValuesCount(arr) {
    let count = 0;
    arr.map(value => {
      if(!value)
        count++;
    })
    return count;
  }
  
  /**
   * Returns a number of all occurences of the specified item in an array  
   * 
   * @param {array} arr
   * @param {any} item 
   * @return {number}
   * 
   * @example
   *    [ 0, 0, 1, 1, 1, 2 ], 1 => 3
   *    [ 1, 2, 3, 4, 5 ], 0 => 0
   *    [ 'a','b','c','c' ], 'c'=> 2
   *    [ null, undefined, null ], null => 2 
   *    [ true, 0, 1, 'true' ], true => 1
   */
  function findAllOccurences(arr, item) {
     let count = 0;
     arr.map(value =>{
      if(value === item)
        count++;
     })
     return count;
  }
  
  /**
   * Concatenates all elements from specified array into single string with ',' delimeter  
   * 
   * @param {array} arr 
   * @return {string}
   * 
   * @example
   *    [0, false, 'cat', NaN, true, '']  => '0,false,cat,NaN,true,'
   *    [1, 2, 3, 4, 5]                   => '1,2,3,4,5'
   *    ['rock', 'paper', 'scissors']     => 'rock,paper,scissors'
   */
  function toStringList(arr) {
     return arr.join(',')
  }
  
  
  /**
   * Sorts the specified array by country name first and city name (if countries are equal) in ascending order.
   * 
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *    [
   *      { country: 'Russia',  city: 'Moscow' },
   *      { country: 'Belarus', city: 'Minsk' },
   *      { country: 'Poland',  city: 'Warsaw' },
   *      { country: 'Russia',  city: 'Saint Petersburg' },
   *      { country: 'Poland',  city: 'Krakow' },
   *      { country: 'Belarus', city: 'Brest' }
   *    ]  
   *                      =>
   *    [
   *      { country: 'Belarus', city: 'Brest' },
   *      { country: 'Belarus', city: 'Minsk' },
   *      { country: 'Poland',  city: 'Krakow' },
   *      { country: 'Poland',  city: 'Warsaw' },
   *      { country: 'Russia',  city: 'Moscow' },
   *      { country: 'Russia',  city: 'Saint Petersburg' }
   */
  function sortCitiesArray(arr) {
     return arr.sort((a, b) => {
        if (a.country > b.country)
            return 1;
        else if (a.country < b.country)
            return -1;
        else if (a.city > b.city)
            return 1;
        else if (a.city < b.city)
            return -1;
        else
            return 0;
    });
  }
  
  /**
   * Creates an indentity matrix of the specified size
   * 
   * @param {number} n
   * @return {array}
   * 
   * @example
   *     1  => [[1]]
   *  
   *     2 => [[1,0],
   *           [0,1]]
   * 
   *          [[1,0,0,0,0],
   *           [0,1,0,0,0],
   *     5 =>  [0,0,1,0,0],
   *           [0,0,0,1,0],
   *           [0,0,0,0,1]]   
   */
  function getIdentityMatrix(n) {
     let arr = new Array(n);
     let innArr = new Array(n);
     innArr.fill(0);
     arr.fill(innArr);
     let res =arr.map((value,i) =>{
      return value.map((val,j)=>{
        return i === j ? 1 : 0;
      })
     });
     return res;
  }
  
  /**
   * Creates an array of integers from the specified start to end (inclusive)
   * 
   * @param {number} start
   * @param {number} end
   * @return {array}
   * 
   * @example
   *     1, 5  => [ 1, 2, 3, 4, 5 ]
   *    -2, 2  => [ -2, -1, 0, 1, 2 ]
   *     0, 100 => [ 0, 1, 2, ..., 100 ]
   *     3, 3   => [ 3 ]
   */
  function getIntervalArray(start, end) {
     let len = end - start +1;
     let count = start -1 ;;
     let arr = new Array(len).fill(0).map(() => {
      count++;
      return count;
     });
     return arr;
  }
  
  /**
   * Returns array containing only unique values from the specified array.
   *
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *   [ 1, 2, 3, 3, 2, 1 ] => [ 1, 2, 3 ]
   *   [ 'a', 'a', 'a', 'a' ]  => [ 'a' ]
   *   [ 1, 1, 2, 2, 3, 3, 4, 4] => [ 1, 2, 3, 4]
   */
  function distinct(arr) {
    let newArr = new Set();
     arr.map(value =>{
      newArr.add(value);
     })
     return [...newArr];
  }
  
  /**
   * Groups elements of the specified array by key.
   * Returns multimap of keys extracted from array elements via keySelector callback
   * and values extracted via valueSelector callback.
   * See: https://en.wikipedia.org/wiki/Multimap
   *
   * @param {array} array
   * @param {Function} keySelector
   * @param {Function} valueSelector
   * @return {Map}
   *
   * @example
   *   group([
   *      { country: 'Belarus', city: 'Brest' },
   *      { country: 'Russia', city: 'Omsk' },
   *      { country: 'Russia', city: 'Samara' },
   *      { country: 'Belarus', city: 'Grodno' },
   *      { country: 'Belarus', city: 'Minsk' },
   *      { country: 'Poland', city: 'Lodz' }
   *     ], 
   *     item => item.country, 
   *     item => item.city
   *   )
   *            => 
   *   Map {
   *    "Belarus" => ["Brest", "Grodno", "Minsk"],
   *    "Russia" => ["Omsk", "Samara"], 
   *    "Poland" => ["Lodz"]
   *   }
   */
  function group(arr, keySelector, valueSelector) {
      let newArr = new Map();
      arr.map(value =>{
        if(!newArr.has(keySelector(value)))
          newArr.set(keySelector(value),[valueSelector(value)]);
        else
          newArr.set(keySelector(value),[...newArr.get(keySelector(value)),valueSelector(value)]);
      })
      return newArr;
  }
  
  
  /**
   * Projects each element of the specified array to a sequence and flattens the resulting sequences into one array.
   *
   * @param {array} arr
   * @param {Function} childrenSelector, a transform function to apply to each element that returns an array of children
   * @return {array}
   * 
   * @example
   *   [[1, 2], [3, 4], [5, 6]], (x) => x     =>   [ 1, 2, 3, 4, 5, 6 ]
   *   ['one','two','three'], x=>x.split('')  =>   ['o','n','e','t','w','o','t','h','r','e','e']
   */
  function selectMany(arr, childrenSelector) {
    return arr.reduce((previous, current) => {
        let child = childrenSelector(current);
  
        return previous.concat(child);
    }, []); 
  }
  
  
  /**
   * Returns an element from the multidimentional array by the specified indexes.
   *
   * @param {array} arr
   * @param {array} indexes
   * @return {any} element from array
   * 
   * @example
   *   [[1, 2], [3, 4], [5, 6]], [0,0]  => 1        (arr[0][0]) 
   *   ['one','two','three'], [2]       => 'three'  (arr[2]) 
   *   [[[ 1, 2, 3]]], [ 0, 0, 1 ]      => 2        (arr[0][0][1])
   */
  function getElementByIndexes(arr, indexes) {
      let val;
      indexes.map(value=>{
        if(val)
          val = val[value]
        else
          val = arr[value]
      })
      return val;
  }
  
  
  /**
   * Swaps the head and tail of the specified array:
   * the head (first half) of array move to the end, the tail (last half) move to the start. 
   * The middle element (if exists) leave on the same position.
   * 
   *  
   * @param {array} arr
   * @return {array}
   * 
   * @example
   *   [ 1, 2, 3, 4, 5 ]   =>  [ 4, 5, 3, 1, 2 ]
   *    \----/   \----/         
   *     head     tail 
   *
   *   [ 1, 2 ]  => [ 2, 1 ] 
   *   [ 1, 2, 3, 4, 5, 6, 7, 8 ]   =>  [ 5, 6, 7, 8, 1, 2, 3, 4 ]   
   * 
   */
  function swapHeadAndTail(arr) {
    if(arr.length % 2 == 0)
      return [...arr.slice(arr.length/2,arr.length),...arr.slice(0,arr.length/2)];
    else
      return [...arr.slice(arr.length/2 +1,arr.length),arr[Math.floor(arr.length/2)],...arr.slice(0,arr.length/2)];
  }
  
  
  module.exports = {
      findElement: findElement,
      generateOdds: generateOdds,
      doubleArray: doubleArray,
      getArrayOfPositives: getArrayOfPositives,
      getArrayOfStrings: getArrayOfStrings,
      removeFalsyValues: removeFalsyValues,
      getUpperCaseStrings: getUpperCaseStrings,
      getStringsLength: getStringsLength,
      insertItem: insertItem,
      getHead: getHead,
      getTail: getTail,
      toCsvText: toCsvText,
      toStringList: toStringList,
      toArrayOfSquares: toArrayOfSquares,
      getMovingSum: getMovingSum,
      getSecondItems: getSecondItems,
      propagateItemsByPositionIndex: propagateItemsByPositionIndex,
      get3TopItems: get3TopItems,
      getPositivesCount: getPositivesCount,
      sortDigitNamesByNumericOrder: sortDigitNamesByNumericOrder,
      getItemsSum: getItemsSum,
      getFalsyValuesCount: getFalsyValuesCount,
      findAllOccurences: findAllOccurences,
      sortCitiesArray: sortCitiesArray,
      getIdentityMatrix: getIdentityMatrix,
      getIntervalArray: getIntervalArray,
      distinct: distinct,
      group: group,
      selectMany: selectMany,
      getElementByIndexes: getElementByIndexes,
      swapHeadAndTail: swapHeadAndTail
  };
  