def test_typ():
    s1 = "kansas:s"
    EXPECT_TRUE(Typ.check_equivalence(Typ.create(T_ENTITY), typ(s1)))

    s2 = [
        "lambda",
        "$0",
        "e",
        [
            "and",
            ["state:t", "$0"],
            [
                "exists",
                "$1",
                ["and", ["state:t", "$1"], ["loc:t", "mississippi_river:r", "$1"], ["next_to:t", "$0", "$1"]],
            ],
        ],
    ]
    EXPECT_TRUE(Typ.check_equivalence(Typ.create((T_ENTITY, T_TRUTH)), typ(s2)))

    s3 = ["argmin", "$0", ["city:t", "$0"], ["population:i", "$0"]]
    EXPECT_TRUE(Typ.check_equivalence(Typ.create(T_ENTITY), typ(s3)))

    s4 = "equals:t"
    EXPECT_TRUE(Typ.check_equivalence(Typ.create((T_ENTITY, T_ENTITY, T_TRUTH)), typ(s4)))
def application_typ(sexp):

    # print sexp

    funct_typ = typ(application_function(sexp))
    # the arg signature might be any combination of the signatures of each arg
    arg_typ = Typ.product([typ(arg) for arg in application_args(sexp)])

    # assert functp(application_function(sexp))

    funct_arg_typ = Typ.arg_typ(funct_typ)
    funct_return_typ = Typ.return_typ(funct_typ)

    print sexp
    print "function expects", funct_arg_typ
    print "actual args", application_args(sexp)
    print "arg_typ", arg_typ

    # assert Typ.check_equivalence(arg_typ, funct_arg_typ)
    # TODO doesn't handle functions properly
    return funct_return_typ
def exp_typ_in(exp, se, bindings=None):
    global __functs__
    if bindings == None:
        bindings = {}
    if atomp(exp):
        if se == exp:
            return Typ.create(T_ENTITY)  # TODO truth literal?
        else:
            return None
    elif lambdap(exp):
        # t = Typ.create_from_string(lambda_arg_typ(exp))
        t = lambda_arg_typ(exp)
        if isinstance(t, str):
            t = Typ.create(t)
        if se == lambda_arg_name(exp):
            return t
        elif se == exp:
            bindings[lambda_arg_name(exp)] = t
            # return Typ.product([t, exp_typ_in(lambda_body(exp), lambda_body(exp),
            # bindings)])
            r = exp_typ_in(lambda_body(exp), lambda_body(exp), bindings)
            return Typ.product([t, r])
        else:
            bindings[lambda_arg_name(exp)] = t
            return exp_typ_in(lambda_body(exp), se, bindings)
    elif quantifierp(exp):
        # t = __functs__[application_function(exp)][0]
        t = Typ.create(T_ENTITY)  # TODO doesn't need to be manual
        if se == quantifier_var(exp):
            return t
        elif se == exp:
            return Typ.return_typ(__functs__[application_function(exp)])
        else:
            bindings[quantifier_var(exp)] = t
            for i in range(2, 2 + len(quantifier_args(exp))):
                r = exp_typ_in(exp[i], se, bindings)
                if not (r == None):
                    return r
            return None
    elif applicationp(exp):
        if se == exp:
            if application_function(exp) in bindings:
                return Typ.return_typ(bindings[application_function(exp)])
            else:
                return Typ.return_typ(__functs__[application_function(exp)])
        else:
            for i in range(1, 1 + len(application_args(exp))):
                r = exp_typ_in(exp[i], se, bindings)
                if not (r == None):
                    return r
            # print "couldn't get %s from %s" % (se, exp)
            # assert False
            return None
def type_in(var, sexp):
    if lambdap(sexp):
        if var == lambda_arg_name(sexp):
            return lambda_arg_typ(sexp)
        else:
            return type_in(var, lambda_body(sexp))
    elif applicationp(sexp):
        if quantifierp(sexp) and var == quantifier_var(sexp):
            return Typ.create(T_ENTITY)
        else:
            for arg in application_args(sexp):
                t = type_in(var, arg)
                if t:
                    return t
    return False
def read_lang_functs():
    global __functs__
    __functs__ = dict(BASE_FUNCTS)
    f = open("corpora/geo-lambda.lang")

    typs = defaultdict(list)
    for line in f.readlines()[1:-2]:
        parts = re.split(r"[\(\)\s]", line)
        name = parts[1]
        typ = parts[2:-2]
        # marginally faster than w/o intermediate list
        btyp = tuple([base_typ(t) for t in typ])
        typs[name].append(btyp)
        # __functs__[name] = Typ.create(btyp)
    f.close()

    for name in typs:
        __functs__[name] = Typ.create_ambiguous(typs[name])
def split(sexp, subexp):
    vrz = variables(sexp)
    vrz = [int(var[1:]) for var in vrz]

    # subtyp = typ(subexp)
    subvars = free_variables(subexp)

    # print subexp
    # print subtyp
    # print subvars

    # TODO orderings
    g = subexp
    for var in subvars:
        # t = type_in(var, sexp)
        t = exp_typ_in(sexp, var)
        g = [LAMBDA, var, t, g]

    # print "###"
    # print pretty_lambda(sexp)
    # print pretty_lambda(g)

    # gtyp = typ(g)
    gtyp = exp_typ_in(g, g)
    # print gtyp
    # print

    results = []

    # total substitution
    if g == subexp:
        if vrz:
            nvar = "$%d" % (max(vrz) + 1)
        else:
            nvar = "$0"
        # t = Typ.product([gtyp, typ(sexp)])
        t = gtyp
        replaced = replace_with_variable(sexp, g, nvar)
        results.append((g, [LAMBDA, nvar, t, replaced]))

        # if not Typ.simplep(gtyp):
        #  nexp = [nvar, '?VARIABLE']
        #  rep2 = replace_with_variable(sexp, g, nexp)
        #  print rep2
        #  results.append((g, [LAMBDA, nvar, t, rep2]))

    if subvars:

        # application
        if vrz:
            nvar = "$%d" % (max(vrz) + 1)
        else:
            nvar = "$0"
        napp = [nvar] + sorted(list(subvars))  # TODO only takes one var!
        # print "replacing %s with %s in %s" % (subexp, napp, sexp)
        replaced = replace_with_variable(sexp, subexp, napp)
        # t = Typ.product([gtyp, typ(sexp)])
        t = gtyp
        results.append((g, [LAMBDA, nvar, t, replaced]))
        # print g
        # print t.signatures
        # print [LAMBDA, nvar, t, replaced]
        # print "DID SUBVARS"

        # composition
        if len(subvars) > 1:
            for var in subvars:
                vz = sorted(list(subvars))
                vz.remove(var)
                # vz.insert(0, var)
                napp = [nvar] + vz
                replaced = replace_with_variable(sexp, subexp, napp)
                # g2typ = Typ.return_typ(gtyp)
                # t = Typ.product([g2typ, typ(sexp)])
                t = Typ.return_typ(gtyp)
                results.append((g, [LAMBDA, nvar, t, replaced]))
                # print vz

    # if subvars:
    #  nvar = '$%d' % (max(vrz) + 1)
    #  nvar2 = '$%d' % (max(vrz) + 2)
    #  nexp = [LAMBDA, nvar, '?', 'REPLACED_HERE']
    #  replaced = replace_with_variable(sexp, subexp, nexp)
    #  results.append((g, [LAMBDA, nvar2, '?', replaced]))

    # print g
    # gtyp = typ(g)
    # if not Typ.simplep(gtyp):
    #  pass

    for i in range(len(results)):
        results[i] = (
            normalize_variables(remove_vacuous_lambdas(results[i][0]), {}),
            normalize_variables(remove_vacuous_lambdas(results[i][1]), {}),
        )

    # print
    return results
def subexps_typed(sexp, t):
    return subexps(sexp, lambda x: Typ.check_equivalence(typ(x), t))
F_ARGMIN = "argmin"
F_COUNT = "count"
F_SUM = "sum"
F_LT = "<"
F_GT = ">"
F_AND = "and"
F_OR = "or"
F_NOT = "not"
F_EXISTS = "exists"

T_TRUTH = "t"
T_INT = "i"
T_ENTITY = "e"

BASE_FUNCTS = {
    F_ARGMAX: Typ.create((T_ENTITY, T_TRUTH, T_INT, T_ENTITY)),
    F_ARGMIN: Typ.create((T_ENTITY, T_TRUTH, T_INT, T_ENTITY)),
    F_COUNT: Typ.create((T_ENTITY, T_TRUTH, T_INT)),
    F_SUM: Typ.create((T_ENTITY, T_TRUTH, T_INT, T_INT)),
    F_LT: Typ.create((T_INT, T_INT, T_TRUTH)),
    F_GT: Typ.create((T_INT, T_INT, T_TRUTH)),
    F_AND: Typ.create_ambiguous([(T_TRUTH, T_TRUTH, T_TRUTH), (T_TRUTH, T_TRUTH, T_TRUTH, T_TRUTH)]),
    F_OR: Typ.create_ambiguous([(T_TRUTH, T_TRUTH, T_TRUTH), (T_TRUTH, T_TRUTH, T_TRUTH, T_TRUTH)]),
    F_NOT: Typ.create((T_TRUTH, T_TRUTH, T_TRUTH)),
    # TODO this actually a macro, not a function
    F_EXISTS: Typ.create((T_ENTITY, T_TRUTH, T_TRUTH)),
}

BASE_TYPES = [T_TRUTH, T_INT, T_ENTITY]

QUANTIFIERS = [F_ARGMIN, F_ARGMAX, F_COUNT, F_SUM, F_EXISTS]