{
  "id": "P164",
  "slug": "travel-expenses",
  "key": "P164-travel-expenses",
  "title": "Travel expenses",
  "summary": "A trip paid by card in four currencies and reimbursed in dollars: an amount column that adds yen to cents, lines summed at ten days' rates, and a card that quotes at authorization and charges at capture.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P164 - Travel expenses\n\nA ten-day trip paid by card in four currencies and reimbursed in dollars:\nwhat each total is a sum of.\n\n- `main.eml` - the menu, the expenses, the rates, the totals, the card's\n  quote and charge, and an expense of your own\n- `fx.eml` - the currencies and their minor units, the rates day by day,\n  the expenses, and conversion to dollar cents\n\nHow each part works:\n\n- Seven expenses in dollars, yen, pounds and euros, each authorized on one\n  day and captured on a later one; the rates move both ways over the ten\n  days. Amounts are whole minor units (yen has none smaller), rates are in\n  millionths of a dollar, and a conversion is rounded half-up to the cent.\n- As in the corpus case `the-total-was-checked-and-the-currencies-were-mixed`,\n  the amount column adds up to 301,490: cents, yen, pence and euro cents in\n  one number, which a check of the lines against a stored sum of the same\n  column passes. By currency the trip is 842.00 USD, 108,800 JPY, 578.50\n  GBP and 506.40 EUR.\n- As in `the-amounts-were-summed-at-each-days-rate`, each line at the rate of\n  the day it was paid comes to 2,848.50, a sum over the rates of six days;\n  every line at the last day's rate, the value at one moment, is 2,864.65.\n- As in `the-exchange-rate-was-right-and-its-timestamp-was-not`, the card\n  quotes at authorization and charges at capture: 2,848.50 quoted and\n  2,852.27 charged. Five lines were charged more than their quote and the\n  Tokyo hotel 0.96 less, the side nobody writes in about.\n\nWhat is checked: menu choices 1 to 6; a currency (USD, JPY, GBP or EUR, in\ncapitals or not); an amount in yen from 1 to 9999999, or otherwise written\nas 12, 12.5 or 12.50 from 0.01 to 99999.99; a day authorized from 1 to 10\nand a day captured from that day to 10. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the expenses, the rates, the totals, and the quote and charge;\n- 85.50 EUR from day 3 to day 4 (charged more), 64,000 JPY from day 2 to\n  day 5 (charged less), and 10.00 USD (the charge is the quote).\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- currencies: a cancel and CHF;\n- yen: a cancel, 0, 10000000 and 1.5; pounds: a cancel, 0, 100000, 12.345\n  and x;\n- days authorized: a cancel, 0 and 11; days captured: a cancel, 3, 11 and\n  x;\n- 20.00 GBP authorized and captured on day 4.\n\nBuilt on the verified corpus cases `the-total-was-checked-and-the-currencies-were-mixed`\n(a nightly reconciliation that adds the amount column across nine\ncurrencies), `the-amounts-were-summed-at-each-days-rate` (a balance whose\nlines keep the rates of their own days) and\n`the-exchange-rate-was-right-and-its-timestamp-was-not` (a payment quoted at\nauthorization and settled at capture).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P164 travel expenses: a trip paid in four currencies - the expenses, the\n# rates, what the totals are sums of, the card's quote and charge, and an\n# expense of your own.\nimport fx\n\ndef plain(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 pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef digits(t):\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            return False\n    return len(t) > 0\n\ndef commas(n):\n    str(n) => s\n    \"\" => out\n    0 => k\n    len(s) => i\n    while i > 0:\n        if k == 3:\n            \",\" + out => out\n            0 => k\n        s[i - 1] + out => out\n        k + 1 => k\n        i - 1 => i\n    return out\n\ndef zeros(n, places):\n    str(n) => s\n    while len(s) < places:\n        \"0\" + s => s\n    return s\n\ndef money(c):\n    # c >= 0 cents\n    return commas(int((c - c % 100) / 100)) + \".\" + zeros(c % 100, 2)\n\ndef signed(c):\n    if c > 0:\n        return \"+\" + money(c)\n    if c < 0:\n        return \"-\" + money(0 - c)\n    return \"0.00\"\n\ndef amount(cur, m):\n    if fx.places(cur) == 0:\n        return commas(m) + \" \" + cur\n    return money(m) + \" \" + cur\n\ndef show_expenses():\n    \"The trip, ten days, every payment by card, reimbursed in dollars:\" ^0\n    (\"  \" + pad(\"item\", 25) + lpad(\"amount\", 14) + \"  \" + pad(\"authorized\", 12) + \"captured\") ^0\n    for e in fx.expenses():\n        (\"  \" + pad(e[0], 25) + lpad(amount(e[1], e[2]), 14) + \"  \" + pad(\"day \" + str(e[3]), 12) + \"day \" + str(e[4])) ^0\n\ndef show_rates():\n    \"Dollars for one unit, day by day:\" ^0\n    (\"  \" + pad(\"day\", 4) + lpad(\"JPY\", 10) + lpad(\"GBP\", 9) + lpad(\"EUR\", 9)) ^0\n    1 => d\n    while d <= 10:\n        fx.rate(\"JPY\", d) => j\n        fx.rate(\"GBP\", d) => g\n        fx.rate(\"EUR\", d) => u\n        (\"  \" + pad(str(d), 4) + lpad(\"0.\" + zeros(j, 6), 10) + lpad(\"1.\" + zeros(int((g - 1000000) / 100), 4), 9) + lpad(\"1.\" + zeros(int((u - 1000000) / 100), 4), 9)) ^0\n        d + 1 => d\n\ndef show_totals():\n    0 => column\n    0 => own_day\n    0 => last_day\n    [] => days\n    for e in fx.expenses():\n        column + e[2] => column\n        own_day + fx.cents(e[1], e[2], e[3]) => own_day\n        last_day + fx.cents(e[1], e[2], 10) => last_day\n        False => found\n        for d in days:\n            if d == e[3]:\n                True => found\n        if not found and e[1] != \"USD\":\n            days + [e[3]] => days\n    (\"The amount column added: \" + commas(column) + \" - cents, yen, pence and euro cents in one number.\") ^0\n    \"The report stores the same sum as its total, so the check of the lines against the total passes, and the total is in no currency.\" ^0\n    \"\" => by\n    for c in fx.currencies():\n        0 => t\n        for e in fx.expenses():\n            if e[1] == c[0]:\n                t + e[2] => t\n        if by != \"\":\n            by + \", \" => by\n        by + amount(c[0], t) => by\n    (\"By currency: \" + by + \".\") ^0\n    (\"In dollars, each line at the rate of the day it was paid: \" + money(own_day) + \".\") ^0\n    (\"Every line at the last day's rate: \" + money(last_day) + \", which is \" + money(last_day - own_day) + \" more: the value at one moment, where the first total mixes the rates of \" + str(len(days)) + \" days.\") ^0\n\ndef show_card():\n    \"Quoted at authorization and charged at capture, in dollars:\" ^0\n    (\"  \" + pad(\"item\", 25) + lpad(\"quoted\", 9) + lpad(\"charged\", 10) + lpad(\"difference\", 12)) ^0\n    0 => q\n    0 => ch\n    0 => more\n    0 => less\n    for e in fx.expenses():\n        fx.cents(e[1], e[2], e[3]) => a\n        fx.cents(e[1], e[2], e[4]) => b\n        q + a => q\n        ch + b => ch\n        if b > a:\n            more + 1 => more\n        if b < a:\n            less + 1 => less\n        (\"  \" + pad(e[0], 25) + lpad(money(a), 9) + lpad(money(b), 10) + lpad(signed(b - a), 12)) ^0\n    (\"Quoted in all \" + money(q) + \", charged \" + money(ch) + \": \" + signed(ch - q) + \".\") ^0\n    (str(more) + \" lines were charged more than their quote and \" + str(less) + \" less. Only those charged more get written in about, so the complaints see one side of a difference that runs both ways.\") ^0\n\ndef price(t, top):\n    # cents for an amount typed as 12, 12.5 or 12.50 with up to top digits\n    # before the point; -1 if it is not one\n    0 => dot\n    0 => i\n    0 => at\n    for c in t:\n        if c == \".\":\n            dot + 1 => dot\n            i => at\n        elif c < \"0\" or c > \"9\":\n            return 0 - 1\n        i + 1 => i\n    if dot == 0:\n        if len(t) < 1 or len(t) > top:\n            return 0 - 1\n        return int(t) * 100\n    if dot > 1 or at < 1 or at > top or len(t) - at - 1 < 1 or len(t) - at - 1 > 2:\n        return 0 - 1\n    t[at + 1:] => f\n    if len(f) == 1:\n        f + \"0\" => f\n    return int(t[0:at]) * 100 + int(f)\n\ndef own():\n    plain(input(\"currency (USD, JPY, GBP or EUR)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    \"\" => cur\n    0 => k\n    for c in [\"USD\", \"JPY\", \"GBP\", \"EUR\"]:\n        if t == c or t == [\"usd\", \"jpy\", \"gbp\", \"eur\"][k]:\n            c => cur\n        k + 1 => k\n    if cur == \"\":\n        \"Type USD, JPY, GBP or EUR.\" ^0\n        return\n    if cur == \"JPY\":\n        plain(input(\"amount in yen (1 to 9999999)> \")) => a\n        if a == \"\":\n            \"Cancelled.\" ^0\n            return\n        if len(a) > 7 or not digits(a) or int(a) < 1:\n            \"Type a whole number of yen from 1 to 9999999.\" ^0\n            return\n        int(a) => m\n    else:\n        plain(input(\"amount (0.01 to 99999.99)> \")) => a\n        if a == \"\":\n            \"Cancelled.\" ^0\n            return\n        price(a, 5) => m\n        if m < 1:\n            \"Type an amount such as 12.50, from 0.01 to 99999.99.\" ^0\n            return\n    plain(input(\"day authorized (1 to 10)> \")) => d1\n    if d1 == \"\":\n        \"Cancelled.\" ^0\n        return\n    if len(d1) > 2 or not digits(d1) or int(d1) < 1 or int(d1) > 10:\n        \"Type a whole number from 1 to 10.\" ^0\n        return\n    int(d1) => a1\n    plain(input(\"day captured (\" + str(a1) + \" to 10)> \")) => d2\n    if d2 == \"\":\n        \"Cancelled.\" ^0\n        return\n    if len(d2) > 2 or not digits(d2) or int(d2) < a1 or int(d2) > 10:\n        (\"Type a whole number from \" + str(a1) + \" to 10.\") ^0\n        return\n    int(d2) => a2\n    fx.cents(cur, m, a1) => qa\n    fx.cents(cur, m, a2) => cb\n    (\"Quoted at authorization, day \" + str(a1) + \": \" + money(qa) + \"; charged at capture, day \" + str(a2) + \": \" + money(cb) + \".\") ^0\n    if cb > qa:\n        (signed(cb - qa) + \": charged more than the quote the traveller agreed to.\") ^0\n    elif cb < qa:\n        (signed(cb - qa) + \": charged less than the quote, and nobody writes in about that.\") ^0\n    else:\n        \"The charge is the quote.\" ^0\n\n\"== Travel expenses ==\" ^0\n\"A trip paid in four currencies and reimbursed in dollars.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_expenses()\n    elif choice == \"2\":\n        show_rates()\n    elif choice == \"3\":\n        show_totals()\n    elif choice == \"4\":\n        show_card()\n    elif choice == \"5\":\n        own()\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import fx\n\ndef plain(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 pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef digits(t):\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            return False\n    return len(t) > 0\n\ndef commas(n):\n    s = str(n)\n    out = \"\"\n    k = 0\n    i = len(s)\n    while i > 0:\n        if k == 3:\n            out = \",\" + out\n            k = 0\n        out = s[i - 1] + out\n        k = k + 1\n        i = i - 1\n    return out\n\ndef zeros(n, places):\n    s = str(n)\n    while len(s) < places:\n        s = \"0\" + s\n    return s\n\ndef money(c):\n    return commas(int((c - c % 100) / 100)) + \".\" + zeros(c % 100, 2)\n\ndef signed(c):\n    if c > 0:\n        return \"+\" + money(c)\n    if c < 0:\n        return \"-\" + money(0 - c)\n    return \"0.00\"\n\ndef amount(cur, m):\n    if fx.places(cur) == 0:\n        return commas(m) + \" \" + cur\n    return money(m) + \" \" + cur\n\ndef show_expenses():\n    print(\"The trip, ten days, every payment by card, reimbursed in dollars:\")\n    print(\"  \" + pad(\"item\", 25) + lpad(\"amount\", 14) + \"  \" + pad(\"authorized\", 12) + \"captured\")\n    for e in fx.expenses():\n        print(\"  \" + pad(e[0], 25) + lpad(amount(e[1], e[2]), 14) + \"  \" + pad(\"day \" + str(e[3]), 12) + \"day \" + str(e[4]))\n\ndef show_rates():\n    print(\"Dollars for one unit, day by day:\")\n    print(\"  \" + pad(\"day\", 4) + lpad(\"JPY\", 10) + lpad(\"GBP\", 9) + lpad(\"EUR\", 9))\n    d = 1\n    while d <= 10:\n        j = fx.rate(\"JPY\", d)\n        g = fx.rate(\"GBP\", d)\n        u = fx.rate(\"EUR\", d)\n        print(\"  \" + pad(str(d), 4) + lpad(\"0.\" + zeros(j, 6), 10) + lpad(\"1.\" + zeros(int((g - 1000000) / 100), 4), 9) + lpad(\"1.\" + zeros(int((u - 1000000) / 100), 4), 9))\n        d = d + 1\n\ndef show_totals():\n    column = 0\n    own_day = 0\n    last_day = 0\n    days = []\n    for e in fx.expenses():\n        column = column + e[2]\n        own_day = own_day + fx.cents(e[1], e[2], e[3])\n        last_day = last_day + fx.cents(e[1], e[2], 10)\n        found = False\n        for d in days:\n            if d == e[3]:\n                found = True\n        if not found and e[1] != \"USD\":\n            days = days + [e[3]]\n    print(\"The amount column added: \" + commas(column) + \" - cents, yen, pence and euro cents in one number.\")\n    print(\"The report stores the same sum as its total, so the check of the lines against the total passes, and the total is in no currency.\")\n    by = \"\"\n    for c in fx.currencies():\n        t = 0\n        for e in fx.expenses():\n            if e[1] == c[0]:\n                t = t + e[2]\n        if by != \"\":\n            by = by + \", \"\n        by = by + amount(c[0], t)\n    print(\"By currency: \" + by + \".\")\n    print(\"In dollars, each line at the rate of the day it was paid: \" + money(own_day) + \".\")\n    print(\"Every line at the last day's rate: \" + money(last_day) + \", which is \" + money(last_day - own_day) + \" more: the value at one moment, where the first total mixes the rates of \" + str(len(days)) + \" days.\")\n\ndef show_card():\n    print(\"Quoted at authorization and charged at capture, in dollars:\")\n    print(\"  \" + pad(\"item\", 25) + lpad(\"quoted\", 9) + lpad(\"charged\", 10) + lpad(\"difference\", 12))\n    q = 0\n    ch = 0\n    more = 0\n    less = 0\n    for e in fx.expenses():\n        a = fx.cents(e[1], e[2], e[3])\n        b = fx.cents(e[1], e[2], e[4])\n        q = q + a\n        ch = ch + b\n        if b > a:\n            more = more + 1\n        if b < a:\n            less = less + 1\n        print(\"  \" + pad(e[0], 25) + lpad(money(a), 9) + lpad(money(b), 10) + lpad(signed(b - a), 12))\n    print(\"Quoted in all \" + money(q) + \", charged \" + money(ch) + \": \" + signed(ch - q) + \".\")\n    print(str(more) + \" lines were charged more than their quote and \" + str(less) + \" less. Only those charged more get written in about, so the complaints see one side of a difference that runs both ways.\")\n\ndef price(t, top):\n    dot = 0\n    i = 0\n    at = 0\n    for c in t:\n        if c == \".\":\n            dot = dot + 1\n            at = i\n        elif c < \"0\" or c > \"9\":\n            return 0 - 1\n        i = i + 1\n    if dot == 0:\n        if len(t) < 1 or len(t) > top:\n            return 0 - 1\n        return int(t) * 100\n    if dot > 1 or at < 1 or at > top or len(t) - at - 1 < 1 or len(t) - at - 1 > 2:\n        return 0 - 1\n    f = t[at + 1:]\n    if len(f) == 1:\n        f = f + \"0\"\n    return int(t[0:at]) * 100 + int(f)\n\ndef own():\n    t = plain(input(\"currency (USD, JPY, GBP or EUR)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    cur = \"\"\n    k = 0\n    for c in [\"USD\", \"JPY\", \"GBP\", \"EUR\"]:\n        if t == c or t == [\"usd\", \"jpy\", \"gbp\", \"eur\"][k]:\n            cur = c\n        k = k + 1\n    if cur == \"\":\n        print(\"Type USD, JPY, GBP or EUR.\")\n        return\n    if cur == \"JPY\":\n        a = plain(input(\"amount in yen (1 to 9999999)> \"))\n        if a == \"\":\n            print(\"Cancelled.\")\n            return\n        if len(a) > 7 or not digits(a) or int(a) < 1:\n            print(\"Type a whole number of yen from 1 to 9999999.\")\n            return\n        m = int(a)\n    else:\n        a = plain(input(\"amount (0.01 to 99999.99)> \"))\n        if a == \"\":\n            print(\"Cancelled.\")\n            return\n        m = price(a, 5)\n        if m < 1:\n            print(\"Type an amount such as 12.50, from 0.01 to 99999.99.\")\n            return\n    d1 = plain(input(\"day authorized (1 to 10)> \"))\n    if d1 == \"\":\n        print(\"Cancelled.\")\n        return\n    if len(d1) > 2 or not digits(d1) or int(d1) < 1 or int(d1) > 10:\n        print(\"Type a whole number from 1 to 10.\")\n        return\n    a1 = int(d1)\n    d2 = plain(input(\"day captured (\" + str(a1) + \" to 10)> \"))\n    if d2 == \"\":\n        print(\"Cancelled.\")\n        return\n    if len(d2) > 2 or not digits(d2) or int(d2) < a1 or int(d2) > 10:\n        print(\"Type a whole number from \" + str(a1) + \" to 10.\")\n        return\n    a2 = int(d2)\n    qa = fx.cents(cur, m, a1)\n    cb = fx.cents(cur, m, a2)\n    print(\"Quoted at authorization, day \" + str(a1) + \": \" + money(qa) + \"; charged at capture, day \" + str(a2) + \": \" + money(cb) + \".\")\n    if cb > qa:\n        print(signed(cb - qa) + \": charged more than the quote the traveller agreed to.\")\n    elif cb < qa:\n        print(signed(cb - qa) + \": charged less than the quote, and nobody writes in about that.\")\n    else:\n        print(\"The charge is the quote.\")\n\nprint(\"== Travel expenses ==\")\nprint(\"A trip paid in four currencies and reimbursed in dollars.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_expenses()\n    elif choice == \"2\":\n        show_rates()\n    elif choice == \"3\":\n        show_totals()\n    elif choice == \"4\":\n        show_card()\n    elif choice == \"5\":\n        own()\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "fx.eml",
      "eml": "# P164 travel expenses - a ten-day trip paid in four currencies and\n# reimbursed in dollars. As in the corpus case\n# the-total-was-checked-and-the-currencies-were-mixed, the amount column\n# holds yen, pence, euro cents and cents, and adding it gives a number in\n# no currency that a check against a stored sum of the same column passes.\n# As in the-amounts-were-summed-at-each-days-rate, each line converted at\n# its own day's rate is right and their sum mixes ten rates. And as in\n# the-exchange-rate-was-right-and-its-timestamp-was-not, a card payment is\n# quoted at authorization and charged at capture, two correct rates from two\n# moments.\n\ndef currencies():\n    # [code, decimal places of its minor unit]\n    return [[\"USD\", 2], [\"JPY\", 0], [\"GBP\", 2], [\"EUR\", 2]]\n\ndef places(cur):\n    for c in currencies():\n        if c[0] == cur:\n            return c[1]\n    return 2\n\ndef rate(cur, d):\n    # dollars for one unit of cur on day d (1 to 10), in millionths\n    if cur == \"JPY\":\n        return [6600, 6620, 6585, 6640, 6610, 6655, 6670, 6650, 6690, 6705][d - 1]\n    if cur == \"GBP\":\n        return [1260000, 1264000, 1261500, 1266000, 1270500, 1268000, 1272000, 1269500, 1275000, 1279000][d - 1]\n    if cur == \"EUR\":\n        return [1080000, 1082500, 1079000, 1084000, 1087000, 1083500, 1088000, 1090500, 1086000, 1092500][d - 1]\n    return 1000000\n\ndef expenses():\n    # [item, currency, amount in its minor unit, day authorized, day captured]\n    return [[\"flight\", \"USD\", 84200, 1, 1],\n            [\"hotel, Tokyo, 3 nights\", \"JPY\", 96000, 2, 5],\n            [\"dinner, Tokyo\", \"JPY\", 12800, 3, 4],\n            [\"hotel, London, 3 nights\", \"GBP\", 54000, 5, 9],\n            [\"taxi, London\", \"GBP\", 3850, 6, 7],\n            [\"hotel, Paris, 2 nights\", \"EUR\", 41000, 8, 10],\n            [\"dinner, Paris\", \"EUR\", 9640, 9, 10]]\n\ndef cents(cur, m, d):\n    # m minor units of cur, in dollar cents at day d's rate, rounded half-up\n    1 => den\n    0 => k\n    while k < places(cur) + 4:\n        den * 10 => den\n        k + 1 => k\n    m * rate(cur, d) * 2 + den => x\n    return int((x - x % (2 * den)) / (2 * den))\n",
      "python": "def currencies():\n    return [[\"USD\", 2], [\"JPY\", 0], [\"GBP\", 2], [\"EUR\", 2]]\n\ndef places(cur):\n    for c in currencies():\n        if c[0] == cur:\n            return c[1]\n    return 2\n\ndef rate(cur, d):\n    if cur == \"JPY\":\n        return [6600, 6620, 6585, 6640, 6610, 6655, 6670, 6650, 6690, 6705][d - 1]\n    if cur == \"GBP\":\n        return [1260000, 1264000, 1261500, 1266000, 1270500, 1268000, 1272000, 1269500, 1275000, 1279000][d - 1]\n    if cur == \"EUR\":\n        return [1080000, 1082500, 1079000, 1084000, 1087000, 1083500, 1088000, 1090500, 1086000, 1092500][d - 1]\n    return 1000000\n\ndef expenses():\n    return [[\"flight\", \"USD\", 84200, 1, 1], [\"hotel, Tokyo, 3 nights\", \"JPY\", 96000, 2, 5], [\"dinner, Tokyo\", \"JPY\", 12800, 3, 4], [\"hotel, London, 3 nights\", \"GBP\", 54000, 5, 9], [\"taxi, London\", \"GBP\", 3850, 6, 7], [\"hotel, Paris, 2 nights\", \"EUR\", 41000, 8, 10], [\"dinner, Paris\", \"EUR\", 9640, 9, 10]]\n\ndef cents(cur, m, d):\n    den = 1\n    k = 0\n    while k < places(cur) + 4:\n        den = den * 10\n        k = k + 1\n    x = m * rate(cur, d) * 2 + den\n    return int((x - x % (2 * den)) / (2 * den))\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n5\n\n5\nchf\n5\njpy\n\n5\njpy\n0\n5\njpy\n10000000\n5\njpy\n1.5\n5\ngbp\n\n5\ngbp\n0\n5\ngbp\n100000\n5\ngbp\n12.345\n5\ngbp\nx\n5\ngbp\n20\n\n5\ngbp\n20\n0\n5\ngbp\n20\n11\n5\ngbp\n20\n4\n\n5\ngbp\n20\n4\n3\n5\ngbp\n20\n4\n11\n5\ngbp\n20\n4\nx\n5\nGBP\n20\n4\n4\n6\n",
      "screen": "== Travel expenses ==\nA trip paid in four currencies and reimbursed in dollars.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> \nCancelled.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> chf\nType USD, JPY, GBP or EUR.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> jpy\namount in yen (1 to 9999999)> \nCancelled.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> jpy\namount in yen (1 to 9999999)> 0\nType a whole number of yen from 1 to 9999999.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> jpy\namount in yen (1 to 9999999)> 10000000\nType a whole number of yen from 1 to 9999999.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> jpy\namount in yen (1 to 9999999)> 1.5\nType a whole number of yen from 1 to 9999999.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> \nCancelled.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 0\nType an amount such as 12.50, from 0.01 to 99999.99.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 100000\nType an amount such as 12.50, from 0.01 to 99999.99.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 12.345\nType an amount such as 12.50, from 0.01 to 99999.99.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> x\nType an amount such as 12.50, from 0.01 to 99999.99.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> \nCancelled.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 0\nType a whole number from 1 to 10.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 11\nType a whole number from 1 to 10.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 4\nday captured (4 to 10)> \nCancelled.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 4\nday captured (4 to 10)> 3\nType a whole number from 4 to 10.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 4\nday captured (4 to 10)> 11\nType a whole number from 4 to 10.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> gbp\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 4\nday captured (4 to 10)> x\nType a whole number from 4 to 10.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> GBP\namount (0.01 to 99999.99)> 20\nday authorized (1 to 10)> 4\nday captured (4 to 10)> 4\nQuoted at authorization, day 4: 25.32; charged at capture, day 4: 25.32.\nThe charge is the quote.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n4\n5\neur\n85.50\n3\n4\n5\nJPY\n64000\n2\n5\n5\nusd\n10\n1\n9\n6\n",
      "screen": "== Travel expenses ==\nA trip paid in four currencies and reimbursed in dollars.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 1\nThe trip, ten days, every payment by card, reimbursed in dollars:\n  item                             amount  authorized  captured\n  flight                       842.00 USD  day 1       day 1\n  hotel, Tokyo, 3 nights       96,000 JPY  day 2       day 5\n  dinner, Tokyo                12,800 JPY  day 3       day 4\n  hotel, London, 3 nights      540.00 GBP  day 5       day 9\n  taxi, London                  38.50 GBP  day 6       day 7\n  hotel, Paris, 2 nights       410.00 EUR  day 8       day 10\n  dinner, Paris                 96.40 EUR  day 9       day 10\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 2\nDollars for one unit, day by day:\n  day        JPY      GBP      EUR\n  1     0.006600   1.2600   1.0800\n  2     0.006620   1.2640   1.0825\n  3     0.006585   1.2615   1.0790\n  4     0.006640   1.2660   1.0840\n  5     0.006610   1.2705   1.0870\n  6     0.006655   1.2680   1.0835\n  7     0.006670   1.2720   1.0880\n  8     0.006650   1.2695   1.0905\n  9     0.006690   1.2750   1.0860\n  10    0.006705   1.2790   1.0925\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 3\nThe amount column added: 301,490 - cents, yen, pence and euro cents in one number.\nThe report stores the same sum as its total, so the check of the lines against the total passes, and the total is in no currency.\nBy currency: 842.00 USD, 108,800 JPY, 578.50 GBP, 506.40 EUR.\nIn dollars, each line at the rate of the day it was paid: 2,848.50.\nEvery line at the last day's rate: 2,864.65, which is 16.15 more: the value at one moment, where the first total mixes the rates of 6 days.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 4\nQuoted at authorization and charged at capture, in dollars:\n  item                        quoted   charged  difference\n  flight                      842.00    842.00        0.00\n  hotel, Tokyo, 3 nights      635.52    634.56       -0.96\n  dinner, Tokyo                84.29     84.99       +0.70\n  hotel, London, 3 nights     686.07    688.50       +2.43\n  taxi, London                 48.82     48.97       +0.15\n  hotel, Paris, 2 nights      447.11    447.93       +0.82\n  dinner, Paris               104.69    105.32       +0.63\nQuoted in all 2,848.50, charged 2,852.27: +3.77.\n5 lines were charged more than their quote and 1 less. Only those charged more get written in about, so the complaints see one side of a difference that runs both ways.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> eur\namount (0.01 to 99999.99)> 85.50\nday authorized (1 to 10)> 3\nday captured (3 to 10)> 4\nQuoted at authorization, day 3: 92.25; charged at capture, day 4: 92.68.\n+0.43: charged more than the quote the traveller agreed to.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> JPY\namount in yen (1 to 9999999)> 64000\nday authorized (1 to 10)> 2\nday captured (2 to 10)> 5\nQuoted at authorization, day 2: 423.68; charged at capture, day 5: 423.04.\n-0.64: charged less than the quote, and nobody writes in about that.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 5\ncurrency (USD, JPY, GBP or EUR)> usd\namount (0.01 to 99999.99)> 10\nday authorized (1 to 10)> 1\nday captured (1 to 10)> 9\nQuoted at authorization, day 1: 10.00; charged at capture, day 9: 10.00.\nThe charge is the quote.\n\n1) the expenses  2) the rates  3) totals  4) quote and charge  5) an expense of your own  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "the-total-was-checked-and-the-currencies-were-mixed",
      "caseId": "713-the-total-was-checked-and-the-currencies-were-mixed",
      "title": "The total was checked and the currencies were mixed"
    },
    {
      "slug": "the-amounts-were-summed-at-each-days-rate",
      "caseId": "807-the-amounts-were-summed-at-each-days-rate",
      "title": "The amounts were summed at each days rate"
    },
    {
      "slug": "the-exchange-rate-was-right-and-its-timestamp-was-not",
      "caseId": "569-the-exchange-rate-was-right-and-its-timestamp-was-not",
      "title": "The exchange rate was right and its timestamp was not"
    }
  ],
  "updated": "2026-10-19"
}
