{
  "id": "P066",
  "slug": "polynomials",
  "key": "P066-polynomials",
  "title": "Polynomials",
  "summary": "Two polynomials typed the usual way, with exact fraction coefficients: add, subtract, multiply, and divide with quotient and remainder (checked back), evaluate term by term and by Horner's rule with the multiplications counted, take the derivative, and find every rational root by the rational root theorem.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P066 - Polynomials\n\nTwo polynomials, P and Q, typed the usual way, like 3x^2 - 2x + 1/2, with\nexact fraction coefficients. You can add, subtract and multiply them, and\ndivide P by Q, getting a quotient and remainder that are checked back. P\ncan be evaluated at a number two ways: term by term, and by Horner's rule,\nwith the multiplications counted. It can also be differentiated, and its\nrational roots are all found.\n\n- `main.eml` - the menu, reading polynomials and numbers, and the messages\n- `poly.eml` - the arithmetic, division, both ways of evaluating, the\n  derivative, reading and writing polynomials, and the rational roots\n- `frac.eml` - exact fractions, from P007 by way of P038, P044, P056, P060\n  and P062\n\nHow each part works:\n\n- A polynomial is the list of its coefficients from the constant up, so\n  3x^2 - 2x + 1/2 is [1/2, -2, 3]. Adding and subtracting go coefficient by\n  coefficient, and multiplying adds each product a x^i times b x^j into\n  the place for x^(i+j).\n- Dividing is long division: take the leading term of what is left over\n  the leading term of Q, take that many Qs away, and repeat until what is\n  left has a lower degree than Q.\n- Term by term, as in the corpus case `polynomial-evaluator`, each term is\n  worked out on its own and then they are added, which shows what every\n  term gives. Horner's rule rewrites 3x^2 - 2x + 1/2 as (3x - 2)x + 1/2: it\n  starts from the top coefficient and then multiplies by x and adds the\n  next one, n multiplications for degree n where term by term needs about\n  n^2 / 2. Both give the same value.\n- Rational roots: scale P to whole coefficients and take out any factor x.\n  By the rational root theorem, a root a/b in lowest terms has a dividing\n  the lowest coefficient and b dividing the highest. Every such candidate\n  is tried, and each root found is divided out as many times as it goes, so\n  its multiplicity is known. Whatever degree is left over belongs to roots\n  that are not rational.\n\nWhat is checked: menu choices 1 to 9. Polynomials are written as terms in\nx with whole, decimal or fraction coefficients (a fraction may be in\nbrackets, as the program writes it), powers up to 20, and no two numbers\nside by side. A number x can be whole, decimal, a fraction, or negative.\nRoot search needs the scaled end coefficients to be at most a million. An\nempty answer cancels.\n\nSessions: `sessions/basic.in` starts from P = x^3 - 6x^2 + 11x - 6, which\nis (x-1)(x-2)(x-3), and Q = x - 1.\n- P + Q, P - Q and P * Q.\n- P / Q = x^2 - 5x + 6 with remainder 0.\n- P at 4: 6, from 9 multiplications term by term and 3 by Horner.\n- P' and the roots 1, 2, 3.\n- 2x^4 - 3x^3 - 2x^2 + 3x: roots -1, 0, 1 and 3/2. Divided by x^2 + 1, and\n  P(1/2) = 3/4.\n- (x - 1)^4 written out: 1 four times.\n- x^2 - 2: no rational root.\n- x^3 - 2x: 0, with two roots that are not rational.\n- P and Q swapped.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- the inputs \"3 4\", \"x^\", 2y, x^21, 1/0 and x--1;\n- Q = 0, then a division by it;\n- P = 5, which has no roots, and P = 0, which has every number as a root;\n- x = abc, then -1/2 for the zero polynomial;\n- 1000001x + 1000003, too large to search.\n\nBuilt on the verified corpus case `polynomial-evaluator` (a polynomial\nworked out term by term, each term's value shown).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P066 polynomials: type two polynomials, P and Q, the usual way - like\n# 3x^2 - 2x + 1/2 - and add, subtract, multiply and divide them, evaluate\n# P at a number both term by term and by Horner's rule, take its\n# derivative, and find its rational roots. Every coefficient is an exact\n# fraction.\nimport frac\nimport poly\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef ask_poly(name):\n    while True:\n        trim(input(name + \" = \")) => s\n        if s == \"\":\n            return [False, []]\n        poly.parse(s) => r\n        if r[1] == \"\":\n            return [True, r[0]]\n        (\"Cannot read that: \" + r[1] + \". Write terms like 3x^2, -x, 1/2 or 0.25x^3.\") ^0\n\ndef show(state):\n    (\"P = \" + poly.text(state[0])) ^0\n    (\"Q = \" + poly.text(state[1])) ^0\n\ndef set_poly(state, k):\n    \"PQ\"[k] => name\n    ask_poly(name) => r\n    if not r[0]:\n        \"Cancelled.\" ^0\n        return\n    r[1] => state[k]\n    (name + \" = \" + poly.text(r[1]) + \", degree \" + degree_text(r[1]) + \".\") ^0\n\ndef degree_text(p):\n    if len(p) == 0:\n        return \"none (the zero polynomial)\"\n    return str(poly.degree(p))\n\ndef arithmetic(state):\n    state[0] => p\n    state[1] => q\n    (\"P + Q = \" + poly.text(poly.plus(p, q))) ^0\n    (\"P - Q = \" + poly.text(poly.minus(p, q))) ^0\n    (\"P * Q = \" + poly.text(poly.times(p, q))) ^0\n\ndef divide(state):\n    if len(state[1]) == 0:\n        \"Q is 0, and nothing divides by 0.\" ^0\n        return\n    poly.divided(state[0], state[1]) => d\n    (\"P / Q: quotient \" + poly.text(d[0]) + \", remainder \" + poly.text(d[1]) + \".\") ^0\n    poly.plus(poly.times(d[0], state[1]), d[1]) => back\n    (\"Check: quotient * Q + remainder = \" + poly.text(back) + \".\") ^0\n\ndef evaluate(state):\n    while True:\n        trim(input(\"x = \")) => s\n        if s == \"\":\n            \"Cancelled.\" ^0\n            return\n        \"\" => body\n        1 => sign\n        s => body\n        if len(s) > 1 and s[0] == \"-\":\n            s[1:len(s)] => body\n            -1 => sign\n        frac.parse(body) => x\n        if len(x) > 0:\n            break\n        \"Type a number like 2, -3, 1/2 or 0.5.\" ^0\n    [sign * x[0], x[1]] => x\n    state[0] => p\n    if len(p) == 0:\n        \"P is the zero polynomial: it is 0 everywhere.\" ^0\n        return\n    poly.direct(p, x) => d\n    poly.horner(p, x) => h\n    \"Term by term:\" ^0\n    for k in [0:len(p) - 1]:\n        if not frac.is_zero(p[k]):\n            (\"  \" + poly.text(poly_term(p[k], k)) + \" -> \" + frac.text(d[1][k])) ^0\n    (\"  sum = \" + frac.text(d[0]) + \", using \" + str(d[2]) + \" multiplications\") ^0\n    \"Horner's rule, from the top coefficient down:\" ^0\n    \"\" => line\n    for st in h[1]:\n        if line != \"\":\n            line + \", \" => line\n        line + frac.text(st[1]) => line\n    (\"  \" + line) ^0\n    (\"  P(\" + frac.text(x) + \") = \" + frac.text(h[0]) + \", using \" + str(poly.degree(p)) + \" multiplications\") ^0\n\ndef poly_term(c, k):\n    [[0, 1]] * (k + 1) => t\n    c => t[k]\n    return t\n\ndef derive(state):\n    poly.derivative(state[0]) => d\n    (\"P' = \" + poly.text(d)) ^0\n\ndef roots(state):\n    state[0] => p\n    if len(p) == 0:\n        \"P is the zero polynomial: every number is a root.\" ^0\n        return\n    if len(p) == 1:\n        \"P is a nonzero constant: it has no roots.\" ^0\n        return\n    poly.rational_roots(p) => r\n    if r[2] != \"\":\n        (\"Not tried: \" + r[2] + \".\") ^0\n        return\n    if len(r[0]) == 0:\n        (\"P has no rational roots (\" + str(r[1]) + \" candidates tried).\") ^0\n        return\n    \"\" => s\n    0 => count\n    for x in r[0]:\n        if s != \"\":\n            s + \", \" => s\n        s + frac.text(x[0]) => s\n        if x[1] == 2:\n            s + \" (twice)\" => s\n        elif x[1] > 2:\n            s + \" (\" + str(x[1]) + \" times)\" => s\n        count + x[1] => count\n    (\"Rational roots: \" + s + \" - \" + str(r[1]) + \" candidates tried.\") ^0\n    if count < poly.degree(p):\n        (\"The other \" + str(poly.degree(p) - count) + \" root(s) of this degree \" + str(poly.degree(p)) + \" polynomial are not rational.\") ^0\n\n\"== Polynomials ==\" ^0\n\"Write a polynomial the usual way, like 3x^2 - 2x + 1/2; every coefficient is an exact fraction.\" ^0\n# [P, Q]\n[[[-6, 1], [11, 1], [-6, 1], [1, 1]], [[-1, 1], [1, 1]]] => state\nshow(state)\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        set_poly(state, 0)\n    elif choice == \"2\":\n        set_poly(state, 1)\n    elif choice == \"3\":\n        arithmetic(state)\n    elif choice == \"4\":\n        divide(state)\n    elif choice == \"5\":\n        evaluate(state)\n    elif choice == \"6\":\n        derive(state)\n    elif choice == \"7\":\n        roots(state)\n    elif choice == \"8\":\n        state[0] => t\n        state[1] => state[0]\n        t => state[1]\n        show(state)\n    elif choice == \"9\":\n        False => running\n    else:\n        \"Pick a number from 1 to 9.\" ^0\n\"Bye.\" ^0\n",
      "python": "import frac\nimport poly\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef ask_poly(name):\n    while True:\n        s = trim(input(name + \" = \"))\n        if s == \"\":\n            return [False, []]\n        r = poly.parse(s)\n        if r[1] == \"\":\n            return [True, r[0]]\n        print(\"Cannot read that: \" + r[1] + \". Write terms like 3x^2, -x, 1/2 or 0.25x^3.\")\n\ndef show(state):\n    print(\"P = \" + poly.text(state[0]))\n    print(\"Q = \" + poly.text(state[1]))\n\ndef set_poly(state, k):\n    name = \"PQ\"[k]\n    r = ask_poly(name)\n    if not r[0]:\n        print(\"Cancelled.\")\n        return\n    state[k] = r[1]\n    print(name + \" = \" + poly.text(r[1]) + \", degree \" + degree_text(r[1]) + \".\")\n\ndef degree_text(p):\n    if len(p) == 0:\n        return \"none (the zero polynomial)\"\n    return str(poly.degree(p))\n\ndef arithmetic(state):\n    p = state[0]\n    q = state[1]\n    print(\"P + Q = \" + poly.text(poly.plus(p, q)))\n    print(\"P - Q = \" + poly.text(poly.minus(p, q)))\n    print(\"P * Q = \" + poly.text(poly.times(p, q)))\n\ndef divide(state):\n    if len(state[1]) == 0:\n        print(\"Q is 0, and nothing divides by 0.\")\n        return\n    d = poly.divided(state[0], state[1])\n    print(\"P / Q: quotient \" + poly.text(d[0]) + \", remainder \" + poly.text(d[1]) + \".\")\n    back = poly.plus(poly.times(d[0], state[1]), d[1])\n    print(\"Check: quotient * Q + remainder = \" + poly.text(back) + \".\")\n\ndef evaluate(state):\n    while True:\n        s = trim(input(\"x = \"))\n        if s == \"\":\n            print(\"Cancelled.\")\n            return\n        body = \"\"\n        sign = 1\n        body = s\n        if len(s) > 1 and s[0] == \"-\":\n            body = s[1:len(s)]\n            sign = -1\n        x = frac.parse(body)\n        if len(x) > 0:\n            break\n        print(\"Type a number like 2, -3, 1/2 or 0.5.\")\n    x = [sign * x[0], x[1]]\n    p = state[0]\n    if len(p) == 0:\n        print(\"P is the zero polynomial: it is 0 everywhere.\")\n        return\n    d = poly.direct(p, x)\n    h = poly.horner(p, x)\n    print(\"Term by term:\")\n    for k in range(0, len(p)):\n        if not frac.is_zero(p[k]):\n            print(\"  \" + poly.text(poly_term(p[k], k)) + \" -> \" + frac.text(d[1][k]))\n    print(\"  sum = \" + frac.text(d[0]) + \", using \" + str(d[2]) + \" multiplications\")\n    print(\"Horner's rule, from the top coefficient down:\")\n    line = \"\"\n    for st in h[1]:\n        if line != \"\":\n            line = line + \", \"\n        line = line + frac.text(st[1])\n    print(\"  \" + line)\n    print(\"  P(\" + frac.text(x) + \") = \" + frac.text(h[0]) + \", using \" + str(poly.degree(p)) + \" multiplications\")\n\ndef poly_term(c, k):\n    t = [[0, 1]] * (k + 1)\n    t[k] = c\n    return t\n\ndef derive(state):\n    d = poly.derivative(state[0])\n    print(\"P' = \" + poly.text(d))\n\ndef roots(state):\n    p = state[0]\n    if len(p) == 0:\n        print(\"P is the zero polynomial: every number is a root.\")\n        return\n    if len(p) == 1:\n        print(\"P is a nonzero constant: it has no roots.\")\n        return\n    r = poly.rational_roots(p)\n    if r[2] != \"\":\n        print(\"Not tried: \" + r[2] + \".\")\n        return\n    if len(r[0]) == 0:\n        print(\"P has no rational roots (\" + str(r[1]) + \" candidates tried).\")\n        return\n    s = \"\"\n    count = 0\n    for x in r[0]:\n        if s != \"\":\n            s = s + \", \"\n        s = s + frac.text(x[0])\n        if x[1] == 2:\n            s = s + \" (twice)\"\n        elif x[1] > 2:\n            s = s + \" (\" + str(x[1]) + \" times)\"\n        count = count + x[1]\n    print(\"Rational roots: \" + s + \" - \" + str(r[1]) + \" candidates tried.\")\n    if count < poly.degree(p):\n        print(\"The other \" + str(poly.degree(p) - count) + \" root(s) of this degree \" + str(poly.degree(p)) + \" polynomial are not rational.\")\n\nprint(\"== Polynomials ==\")\nprint(\"Write a polynomial the usual way, like 3x^2 - 2x + 1/2; every coefficient is an exact fraction.\")\nstate = [[[-6, 1], [11, 1], [-6, 1], [1, 1]], [[-1, 1], [1, 1]]]\nshow(state)\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        set_poly(state, 0)\n    elif choice == \"2\":\n        set_poly(state, 1)\n    elif choice == \"3\":\n        arithmetic(state)\n    elif choice == \"4\":\n        divide(state)\n    elif choice == \"5\":\n        evaluate(state)\n    elif choice == \"6\":\n        derive(state)\n    elif choice == \"7\":\n        roots(state)\n    elif choice == \"8\":\n        t = state[0]\n        state[0] = state[1]\n        state[1] = t\n        show(state)\n    elif choice == \"9\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 9.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "poly.eml",
      "eml": "# P066 polynomials - a polynomial is the list of its coefficients, exact\n# fractions, from the constant up: [c0, c1, ..., cn] is c0 + c1 x + ... +\n# cn x^n, with no zero at the end (the zero polynomial is []).\nimport frac\n\ndef trimmed(p):\n    len(p) => n\n    while n > 0 and frac.is_zero(p[n - 1]):\n        n - 1 => n\n    return p[0:n]\n\ndef degree(p):\n    return len(p) - 1\n\ndef coefficient(p, k):\n    if k < len(p):\n        return p[k]\n    return [0, 1]\n\ndef plus(p, q):\n    [] => out\n    len(p) => n\n    if len(q) > n:\n        len(q) => n\n    for k in [0:n - 1]:\n        out + [frac.plus(coefficient(p, k), coefficient(q, k))] => out\n    return trimmed(out)\n\ndef minus(p, q):\n    [] => out\n    len(p) => n\n    if len(q) > n:\n        len(q) => n\n    for k in [0:n - 1]:\n        out + [frac.minus(coefficient(p, k), coefficient(q, k))] => out\n    return trimmed(out)\n\ndef times(p, q):\n    if len(p) == 0 or len(q) == 0:\n        return []\n    [[0, 1]] * (len(p) + len(q) - 1) => out\n    for i in [0:len(p) - 1]:\n        for j in [0:len(q) - 1]:\n            frac.plus(out[i + j], frac.times(p[i], q[j])) => out[i + j]\n    return trimmed(out)\n\ndef divided(p, q):\n    # Long division: [quotient, remainder] with p = quotient * q + remainder\n    # and the remainder of lower degree than q (q not zero).\n    [] => quo\n    p[0:len(p)] => r\n    if len(r) >= len(q):\n        [[0, 1]] * (len(r) - len(q) + 1) => quo\n    while len(r) >= len(q) and len(r) > 0:\n        len(r) - len(q) => shift\n        frac.over(r[len(r) - 1], q[len(q) - 1]) => c\n        c => quo[shift]\n        for k in [0:len(q) - 1]:\n            frac.minus(r[shift + k], frac.times(c, q[k])) => r[shift + k]\n        trimmed(r[0:len(r) - 1]) => r\n    return [trimmed(quo), trimmed(r)]\n\ndef derivative(p):\n    [] => out\n    for k in [1:len(p) - 1]:\n        out + [frac.times([k, 1], p[k])] => out\n    return trimmed(out)\n\ndef horner(p, x):\n    # [value, steps]: start from the top coefficient, then again and again\n    # multiply by x and add the next one down - n multiplications for degree\n    # n. Each step is [coefficient added, value so far].\n    [0, 1] => v\n    [] => steps\n    for k in [0:len(p) - 1]:\n        len(p) - 1 - k => i\n        frac.plus(frac.times(v, x), p[i]) => v\n        steps + [[p[i], v]] => steps\n    return [v, steps]\n\ndef direct(p, x):\n    # The corpus case polynomial-evaluator's way: each term c x^k on its\n    # own, x^k by k multiplications, then the terms added up. Returns\n    # [value, each term's value, multiplications used].\n    [0, 1] => total\n    [] => parts\n    0 => mults\n    for k in [0:len(p) - 1]:\n        [1, 1] => xk\n        for i in [1:k]:\n            frac.times(xk, x) => xk\n            mults + 1 => mults\n        frac.times(p[k], xk) => term\n        if k > 0:\n            mults + 1 => mults\n        parts + [term] => parts\n        frac.plus(total, term) => total\n    return [total, parts, mults]\n\ndef text(p):\n    # 3x^2 - 2x + 1/2; \"0\" for the zero polynomial.\n    if len(p) == 0:\n        return \"0\"\n    \"\" => s\n    for k in [0:len(p) - 1]:\n        len(p) - 1 - k => i\n        p[i] => c\n        if not frac.is_zero(c):\n            c[0] < 0 => negative\n            frac.text([abs(c[0]), c[1]]) => mag\n            if i > 0 and mag == \"1\":\n                \"\" => mag\n            elif i > 0 and c[1] != 1:\n                \"(\" + mag + \")\" => mag\n            \"\" => xpart\n            if i == 1:\n                \"x\" => xpart\n            elif i > 1:\n                \"x^\" + str(i) => xpart\n            if s == \"\":\n                if negative:\n                    \"-\" => s\n            elif negative:\n                s + \" - \" => s\n            else:\n                s + \" + \" => s\n            s + mag + xpart => s\n    return s\n\ndef digits_value(s):\n    if s == \"\" or len(s) > 9:\n        return -1\n    0 => n\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef digit_like(ch):\n    return (ch >= \"0\" and ch <= \"9\") or ch == \".\" or ch == \"/\"\n\ndef parse(s):\n    # \"3x^2 - 2x + 1/2\", \"x^3 - x\", \"0.5x + 2\" as [polynomial, \"\"], or\n    # [[], what is wrong]. Terms with the same power are added; powers up to\n    # 20.\n    # Spaces are dropped - but two numbers with only spaces between them are\n    # an error, not one number: \"3 4\" is not 34.\n    \"\" => t\n    \"\" => before\n    False => gap\n    for ch in s:\n        if ch == \" \":\n            if before != \"\":\n                True => gap\n        else:\n            if gap and digit_like(before) and digit_like(ch):\n                return [[], \"two numbers side by side\"]\n            False => gap\n            ch => before\n            t + ch => t\n    if t == \"\":\n        return [[], \"it is empty\"]\n    [[0, 1]] * 21 => coeffs\n    0 => i\n    while i < len(t):\n        [1, 1] => sign\n        if t[i] == \"+\" or t[i] == \"-\":\n            if t[i] == \"-\":\n                [-1, 1] => sign\n            i + 1 => i\n        elif i > 0:\n            return [[], \"a term without + or - before it\"]\n        i => j\n        while j < len(t) and t[j] != \"x\" and t[j] != \"+\" and t[j] != \"-\":\n            j + 1 => j\n        t[i:j] => num\n        # a fraction written in brackets, as text() writes one: (9/2)x\n        if len(num) > 2 and num[0] == \"(\" and num[len(num) - 1] == \")\":\n            num[1:len(num) - 1] => num\n        [1, 1] => c\n        if num != \"\":\n            frac.parse(num) => c\n            if len(c) == 0:\n                return [[], \"the coefficient \" + num]\n        j => i\n        0 => power\n        if i < len(t) and t[i] == \"x\":\n            1 => power\n            i + 1 => i\n            if i < len(t) and t[i] == \"^\":\n                i + 1 => i\n                i => j\n                while j < len(t) and t[j] >= \"0\" and t[j] <= \"9\":\n                    j + 1 => j\n                digits_value(t[i:j]) => power\n                if power < 0:\n                    return [[], \"a power after ^ that is not a whole number\"]\n                j => i\n        elif num == \"\":\n            return [[], \"a sign with no term after it\"]\n        if power > 20:\n            return [[], \"a power above 20\"]\n        frac.plus(coeffs[power], frac.times(sign, c)) => coeffs[power]\n    return [trimmed(coeffs), \"\"]\n\ndef divisors(n):\n    # The positive divisors of n > 0, by trial up to its square root.\n    [] => small\n    [] => large\n    1 => d\n    while d * d <= n:\n        if n % d == 0:\n            small + [d] => small\n            if d * d != n:\n                [frac.quotient(n, d)] + large => large\n        d + 1 => d\n    return small + large\n\ndef rational_roots(p):\n    # The rational roots of p with their multiplicities, by the rational\n    # root theorem: after scaling p to whole coefficients, a root a/b in\n    # lowest terms has a dividing the lowest nonzero coefficient and b the\n    # leading one. Every such candidate is tried. Returns [[root, times],\n    # ...] in increasing order, the candidates tried, and \"\" or why not.\n    # The smallest whole scale that clears every denominator: their least\n    # common multiple, by exact whole-number division (a float would lose\n    # digits once the denominators multiply up).\n    1 => scale\n    for c in p:\n        frac.quotient(scale * c[1], frac.gcd(scale, c[1])) => scale\n    [] => a\n    for c in p:\n        a + [c[0] * frac.quotient(scale, c[1])] => a\n    0 => zeros\n    while zeros < len(a) and a[zeros] == 0:\n        zeros + 1 => zeros\n    [] => roots\n    if zeros > 0:\n        roots + [[[0, 1], zeros]] => roots\n    a[zeros:len(a)] => a\n    if abs(a[0]) > 1000000 or abs(a[len(a) - 1]) > 1000000:\n        return [roots, 0, \"the coefficients are too large to try every candidate\"]\n    [] => cands\n    for num in divisors(abs(a[0])):\n        for den in divisors(abs(a[len(a) - 1])):\n            for sg in [1, -1]:\n                frac.make(sg * num, den) => r\n                False => seen\n                for x in cands:\n                    if x[0] == r[0] and x[1] == r[1]:\n                        True => seen\n                if not seen:\n                    cands + [r] => cands\n    p[zeros:len(p)] => rest\n    for r in sorted_fracs(cands):\n        0 => times\n        True => going\n        while going and len(rest) > 1:\n            divided(rest, [frac.minus([0, 1], r), [1, 1]]) => d\n            if len(d[1]) == 0:\n                d[0] => rest\n                times + 1 => times\n            else:\n                False => going\n        if times > 0:\n            roots + [[r, times]] => roots\n    return [sorted_roots(roots), len(cands), \"\"]\n\ndef sorted_fracs(xs):\n    xs[0:len(xs)] => out\n    for i in [1:len(out) - 1]:\n        out[i] => x\n        i => j\n        while j > 0 and frac.less(x, out[j - 1]):\n            out[j - 1] => out[j]\n            j - 1 => j\n        x => out[j]\n    return out\n\ndef sorted_roots(rs):\n    rs[0:len(rs)] => out\n    for i in [1:len(out) - 1]:\n        out[i] => x\n        i => j\n        while j > 0 and frac.less(x[0], out[j - 1][0]):\n            out[j - 1] => out[j]\n            j - 1 => j\n        x => out[j]\n    return out\n",
      "python": "import frac\n\ndef trimmed(p):\n    n = len(p)\n    while n > 0 and frac.is_zero(p[n - 1]):\n        n = n - 1\n    return p[0:n]\n\ndef degree(p):\n    return len(p) - 1\n\ndef coefficient(p, k):\n    if k < len(p):\n        return p[k]\n    return [0, 1]\n\ndef plus(p, q):\n    out = []\n    n = len(p)\n    if len(q) > n:\n        n = len(q)\n    for k in range(0, n):\n        out = out + [frac.plus(coefficient(p, k), coefficient(q, k))]\n    return trimmed(out)\n\ndef minus(p, q):\n    out = []\n    n = len(p)\n    if len(q) > n:\n        n = len(q)\n    for k in range(0, n):\n        out = out + [frac.minus(coefficient(p, k), coefficient(q, k))]\n    return trimmed(out)\n\ndef times(p, q):\n    if len(p) == 0 or len(q) == 0:\n        return []\n    out = [[0, 1]] * (len(p) + len(q) - 1)\n    for i in range(0, len(p)):\n        for j in range(0, len(q)):\n            out[i + j] = frac.plus(out[i + j], frac.times(p[i], q[j]))\n    return trimmed(out)\n\ndef divided(p, q):\n    quo = []\n    r = p[0:len(p)]\n    if len(r) >= len(q):\n        quo = [[0, 1]] * (len(r) - len(q) + 1)\n    while len(r) >= len(q) and len(r) > 0:\n        shift = len(r) - len(q)\n        c = frac.over(r[len(r) - 1], q[len(q) - 1])\n        quo[shift] = c\n        for k in range(0, len(q)):\n            r[shift + k] = frac.minus(r[shift + k], frac.times(c, q[k]))\n        r = trimmed(r[0:len(r) - 1])\n    return [trimmed(quo), trimmed(r)]\n\ndef derivative(p):\n    out = []\n    for k in range(1, len(p)):\n        out = out + [frac.times([k, 1], p[k])]\n    return trimmed(out)\n\ndef horner(p, x):\n    v = [0, 1]\n    steps = []\n    for k in range(0, len(p)):\n        i = len(p) - 1 - k\n        v = frac.plus(frac.times(v, x), p[i])\n        steps = steps + [[p[i], v]]\n    return [v, steps]\n\ndef direct(p, x):\n    total = [0, 1]\n    parts = []\n    mults = 0\n    for k in range(0, len(p)):\n        xk = [1, 1]\n        for i in range(1, k+1):\n            xk = frac.times(xk, x)\n            mults = mults + 1\n        term = frac.times(p[k], xk)\n        if k > 0:\n            mults = mults + 1\n        parts = parts + [term]\n        total = frac.plus(total, term)\n    return [total, parts, mults]\n\ndef text(p):\n    if len(p) == 0:\n        return \"0\"\n    s = \"\"\n    for k in range(0, len(p)):\n        i = len(p) - 1 - k\n        c = p[i]\n        if not frac.is_zero(c):\n            negative = c[0] < 0\n            mag = frac.text([abs(c[0]), c[1]])\n            if i > 0 and mag == \"1\":\n                mag = \"\"\n            elif i > 0 and c[1] != 1:\n                mag = \"(\" + mag + \")\"\n            xpart = \"\"\n            if i == 1:\n                xpart = \"x\"\n            elif i > 1:\n                xpart = \"x^\" + str(i)\n            if s == \"\":\n                if negative:\n                    s = \"-\"\n            elif negative:\n                s = s + \" - \"\n            else:\n                s = s + \" + \"\n            s = s + mag + xpart\n    return s\n\ndef digits_value(s):\n    if s == \"\" or len(s) > 9:\n        return -1\n    n = 0\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef digit_like(ch):\n    return ch >= \"0\" and ch <= \"9\" or ch == \".\" or ch == \"/\"\n\ndef parse(s):\n    t = \"\"\n    before = \"\"\n    gap = False\n    for ch in s:\n        if ch == \" \":\n            if before != \"\":\n                gap = True\n        else:\n            if gap and digit_like(before) and digit_like(ch):\n                return [[], \"two numbers side by side\"]\n            gap = False\n            before = ch\n            t = t + ch\n    if t == \"\":\n        return [[], \"it is empty\"]\n    coeffs = [[0, 1]] * 21\n    i = 0\n    while i < len(t):\n        sign = [1, 1]\n        if t[i] == \"+\" or t[i] == \"-\":\n            if t[i] == \"-\":\n                sign = [-1, 1]\n            i = i + 1\n        elif i > 0:\n            return [[], \"a term without + or - before it\"]\n        j = i\n        while j < len(t) and t[j] != \"x\" and t[j] != \"+\" and t[j] != \"-\":\n            j = j + 1\n        num = t[i:j]\n        if len(num) > 2 and num[0] == \"(\" and num[len(num) - 1] == \")\":\n            num = num[1:len(num) - 1]\n        c = [1, 1]\n        if num != \"\":\n            c = frac.parse(num)\n            if len(c) == 0:\n                return [[], \"the coefficient \" + num]\n        i = j\n        power = 0\n        if i < len(t) and t[i] == \"x\":\n            power = 1\n            i = i + 1\n            if i < len(t) and t[i] == \"^\":\n                i = i + 1\n                j = i\n                while j < len(t) and t[j] >= \"0\" and t[j] <= \"9\":\n                    j = j + 1\n                power = digits_value(t[i:j])\n                if power < 0:\n                    return [[], \"a power after ^ that is not a whole number\"]\n                i = j\n        elif num == \"\":\n            return [[], \"a sign with no term after it\"]\n        if power > 20:\n            return [[], \"a power above 20\"]\n        coeffs[power] = frac.plus(coeffs[power], frac.times(sign, c))\n    return [trimmed(coeffs), \"\"]\n\ndef divisors(n):\n    small = []\n    large = []\n    d = 1\n    while d * d <= n:\n        if n % d == 0:\n            small = small + [d]\n            if d * d != n:\n                large = [frac.quotient(n, d)] + large\n        d = d + 1\n    return small + large\n\ndef rational_roots(p):\n    scale = 1\n    for c in p:\n        scale = frac.quotient(scale * c[1], frac.gcd(scale, c[1]))\n    a = []\n    for c in p:\n        a = a + [c[0] * frac.quotient(scale, c[1])]\n    zeros = 0\n    while zeros < len(a) and a[zeros] == 0:\n        zeros = zeros + 1\n    roots = []\n    if zeros > 0:\n        roots = roots + [[[0, 1], zeros]]\n    a = a[zeros:len(a)]\n    if abs(a[0]) > 1000000 or abs(a[len(a) - 1]) > 1000000:\n        return [roots, 0, \"the coefficients are too large to try every candidate\"]\n    cands = []\n    for num in divisors(abs(a[0])):\n        for den in divisors(abs(a[len(a) - 1])):\n            for sg in [1, -1]:\n                r = frac.make(sg * num, den)\n                seen = False\n                for x in cands:\n                    if x[0] == r[0] and x[1] == r[1]:\n                        seen = True\n                if not seen:\n                    cands = cands + [r]\n    rest = p[zeros:len(p)]\n    for r in sorted_fracs(cands):\n        times = 0\n        going = True\n        while going and len(rest) > 1:\n            d = divided(rest, [frac.minus([0, 1], r), [1, 1]])\n            if len(d[1]) == 0:\n                rest = d[0]\n                times = times + 1\n            else:\n                going = False\n        if times > 0:\n            roots = roots + [[r, times]]\n    return [sorted_roots(roots), len(cands), \"\"]\n\ndef sorted_fracs(xs):\n    out = xs[0:len(xs)]\n    for i in range(1, len(out)):\n        x = out[i]\n        j = i\n        while j > 0 and frac.less(x, out[j - 1]):\n            out[j] = out[j - 1]\n            j = j - 1\n        out[j] = x\n    return out\n\ndef sorted_roots(rs):\n    out = rs[0:len(rs)]\n    for i in range(1, len(out)):\n        x = out[i]\n        j = i\n        while j > 0 and frac.less(x[0], out[j - 1][0]):\n            out[j] = out[j - 1]\n            j = j - 1\n        out[j] = x\n    return out\n"
    },
    {
      "name": "frac.eml",
      "eml": "# P066 polynomials - exact fractions, from P007 (calculator) by way of\n# P038, P044, P056, P060 and P062.\n# A number is a list [n, d]: n / d in lowest terms with d > 0. Integers have\n# no size limit, but EML has no //, and a / b goes through a float that cannot\n# hold a large quotient exactly, so whole-number division is written out.\n\ndef quotient(a, b):\n    # a // b for whole numbers a >= 0 and b > 0, exact at any size. Long\n    # division by doubling: take away the largest b * 2^k that still fits.\n    0 => q\n    while a >= b:\n        b => m\n        1 => k\n        while m + m <= a:\n            m + m => m\n            k + k => k\n        a - m => a\n        q + k => q\n    return q\n\ndef gcd(a, b):\n    while b != 0:\n        a % b => r\n        b => a\n        r => b\n    return a\n\ndef make(n, d):\n    # n / d in lowest terms with a positive denominator (d != 0).\n    if d < 0:\n        0 - n => n\n        0 - d => d\n    abs(n) => a\n    gcd(a, d) => g\n    if n < 0:\n        return [0 - quotient(a, g), quotient(d, g)]\n    return [quotient(a, g), quotient(d, g)]\n\ndef plus(x, y):\n    return make(x[0] * y[1] + y[0] * x[1], x[1] * y[1])\n\ndef minus(x, y):\n    return make(x[0] * y[1] - y[0] * x[1], x[1] * y[1])\n\ndef times(x, y):\n    return make(x[0] * y[0], x[1] * y[1])\n\ndef over(x, y):\n    # x / y; the caller has checked that y is not zero.\n    return make(x[0] * y[1], x[1] * y[0])\n\ndef is_zero(x):\n    return x[0] == 0\n\ndef digits_value(s):\n    # The value of a string of 1 to 9 digits, otherwise -1.\n    if s == \"\" or len(s) > 9:\n        return -1\n    0 => n\n    for c in s:\n        if not (c in \"0123456789\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef parse(s):\n    # \"3\", \"-2\", \"3/4\", \"-0.25\" as a fraction; [] if it is not a number.\n    1 => sign\n    if len(s) > 0 and s[0] == \"-\":\n        -1 => sign\n        s[1:len(s)] => s\n    0 => k\n    while k < len(s) and s[k] != \"/\" and s[k] != \".\":\n        k + 1 => k\n    if k == len(s):\n        digits_value(s) => n\n        if n < 0:\n            return []\n        return make(sign * n, 1)\n    digits_value(s[0:k]) => whole\n    s[k + 1:len(s)] => rest\n    digits_value(rest) => part\n    if whole < 0 or part < 0:\n        return []\n    if s[k] == \"/\":\n        if part == 0:\n            return []\n        return make(sign * whole, part)\n    # a decimal point: 0.25 is 25 / 100\n    1 => scale\n    for c in rest:\n        scale * 10 => scale\n    return make(sign * (whole * scale + part), scale)\n\ndef less(x, y):\n    return x[0] * y[1] < y[0] * x[1]\n\ndef decimal(x, places):\n    # x to the given number of decimal places, halves rounded away from zero,\n    # with \"about \" in front when the value does not end there exactly.\n    \"\" => sign\n    abs(x[0]) => n\n    if x[0] < 0:\n        \"-\" => sign\n    1 => scale\n    for k in [1:places]:\n        scale * 10 => scale\n    quotient(2 * n * scale + x[1], 2 * x[1]) => t\n    \"\" => about\n    if (n * scale) % x[1] != 0:\n        \"about \" => about\n    str(quotient(t, scale)) => whole\n    str(t % scale) => part\n    while len(part) < places:\n        \"0\" + part => part\n    # drop trailing zeros of an exact value\n    if about == \"\":\n        while len(part) > 0 and part[len(part) - 1] == \"0\":\n            part[0:len(part) - 1] => part\n    if t == 0:\n        \"\" => sign\n    if part == \"\":\n        return about + sign + whole\n    return about + sign + whole + \".\" + part\n\ndef text(x):\n    # \"3\", \"-3\", \"3/4\", \"-3/4\".\n    if x[1] == 1:\n        return str(x[0])\n    return str(x[0]) + \"/\" + str(x[1])\n",
      "python": "def quotient(a, b):\n    q = 0\n    while a >= b:\n        m = b\n        k = 1\n        while m + m <= a:\n            m = m + m\n            k = k + k\n        a = a - m\n        q = q + k\n    return q\n\ndef gcd(a, b):\n    while b != 0:\n        r = a % b\n        a = b\n        b = r\n    return a\n\ndef make(n, d):\n    if d < 0:\n        n = 0 - n\n        d = 0 - d\n    a = abs(n)\n    g = gcd(a, d)\n    if n < 0:\n        return [0 - quotient(a, g), quotient(d, g)]\n    return [quotient(a, g), quotient(d, g)]\n\ndef plus(x, y):\n    return make(x[0] * y[1] + y[0] * x[1], x[1] * y[1])\n\ndef minus(x, y):\n    return make(x[0] * y[1] - y[0] * x[1], x[1] * y[1])\n\ndef times(x, y):\n    return make(x[0] * y[0], x[1] * y[1])\n\ndef over(x, y):\n    return make(x[0] * y[1], x[1] * y[0])\n\ndef is_zero(x):\n    return x[0] == 0\n\ndef digits_value(s):\n    if s == \"\" or len(s) > 9:\n        return -1\n    n = 0\n    for c in s:\n        if not c in \"0123456789\":\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef parse(s):\n    sign = 1\n    if len(s) > 0 and s[0] == \"-\":\n        sign = -1\n        s = s[1:len(s)]\n    k = 0\n    while k < len(s) and s[k] != \"/\" and s[k] != \".\":\n        k = k + 1\n    if k == len(s):\n        n = digits_value(s)\n        if n < 0:\n            return []\n        return make(sign * n, 1)\n    whole = digits_value(s[0:k])\n    rest = s[k + 1:len(s)]\n    part = digits_value(rest)\n    if whole < 0 or part < 0:\n        return []\n    if s[k] == \"/\":\n        if part == 0:\n            return []\n        return make(sign * whole, part)\n    scale = 1\n    for c in rest:\n        scale = scale * 10\n    return make(sign * (whole * scale + part), scale)\n\ndef less(x, y):\n    return x[0] * y[1] < y[0] * x[1]\n\ndef decimal(x, places):\n    sign = \"\"\n    n = abs(x[0])\n    if x[0] < 0:\n        sign = \"-\"\n    scale = 1\n    for k in range(1, places+1):\n        scale = scale * 10\n    t = quotient(2 * n * scale + x[1], 2 * x[1])\n    about = \"\"\n    if n * scale % x[1] != 0:\n        about = \"about \"\n    whole = str(quotient(t, scale))\n    part = str(t % scale)\n    while len(part) < places:\n        part = \"0\" + part\n    if about == \"\":\n        while len(part) > 0 and part[len(part) - 1] == \"0\":\n            part = part[0:len(part) - 1]\n    if t == 0:\n        sign = \"\"\n    if part == \"\":\n        return about + sign + whole\n    return about + sign + whole + \".\" + part\n\ndef text(x):\n    if x[1] == 1:\n        return str(x[0])\n    return str(x[0]) + \"/\" + str(x[1])\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n\n1\n3 4\nx^\n2y\nx^21\n1/0\nx--1\n\n2\n0\n4\n1\n5\n7\n1\n0\n7\n5\nabc\n-1/2\n1\n1000001x + 1000003\n7\n9\n",
      "screen": "== Polynomials ==\nWrite a polynomial the usual way, like 3x^2 - 2x + 1/2; every coefficient is an exact fraction.\nP = x^3 - 6x^2 + 11x - 6\nQ = x - 1\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 0\nPick a number from 1 to 9.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> x\nPick a number from 1 to 9.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = \nCancelled.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = 3 4\nCannot read that: two numbers side by side. Write terms like 3x^2, -x, 1/2 or 0.25x^3.\nP = x^\nCannot read that: a power after ^ that is not a whole number. Write terms like 3x^2, -x, 1/2 or 0.25x^3.\nP = 2y\nCannot read that: the coefficient 2y. Write terms like 3x^2, -x, 1/2 or 0.25x^3.\nP = x^21\nCannot read that: a power above 20. Write terms like 3x^2, -x, 1/2 or 0.25x^3.\nP = 1/0\nCannot read that: the coefficient 1/0. Write terms like 3x^2, -x, 1/2 or 0.25x^3.\nP = x--1\nCannot read that: a sign with no term after it. Write terms like 3x^2, -x, 1/2 or 0.25x^3.\nP = \nCancelled.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 2\nQ = 0\nQ = 0, degree none (the zero polynomial).\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 4\nQ is 0, and nothing divides by 0.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = 5\nP = 5, degree 0.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nP is a nonzero constant: it has no roots.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = 0\nP = 0, degree none (the zero polynomial).\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nP is the zero polynomial: every number is a root.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 5\nx = abc\nType a number like 2, -3, 1/2 or 0.5.\nx = -1/2\nP is the zero polynomial: it is 0 everywhere.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = 1000001x + 1000003\nP = 1000001x + 1000003, degree 1.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nNot tried: the coefficients are too large to try every candidate.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 9\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "3\n4\n5\n4\n6\n7\n1\n2x^4 - 3x^3 - 2x^2 + 3x\n7\n2\nx^2 + 1\n4\n5\n1/2\n1\nx^4 - 4x^3 + 6x^2 - 4x + 1\n7\n1\nx^2 - 2\n7\n1\nx^3 - 2x\n7\n8\n9\n",
      "screen": "== Polynomials ==\nWrite a polynomial the usual way, like 3x^2 - 2x + 1/2; every coefficient is an exact fraction.\nP = x^3 - 6x^2 + 11x - 6\nQ = x - 1\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 3\nP + Q = x^3 - 6x^2 + 12x - 7\nP - Q = x^3 - 6x^2 + 10x - 5\nP * Q = x^4 - 7x^3 + 17x^2 - 17x + 6\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 4\nP / Q: quotient x^2 - 5x + 6, remainder 0.\nCheck: quotient * Q + remainder = x^3 - 6x^2 + 11x - 6.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 5\nx = 4\nTerm by term:\n  -6 -> -6\n  11x -> 44\n  -6x^2 -> -96\n  x^3 -> 64\n  sum = 6, using 9 multiplications\nHorner's rule, from the top coefficient down:\n  1, -2, 3, 6\n  P(4) = 6, using 3 multiplications\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 6\nP' = 3x^2 - 12x + 11\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nRational roots: 1, 2, 3 - 8 candidates tried.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = 2x^4 - 3x^3 - 2x^2 + 3x\nP = 2x^4 - 3x^3 - 2x^2 + 3x, degree 4.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nRational roots: -1, 0, 1, 3/2 - 8 candidates tried.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 2\nQ = x^2 + 1\nQ = x^2 + 1, degree 2.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 4\nP / Q: quotient 2x^2 - 3x - 4, remainder 6x + 4.\nCheck: quotient * Q + remainder = 2x^4 - 3x^3 - 2x^2 + 3x.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 5\nx = 1/2\nTerm by term:\n  3x -> 3/2\n  -2x^2 -> -1/2\n  -3x^3 -> -3/8\n  2x^4 -> 1/8\n  sum = 3/4, using 14 multiplications\nHorner's rule, from the top coefficient down:\n  2, -2, -3, 3/2, 3/4\n  P(1/2) = 3/4, using 4 multiplications\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = x^4 - 4x^3 + 6x^2 - 4x + 1\nP = x^4 - 4x^3 + 6x^2 - 4x + 1, degree 4.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nRational roots: 1 (4 times) - 2 candidates tried.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = x^2 - 2\nP = x^2 - 2, degree 2.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nP has no rational roots (4 candidates tried).\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 1\nP = x^3 - 2x\nP = x^3 - 2x, degree 3.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 7\nRational roots: 0 - 4 candidates tried.\nThe other 2 root(s) of this degree 3 polynomial are not rational.\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 8\nP = x^2 + 1\nQ = x^3 - 2x\n\n1) set P  2) set Q  3) P + Q, P - Q, P * Q  4) P / Q  5) P at x  6) P'  7) roots of P  8) swap P and Q  9) quit\nchoice> 9\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "polynomial-evaluator",
      "caseId": "164-polynomial-evaluator",
      "title": "A polynomial as (coefficient, exponent) pairs"
    }
  ],
  "updated": "2026-10-14"
}
