text: import re
  from nltk.corpus import wordnet as wn
  from itertools import product
  import math
  from collections import Counter, MutableMapping
  from fuzzywuzzy import fuzz
  import Levenshtein
  from datetime import date, datetime
  import copy
  from nl4dv.utils import constants
  import json
  
  
  WORD = re.compile(r'\w+')
  
  
  # Includes Repetition
  def get_ngrams(input, n):
      input = input.split(' ')
      output = []
      for i in range(len(input)-n+1):
          output.append(input[i:i+n])
      return output
  
  
  # Removes Duplicate N-Grams
  def get_ngrams_without_duplicate(input, n):
      input = input.split(' ')
      output = {}
      for i in range(len(input)-n+1):
          g = ' '.join(input[i:i+n])
          output.setdefault(g, 0)
          output[g] += + 1
          return output
  
  
  # ToDo:- Explore alternatives to WordNet similarity
  def synonymity_score(word_x, word_y):
      # type: (str, str) -> int
      """
      Helper method to find similarity between two words
  
      """
      # print("################################################")
      # print("word_x:")
      # print(word_x)
      # print("word_y:")
      # print(word_y)
      sem_1 = wn.synsets(word_x)
      sem_2 = wn.synsets(word_y)
      # print(sem_1)
      # print(sem_2)
      max_score = 0
      for i, j in list(product(*[sem_1, sem_2])):
          # print("i:")
          # print(i)
          # print("j:")
          # print(j)
          score = i.wup_similarity(j)
          # print("score:")
          # print(score)
          max_score = score if (score is not None and max_score < score) else max_score
      # print("max_score:")
      # print(max_score)
      # print("################################################")
  
      return max_score * 100
  
  
  def set_default(obj):
      """
      Return a serialized version of set  which is a list.
  
      """
      if isinstance(obj, (datetime, date)):
          return obj.isoformat()
  
      if isinstance(obj, set):
          return list(obj)
  
      raise TypeError ("Type %s not serializable" % type(obj))
  
  
  def cond_print(string_to_print, debug=True):
      if debug:
          print(string_to_print)
  
  
  def normalize(vis_list, attr, tasks):
      max_score_by_attr = len(attr)
      max_score_by_tasks = len(tasks)
      max_score_by_vis = 1
      for vis in vis_list:
          vis["normalized_score"] = vis["score"] / (max_score_by_attr + max_score_by_tasks + max_score_by_vis)
      return vis_list
  
  
  def vectorize(text):
      words = WORD.findall(text)
      return Counter(words)
  
  
  def cosine_similarity(vec1, vec2):
      intersection = set(vec1.keys()) & set(vec2.keys())
      numerator = sum([vec1[x] * vec2[x] for x in intersection])
  
      sum1 = sum([vec1[x]**2 for x in vec1.keys()])
      sum2 = sum([vec2[x]**2 for x in vec2.keys()])
      denominator = math.sqrt(sum1) * math.sqrt(sum2)
  
      if not denominator:
          return 0.0
      else:
          score = float(numerator) * 100 / denominator
          return score
  
  
  def compute_similarity(str1, str2, type):
      if type == 'cosine_similarity':
          return cosine_similarity(vectorize(str1), vectorize(str2.lower()))
  
      elif type == 'token_similarity':
          return fuzzy_token_similarity(str1, str2.lower())
  
      elif type == 'partial_ratio':
          return fuzzy_partial_ratio(str1, str2.lower())
  
      elif type == 'ratio_similarity':
          return fuzzy_ratio_similarity(str1, str2.lower())
  
      elif type == 'levenshtein_distance':
          return levenshtein_distance(str1, str2.lower())
  
  
  def fuzzy_token_similarity(str1, str2):
      return fuzz.token_set_ratio(str1, str2)
  
  
  def fuzzy_ratio_similarity(str1, str2):
      return fuzz.ratio(str1, str2)
  
  
  def fuzzy_partial_ratio(str1, str2):
      return fuzz.partial_ratio(str1, str2)
  
  
  def levenshtein_distance(str1, str2):
      dist = Levenshtein.distance(str1, str2)
      longer_str = str1 if len(str1) > len(str2) else str2
      score = float(dist) * 100 / len(longer_str)
      return score
  
  
  def isfloat(datum):
      try:
          if datum == '' or str(datum).isspace():
              return False
          float(datum)
      except AttributeError:
          return False
      except ValueError:
          return False
      except OverflowError:
          return False
      return True
  
  
  def isint(datum):
      try:
          if datum == '' or str(datum).isspace():
              return False
          a = float(datum)
          b = int(a)
      except AttributeError:
          return False
      except ValueError:
          return False
      except OverflowError:
          return False
      return a == b
  
  
  def isdate(datum):
      try:
          if datum == '' or str(datum).isspace():
              return False, None
  
          for idx, regex_list in enumerate(constants.date_regexes):
              regex = re.compile(regex_list[1])
              match = regex.match(str(datum))
              if match is not None:
                  dateobj = dict()
                  dateobj["regex_id"] = idx
                  dateobj["regex_matches"] = list(match.groups())
                  return True, dateobj
  
      except Exception as e:
          pass
  
      return False, None
  
  # Trim the output LIST based on the debug parameter
  def delete_keys_from_list(array, keys):
      keys_set = set(keys)
      modified_array = copy.deepcopy(array)
      for index, value in enumerate(array):
          if isinstance(value, MutableMapping):
              modified_array[index] = delete_keys_from_dict(value, keys_set)
          else:
              modified_array[index] = value
      return modified_array
  
  
  # Trim the output DICTIONARY based on the debug parameter
  def delete_keys_from_dict(dictionary, keys):
      keys_set = set(keys)
      modified_dict = {}
      for key, value in dictionary.items():
          if key not in keys_set:
              if isinstance(value, MutableMapping):
                  modified_dict[key] = delete_keys_from_dict(value, keys_set)
              elif isinstance(value, list):
                  modified_dict[key] = delete_keys_from_list(value, keys_set)
              else:
                  modified_dict[key] = value  # or copy.deepcopy(value) if a copy is desired for non-dicts.
      return modified_dict
  
  
  # Use to determine if two lists have any common elements, if any.
  def common_member(a, b):
      a_set = set(a)
      b_set = set(b)
      if (a_set & b_set):
          return True
      else:
          return False
  
  
  def format_str_to_date(str, format):
      try:
          return datetime.strptime(str, format)
      except Exception as e:
          return None
  
  
  class CustomJSONEncoder(json.JSONEncoder):
      def default(self, obj):
          if isinstance(obj, set):
              return list(obj)
          if isinstance(obj, (date, datetime)):
              return obj.strftime("%Y/%m/%d")
          return json.JSONEncoder.default(self, obj)
  