def add_temporal_relation(g):
    """
    Adds a temporal argmax relation in the graph, that is only the latest/earliest entity is returned as the answer.

    :param g: a graph with a non-empty edgeSet
    :return: a list of suggested graphs
    >>> add_temporal_relation({'edgeSet': [{'right':[2]}, {'right':[8]}], 'entities': [], 'tokens':['who', 'president']}) == \
     [{'edgeSet': [{'right':[2]}, {'right':[8]}, {'type':'time', 'argmax':'time'}], 'entities': [], 'tokens':['who', 'president']}]
    True
    >>> add_temporal_relation({'edgeSet': [{'right':[2]}, {'right':[8], 'argmin':'time'}], 'entities': []})
    []
    >>> add_temporal_relation({'edgeSet': [{'right':[2]}, {'type':'time'}], 'entities': []})
    []
    >>> add_temporal_relation({'edgeSet': [{'kbID': 'P161v','type': 'direct'}],'tokens': ['where','was','<e>','assassinated','?']})
    []
    """
    if len(g.get('edgeSet', [])) == 0 or graph.graph_has_temporal(g):
        return []
    new_graphs = []
    consider_types = set()
    if any(t in argmax_time_markers for t in g.get('tokens', [])):
        consider_types.add('argmax')
    if any(t in argmin_time_markers for t in g.get('tokens', [])):
        consider_types.add('argmin')
    for t in consider_types.intersection(ARG_TYPES):
        new_g = graph.copy_graph(g)
        new_edge = {'type': 'time', t: 'time', 'kbID': "P585v"}
        new_g['edgeSet'].append(new_edge)
        new_graphs.append(new_g)
    return new_graphs
def last_relation_numeric(g):
    """
    Adds a numeric restriction to the last relation in the graph.

    :param g: a graph with a non-empty edgeSet
    :return: a list of suggested graphs
    >>> last_relation_numeric({'edgeSet': [{'right':[2], 'type':'direct'}, {'right':[8], 'type':'direct'}], 'entities': [{'linkings':[("Q37876", "Natalie Portman")], 'tokens':["Portman"], 'type':'PERSON'}, {'linkings': [(None, ['2012'])], 'type': 'CD', 'tokens': ['2012']}]}) == \
    [{'edgeSet': [{'right':[2], 'type':'direct'}, {'right':[8], 'type':'direct', 'num': ['2012']}], 'entities': [{'linkings':[("Q37876", "Natalie Portman")], 'tokens':["Portman"], 'type':'PERSON'}]}]
    True
    >>> last_relation_numeric({'edgeSet': [{'right':[2]}, {'right':[8], 'argmin':'time'}], 'entities': [{'linkings': [(None, ['2012'])], 'type': 'CD', 'tokens': ['2012']}]})
    []
    >>> last_relation_numeric({'edgeSet': [{'right':[2]}, {'right':[8], 'num':'2009'}], 'entities': [{'linkings': [(None, ['2012'])], 'type': 'CD', 'tokens': ['2012']}]})
    []
    >>> last_relation_numeric({'edgeSet': [{'right':[2]}], 'entities': []})
    []
    >>> last_relation_numeric({'edgeSet': [{'right':[2]}], 'entities': [{'linkings':[("Q37876", "Natalie Portman")], 'tokens':["Portman"], 'type':'PERSON'}]})
    []
    """
    if len(g.get('edgeSet', [])) == 0 or graph.graph_has_temporal(g):
        return []
    if len(g.get('entities', [])) == 0 or not any(
            e.get("type") == 'CD' for e in g['entities'] if len(e) > 1):
        return []
    entities = copy.copy(g.get('entities', []))
    cd_entities = [e.get("tokens") for e in entities if e.get("type") == 'CD']
    if len(cd_entities) == 0:
        return []
    cd_entity = cd_entities[0]
    entities = [e for e in entities if e.get("tokens") != cd_entity]
    new_g = graph.copy_graph(g)
    new_g['entities'] = entities
    edge_to_modify = graph.get_graph_last_edge(
        new_g, filter_out_types={'iclass', 'v-structure', 'class'})
    if len(edge_to_modify) > 0:
        edge_to_modify['num'] = cd_entity
        return [new_g]
    return []
def last_relation_hop(g):
    """
    Takes a graph with an existing relation and an intermediate variable by performing a hop-up for the second entity.

    :param g: a graph with an non-empty edgeSet
    :return: a list of suggested graphs
    >>> last_relation_hop({'edgeSet': [], 'entities': [[4, 5, 6]]})
    []
    >>> last_relation_hop({'edgeSet': [{'right':[4,5,6]}], 'entities': []}) ==\
     [{'edgeSet': [{'right':[4,5,6], 'hopUp': None}], 'entities': []}, {'edgeSet': [{'right':[4,5,6], 'hopDown': None}], 'entities': []}]
    True
    >>> last_relation_hop({'edgeSet': [{'right':[4,5,6], 'hopUp': None}], 'entities': []})
    []
    >>> last_relation_hop({'edgeSet': [{'right':[4,5,6], 'kbID': "P31v", "type": "direct"}], 'entities': []})
    []
    >>> last_relation_hop({'edgeSet': [{'right':["Bahama"], "rightkbID":"Q6754", 'type':'direct'}], 'entities': []}) ==\
     [{'edgeSet': [{'right':["Bahama"], "rightkbID":"Q6754", 'hopUp': None, 'type':'direct'}], 'entities': []}, {'edgeSet': [{'right':["Bahama"], "rightkbID":"Q6754", 'hopDown': None, 'type':'direct'}], 'entities': []}]
    True
    >>> last_relation_hop({'edgeSet': [{'right':[4,5,6], 'argmax':'time'}], 'entities': []})
    []
    >>> last_relation_hop({'edgeSet': [{'right':[4,5,6], 'num':['2012']}], 'entities': []})
    []
    """
    if len(g.get('edgeSet', [])) == 0 or any(
            hop in g['edgeSet'][-1]
            for hop in {'hopUp', 'hopDown'}) or graph.graph_has_temporal(g):
        return []
    new_graphs = []
    for hop in HOP_TYPES:
        new_g = graph.copy_graph(g)
        edge_to_modify = graph.get_graph_last_edge(
            new_g, filter_out_types={'iclass', 'class', 'v-structure', 'time'})
        if len(edge_to_modify) > 0 and 'kbID' not in edge_to_modify:
            edge_to_modify[hop] = None
            new_graphs.append(new_g)
    return new_graphs
def graph_to_query(g,
                   ask=False,
                   return_var_values=False,
                   limit=GLOBAL_RESULT_LIMIT):
    """
    Convert graph to a sparql query.

    :param ask: if the a simple existence of the graph should be checked instead of returning variable values.
    :param g: a graph as a dictionary with non-empty edgeSet
    :param return_var_values: if True the denotations for free variables will be returned
    :param limit: limit on the result list size
    :return: a sparql query
    >>> g = {'edgeSet': [{'kbID': 'P35v', 'type': 'reverse', 'rightkbID': 'Q155', 'right': [5], 'argmax':'time'}], 'entities': []}
    >>> len(query_wikidata(graph_to_query(g, return_var_values = True)))
    1
    >>> g = {'edgeSet': [{'kbID': 'P35v', 'type': 'reverse', 'rightkbID': 'Q155', 'right': [5]}], 'entities': []}
    >>> len(query_wikidata(graph_to_query(g, return_var_values = True)))
    6
    >>> g = {'edgeSet': [{'right': ["Missouri"]}], 'entities': [[4]], 'tokens': ['who', 'are', 'the', 'current', 'senator', 'from', 'missouri', '?']}
    >>> len(query_wikidata(graph_to_query(g, return_var_values = False)))
    152
    """
    variables = []
    order_by = []
    query = "{"
    if graph.graph_has_temporal(g):
        if any(edge.get('type') == 'time' for edge in g.get('edgeSet', [])):
            query += sparql_temporal_values_v
        else:
            query += sparql_temporal_values_q
    edge_set = [
        e for e in g.get('edgeSet', [])
        if e.get('type') != "iclass" or "canonical_right" not in e
    ]  # implicit class edges are ignored as long as they are resolved already
    for i, edge in enumerate(edge_set):
        local_variables = []
        if 'type' in edge:
            sparql_relation_inst = sparql_relation[edge['type']]
        else:
            sparql_relation_inst = sparql_relation_complex

        if 'kbID' in edge and len(edge['kbID']) > 1:
            if edge.get('type') == 'v-structure':
                sparql_relation_inst = sparql_relation_inst.replace(
                    "VALUES ?rv { e:P161v e:P453q e:P175q}", "")
                sparql_relation_inst = sparql_relation_inst.replace(
                    "?rv", "e:" + edge['kbID'])
            elif edge.get('type') == 'iclass':
                sparql_relation_inst = sparql_relation_inst.replace(
                    "VALUES (?pt ?rt) { (e:P106s e:P106v) (e:P31s e:P31v) }",
                    "")
                sparql_relation_inst = sparql_relation_inst.replace(
                    "?rt", "e:" + edge['kbID'])
                sparql_relation_inst = sparql_relation_inst.replace(
                    "?pt", "e:" + edge['kbID'][:-1] + "s")
            else:
                if wdaccess_p.get(
                        'expand.transitive.relations',
                        False) and edge['kbID'] == "P131v" and edge.get(
                            'type') == "reverse":
                    sparql_relation_inst = sparql_relation_inst.replace(
                        "?p ?m . ?m ?rr", "(e:P131s/e:P131v)+")
                else:
                    sparql_relation_inst = re.sub(r"\?r[dr]",
                                                  "e:" + edge['kbID'],
                                                  sparql_relation_inst)

        elif edge.get('type') not in {'time', 'iclass'}:
            sparql_relation_inst = sparql_relation_inst.replace(
                "?r", "?r" + str(i))
            local_variables.extend(
                ["?r{}{}".format(i, t[0])
                 for t in ['direct', 'reverse']] if 'type' not in
                edge else ["?r{}{}".format(i, edge['type'][0])])
            # for v in local_variables:
            #     sparql_relation_inst += sparql_relation_filter.replace("%relationvar%", v)
        if edge.get('type') == "iclass":
            if "canonical_right" not in edge:
                local_variables.append("?topic")
            if "kbID" not in edge:
                local_variables.append("?rt")

        if 'hopUp' in edge or 'hopDown' in edge:
            hop = 'hopDown' if 'hopDown' in edge else 'hopUp'
            sparql_hop = sparql_entity_specify if 'hopDown' in edge else sparql_entity_abstract
            sparql_relation_inst = sparql_relation_inst.replace("?e2", "?e3")
            sparql_relation_inst = sparql_relation_inst.replace(
                "%restriction%", sparql_hop + " %restriction%")
            if edge[hop]:
                sparql_relation_inst = sparql_relation_inst.replace(
                    "?hopv", "e:" + edge[hop])
                sparql_relation_inst = sparql_relation_inst.replace(
                    "?hops", "e:" + edge[hop][:-1] + "s")
            else:
                sparql_relation_inst = sparql_hopup_values + sparql_relation_inst
                local_variables.append("?hop{}v".format(i))

        if any(arg_type in edge for arg_type in ['argmax', 'argmin']):
            sparql_relation_inst = sparql_relation_inst.replace(
                "%restriction%", sparql_restriction_time_argmax)
            sparql_relation_inst = sparql_relation_inst.replace(
                "FILTER (YEAR(?n) = ?yearvalue)", "")
            # sparql_relation_inst = sparql_relation_inst.replace("?n", "?n" + str(i))
            # sparql_relation_inst = sparql_relation_inst.replace("?a", "?a" + str(i))
            if return_var_values:
                order_by.append("{}({})".format(
                    "DESC" if 'argmax' in edge else "ASC", "?n"))
                limit = 1
        elif 'num' in edge:
            sparql_relation_inst = sparql_relation_inst.replace(
                "%restriction%", sparql_restriction_time_argmax)
            sparql_relation_inst = sparql_relation_inst.replace(
                "?yearvalue", " ".join(edge['num']))
        else:
            sparql_relation_inst = sparql_relation_inst.replace(
                "%restriction%", "")

        sparql_relation_inst = sparql_relation_inst.replace(
            "?p", "?p" + str(i))
        sparql_relation_inst = sparql_relation_inst.replace(
            "?m", "?m" + str(i))
        sparql_relation_inst = sparql_relation_inst.replace(
            "?j", "?j" + str(i))
        sparql_relation_inst = sparql_relation_inst.replace(
            "?e3", "?e3" + str(i))
        sparql_relation_inst = sparql_relation_inst.replace(
            "?hop", "?hop" + str(i))

        if 'leftkbID' in edge and ask:
            sparql_relation_inst = sparql_relation_inst.replace(
                "?e1", "e:" + edge['leftkbID'])
        if 'rightkbID' in edge:
            sparql_relation_inst = sparql_relation_inst.replace(
                "?e2", "e:" + edge['rightkbID'])
        elif 'right' in edge:
            sparql_relation_inst = sparql_relation_inst.replace(
                "?e2", "?e2" + str(i))
            right_label = " ".join(edge['right'])
            sparql_entity_label_inst = sparql_entity_label.replace(
                "VALUES ?labelright { %entitylabels }", "")
            sparql_entity_label_inst = sparql_entity_label_inst.replace(
                "%linkbyname%", "")
            sparql_entity_label_inst = sparql_entity_label_inst.replace(
                "?e2", "?e2" + str(i))
            sparql_entity_label_inst = sparql_entity_label_inst.replace(
                "?labelright", "\"{}\"@en".format(right_label))
            local_variables.append("?e2" + str(i))
            query += sparql_entity_label_inst
        sparql_relation_inst = sparql_relation_inst.replace(
            "_:m", "_:m" + str(i))
        sparql_relation_inst = sparql_relation_inst.replace(
            "_:s", "_:s" + str(i))

        if edge.get('type') != "class" or all(
                e.get('type') != 'v-structure' for e in g.get('edgeSet', [])):
            query += sparql_relation_inst
            variables.extend(local_variables)
        # if not local_variables or return_var_values:
        #     local_variables.append('?e1')
        # query = query.replace("%queryvariables%", " ".join(local_variables))

    query = sparql_prefix + (sparql_select if not ask else sparql_ask) + query
    # if return_var_values and all('leftkbID' not in edge for edge in g.get('edgeSet', [])) and not graph.graph_question_is_temporal(g):
    #     query += sparql_filter_main_entity

    if return_var_values and not ask:
        variables.append("?e1")
        # query += "BIND (xsd:integer(SUBSTR(STR(?e1), 33)) AS ?eid)"
        # order_by.append("?eid")
    query += "}"
    query = query.replace("%queryvariables%", " ".join(variables))
    if order_by and not ask:
        order_by_pattern = sparql_close_order.format(" ".join(order_by))
        query += order_by_pattern
    if not ask:
        query += sparql_close.format(limit)

    logger.debug("Querying with variables: {}".format(variables))
    return query