def is_variable(x): return is_string(x)
def is_same_variable(v1, v2):
    return is_variable(v1) and is_variable(v2) and v1 == v2
def install_python_number_is_equal_to_zero():
    put("is_equal_to_zero", llist("python_number"),
        lambda x: x == 0)
    return "done"
install_python_number_is_equal_to_zero()

def is_equal_to_zero(x):
    return apply_generic("is_equal_to_zero", llist(x))

def install_polynomial_package():

    # internal functions

    # representation of poly
    def make_poly(variable, term_list):
        return pair(variable, term_list)
    def variable(p): return head(p)
    def term_list(p): return tail(p)

    # representation of terms and term lists
    def adjoin_term(term, term_list):
        return (term_list
                if is_equal_to_zero(coeff(term))
                else pair(term, term_list))
    the_empty_termlist = None
    def first_term(term_list):
        return head(term_list)
    def rest_terms(term_list):
        return tail(term_list)
    def is_empty_termlist(term_list):
        return is_none(term_list)
    def make_term(order, coeff):
        return llist(order, coeff)
    def order(term):
        return head(term)
    def coeff(term):
        return head(tail(term))

    def add_poly(p1, p2):
        return (make_poly(variable(p1),
                          add_terms(term_list(p1),
                                    term_list(p2)))
                if is_same_variable(variable(p1), variable(p2))
                else error("polys not in same var -- add_poly",
                           llist(p1, p2)))

    def add_terms(L1, L2):
        if is_empty_termlist(L1):
          return L2
        elif is_empty_termlist(L2):
          return L1
        else:
            t1 = first_term(L1)
            t2 = first_term(L2)
            return (adjoin_term(t1, add_terms(rest_terms(L1), L2))
                    if order(t1) > order(t2)
                    else adjoin_term(t2, add_terms(L1, rest_terms(L2)))
                    if order(t1) < order(t2)
                    else adjoin_term(make_term(order(t1),
                                               add(coeff(t1),
                                                   coeff(t2))),
                                     add_terms(rest_terms(L1),
                                               rest_terms(L2))))

    def mul_poly(p1, p2):
        return (make_poly(variable(p1),
                          mul_terms(term_list(p1),
                                    term_list(p2)))
                if is_same_variable(variable(p1), variable(p2))
                else error("polys not in same var -- mul_poly",
                           llist(p1, p2)))

    def mul_terms(L1, L2):
        return (the_empty_termlist
                if is_empty_termlist(L1)
                else add_terms(mul_term_by_all_terms(
                                      first_term(L1), L2),
                               mul_terms(rest_terms(L1), L2)))
    def mul_term_by_all_terms(t1, L):
        if is_empty_termlist(L):
            return the_empty_termlist
        else:
            t2 = first_term(L)
            return adjoin_term(
                       make_term(order(t1) + order(t2),
                                 mul(coeff(t1), coeff(t2))),
                       mul_term_by_all_terms(t1, rest_terms(L)))

    # interface to rest of the system
    def tag(p):
        return attach_tag("polynomial", p)
    put("add", llist("polynomial", "polynomial"),
        lambda p1, p2: tag(add_poly(p1, p2)))
    put("mul", llist("polynomial", "polynomial"),
        lambda p1, p2: tag(mul_poly(p1, p2)))
    put("make", "polynomial",
        lambda variable, terms:
            tag(make_poly(variable, terms)))
    return "done"
print(install_polynomial_package())
