# Path: linguistica/util.py
# def double_sorted(input_object, key=lambda x: x, reverse=False,
#                   subkey=lambda x: x, subreverse=False):
#     new_sorted_list = list()
#     sorted_list = sorted(input_object, key=key, reverse=reverse)
# 
#     for _, group in groupby(sorted_list, key=key):  # groupby from itertools
# 
#         # must use "list(group)", cannot use just "group"!
#         sublist = sorted(list(group), key=subkey, reverse=subreverse)
#         # see python 3.4 documentation:
#         # https://docs.python.org/3/library/itertools.html#itertools.groupby
#         # "The returned group is itself an iterator that shares the underlying
#         # iterable with groupby(). Because the source is shared, when the
#         # groupby() object is advanced, the previous group is no longer
#         # visible. So, if that data is needed later, it should be stored as a
#         # list"
# 
#         new_sorted_list.extend(sublist)
# 
#     return new_sorted_list


from collections import defaultdict
from scipy import (sparse, spatial)
from scipy.sparse import linalg
from linguistica.util import double_sorted
import numpy as np
import networkx as nx


def compute_words_distance(coordinates):
    # the scipy pdist function is to compute pairwise distances
    return spatial.distance.squareform(
        spatial.distance.pdist(coordinates, 'euclidean'))


def compute_closest_neighbors(word_distances, n_neighbors):
    sorted_neighbors = word_distances.argsort()
    # indices of sorted rows, low to high

    # truncate columns at n_neighbors + 1
    nearest_neighbors = sorted_neighbors[:, :n_neighbors + 1]
    return nearest_neighbors


def compute_graph(words_to_neighbors):
    graph = nx.Graph()
    for word in words_to_neighbors.keys():
        neighbors = words_to_neighbors[word]
        for neighbor in neighbors:
            graph.add_edge(word, neighbor)
    return graph


def run(unigram_counter=None, bigram_counter=None, trigram_counter=None,
        max_word_types=1000, n_neighbors=9, n_eigenvectors=11,
        min_context_count=3):


# Path: linguistica/util.py
# def double_sorted(input_object, key=lambda x: x, reverse=False,
#                   subkey=lambda x: x, subreverse=False):
#     new_sorted_list = list()
#     sorted_list = sorted(input_object, key=key, reverse=reverse)
# 
#     for _, group in groupby(sorted_list, key=key):  # groupby from itertools
# 
#         # must use "list(group)", cannot use just "group"!
#         sublist = sorted(list(group), key=subkey, reverse=subreverse)
#         # see python 3.4 documentation:
#         # https://docs.python.org/3/library/itertools.html#itertools.groupby
#         # "The returned group is itself an iterator that shares the underlying
#         # iterable with groupby(). Because the source is shared, when the
#         # groupby() object is advanced, the previous group is no longer
#         # visible. So, if that data is needed later, it should be stored as a
#         # list"
# 
#         new_sorted_list.extend(sublist)
# 
#     return new_sorted_list


# Path: linguistica/manifold.py
from collections import defaultdict
from scipy import (sparse, spatial)
from scipy.sparse import linalg
from linguistica.util import double_sorted
import numpy as np
import networkx as nx



def compute_words_distance(coordinates):
    # the scipy pdist function is to compute pairwise distances
    return spatial.distance.squareform(
        spatial.distance.pdist(coordinates, 'euclidean'))


def compute_closest_neighbors(word_distances, n_neighbors):
    sorted_neighbors = word_distances.argsort()
    # indices of sorted rows, low to high

    # truncate columns at n_neighbors + 1
    nearest_neighbors = sorted_neighbors[:, :n_neighbors + 1]
    return nearest_neighbors


def compute_graph(words_to_neighbors):
    graph = nx.Graph()
    for word in words_to_neighbors.keys():
        neighbors = words_to_neighbors[word]
        for neighbor in neighbors:
            graph.add_edge(word, neighbor)
    return graph


def run(unigram_counter=None, bigram_counter=None, trigram_counter=None,
        max_word_types=1000, n_neighbors=9, n_eigenvectors=11,
        min_context_count=3):


    word_freq_pairs = double_sorted(unigram_counter.items(),