{
  "id": "P161",
  "slug": "subscription-billing",
  "key": "P161-subscription-billing",
  "title": "Subscription billing",
  "summary": "An API plan whose quota resets on the 1st, invoiced over a cycle that starts on the billing day: a customer who never ran out of quota is billed for overage, and a steady use is over the quota in the 31-day months only.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P161 - Subscription billing\n\nAn API plan whose quota resets on the 1st, invoiced over a cycle that\nstarts on the billing day, and two customers through 2027.\n\n- `main.eml` - the menu, the plan, the quota months, the invoices, the\n  billing day, and a steady use of your own\n- `billing.eml` - the quota and its price, the calendar of 2027, the\n  customers' calls day by day, the billing cycles, and what is over\n\nHow each part works:\n\n- The quota is 5,000,000 calls a calendar month, reset on the 1st; calls\n  over it cost 0.40 for each 1,000 or part of 1,000. The invoice covers a\n  cycle from the billing day to the day before it a month later.\n- As in the corpus case `the-quota-renewed-monthly-and-the-billing-cycle-did-not`,\n  the two windows are both a month long and not the same month. The\n  month-end customer runs jobs over the last 10 days of every other month\n  and the first 14 of the next: no calendar month reaches the quota, and\n  with the billing day on the 18th six of the eleven invoices are over it,\n  28,400,000 calls in all, because a cycle that holds the 1st holds two\n  quota months and the invoice allows one. With the billing day on the 1st\n  the cycles are the quota months and nothing is billed.\n- As in `the-target-was-per-calendar-month-and-the-months-were-unequal`, a\n  monthly number is 28 to 31 days. The steady customer makes 165,000 calls\n  every day: 4,620,000 in February, 5,115,000 in each 31-day month, over\n  the quota in 7 of 12 months at the same use every day. A steady use stays\n  under the quota every month up to 161,290 a day and is over it every\n  month from 178,572.\n\nWhat is checked: menu choices 1 to 6; a billing day from 1 to 28; a steady\nuse of 1 to 999999 calls a day. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the plan, the quota months and the invoices with the billing day on the\n  18th;\n- the billing day moved to the 1st, and the invoices again;\n- steady uses of 165,000 and 170,000 calls a day.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- billing days: a cancel, 0, 29 and x;\n- steady uses: a cancel, 0, 1000000 and x;\n- steady uses of 161,290 and 161,291 calls a day, either side of the\n  31-day line.\n\nBuilt on the verified corpus cases `the-quota-renewed-monthly-and-the-billing-cycle-did-not`\n(a quota reset on the 1st and a cycle from the 18th: two grants inside one\ninvoice) and `the-target-was-per-calendar-month-and-the-months-were-unequal`\n(a monthly target met in July and missed in February at the same rate).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P161 subscription billing: an API plan whose quota resets on the 1st,\n# invoiced over a cycle that starts on the billing day - the quota months,\n# the invoices, a billing day of your own and a steady use of your own.\nimport billing\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 rstrip(s):\n    len(s) => j\n    while j > 0 and s[j - 1] == \" \":\n        j - 1 => j\n    return s[0:j]\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 money(cents):\n    cents % 100 => c\n    if c < 10:\n        return commas(int((cents - c) / 100)) + \".0\" + str(c)\n    return commas(int((cents - c) / 100)) + \".\" + str(c)\n\ndef ordinal(n):\n    if n % 10 == 1 and n != 11:\n        return str(n) + \"st\"\n    if n % 10 == 2 and n != 12:\n        return str(n) + \"nd\"\n    if n % 10 == 3 and n != 13:\n        return str(n) + \"rd\"\n    return str(n) + \"th\"\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 label(cy):\n    return billing.month_name(cy[0]) + \" \" + str(cy[1]) + \" - \" + billing.month_name(cy[2]) + \" \" + str(cy[3])\n\ndef show_plan(b):\n    (\"The plan: \" + commas(billing.quota()) + \" calls a calendar month; the count resets at midnight on the 1st, and every response says how many are left.\") ^0\n    \"Calls over the quota cost 0.40 for each 1,000 or part of 1,000.\" ^0\n    (\"The invoice covers a cycle from the billing day to the day before it a month later. The billing day is the \" + ordinal(b) + \".\") ^0\n    \"The customers, through 2027:\" ^0\n    \"  steady     165,000 calls every day\" ^0\n    \"  month-end  480,000 calls a day over the last 10 days of Jan, Mar, May, Jul, Sep and Nov,\" ^0\n    \"             350,000 a day over the first 14 days of the month after, 5,000 a day otherwise\" ^0\n\ndef show_months():\n    (\"Calls in each month of 2027, against the quota of \" + commas(billing.quota()) + \" (* over it):\") ^0\n    (\"  \" + pad(\"month\", 5) + lpad(\"days\", 6) + lpad(\"steady\", 11) + \"  \" + lpad(\"month-end\", 12)) ^0\n    billing.customers() => cus\n    [0, 0] => n_over\n    [0, 0] => busiest\n    1 => m\n    while m <= 12:\n        \"  \" + pad(billing.month_name(m), 5) + lpad(str(billing.days_in(m)), 6) => line\n        0 => k\n        while k < 2:\n            billing.month_calls(cus[k], m) => n\n            \"  \" => mark\n            if n > billing.quota():\n                \" *\" => mark\n                n_over[k] + 1 => n_over[k]\n            if n > busiest[k]:\n                n => busiest[k]\n            line + lpad(commas(n), 11 + k) + mark => line\n            k + 1 => k\n        rstrip(line) ^0\n        m + 1 => m\n    (\"steady: over the quota in \" + str(n_over[0]) + \" of 12 months, all of them 31 days long, at the same 165,000 calls every day; February holds \" + commas(billing.month_calls(cus[0], 2)) + \". A monthly quota is 28 to 31 days of use.\") ^0\n    (\"month-end: over the quota in \" + str(n_over[1]) + \" of 12 months; its busiest month holds \" + commas(busiest[1]) + \". The header never showed its quota run out.\") ^0\n\ndef cell(n):\n    lpad(commas(n), 10) => out\n    if billing.over(n) > 0:\n        out + \", \" + commas(billing.over(n)) + \" over\" => out\n    return out\n\ndef tally(cu, b):\n    # [invoices over the quota, calls over, cents billed]\n    [0, 0, 0] => t\n    for cy in billing.cycles(b):\n        billing.over(billing.span(cu, cy[0], cy[1], cy[2], cy[3])) => o\n        if o > 0:\n            t[0] + 1 => t[0]\n            t[1] + o => t[1]\n            t[2] + billing.charge(o) => t[2]\n    return t\n\ndef invoiced(t, n):\n    if t[0] == 0:\n        return \"over in 0 of \" + str(n) + \" invoices, billed 0.00.\"\n    return \"over in \" + str(t[0]) + \" of \" + str(n) + \" invoices, \" + commas(t[1]) + \" calls in all, billed \" + money(t[2]) + \".\"\n\ndef show_invoices(b):\n    billing.cycles(b) => cys\n    (\"Invoices with the billing day the \" + ordinal(b) + \", for the \" + str(len(cys)) + \" cycles inside 2027, each allowing \" + commas(billing.quota()) + \":\") ^0\n    (\"  \" + pad(\"cycle\", 17) + lpad(\"days\", 4) + \"  \" + pad(\"steady\", 30) + \"month-end\") ^0\n    billing.customers() => cus\n    for cy in cys:\n        billing.span(cus[0], cy[0], cy[1], cy[2], cy[3]) => s\n        billing.span(cus[1], cy[0], cy[1], cy[2], cy[3]) => e\n        (\"  \" + pad(label(cy), 17) + lpad(str(billing.cycle_days(cy)), 4) + \"  \" + pad(cell(s), 30) + cell(e)) ^0\n    tally(cus[1], b) => te\n    (\"steady: \" + invoiced(tally(cus[0], b), len(cys))) ^0\n    (\"month-end: \" + invoiced(te, len(cys))) ^0\n    if te[0] > 0:\n        \"The month-end customer never ran out of quota: a cycle that holds the 1st holds two quota months, and the invoice allows one.\" ^0\n    else:\n        \"With the billing day on the 1st each cycle is a quota month, and the invoices bill what the quota counted.\" ^0\n\ndef set_day(b):\n    plain(input(\"billing day (1 to 28)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return b\n    if len(t) > 2 or not digits(t) or int(t) < 1 or int(t) > 28:\n        \"Type a whole number from 1 to 28.\" ^0\n        return b\n    (\"The billing day is now the \" + ordinal(int(t)) + \".\") ^0\n    return int(t)\n\ndef own(b):\n    plain(input(\"calls a day (1 to 999999)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    if len(t) > 6 or not digits(t) or int(t) < 1:\n        \"Type a whole number from 1 to 999999.\" ^0\n        return\n    int(t) => r\n    [\"yours\", r] => cu\n    (\"At \" + commas(r) + \" calls a day: \" + commas(28 * r) + \" in a 28-day month, \" + commas(30 * r) + \" in 30 days, \" + commas(31 * r) + \" in 31.\") ^0\n    0 => n\n    1 => m\n    while m <= 12:\n        if billing.month_calls(cu, m) > billing.quota():\n            n + 1 => n\n        m + 1 => m\n    tally(cu, b) => tt\n    (\"Over the quota in \" + str(n) + \" of 12 months of 2027. With the billing day the \" + ordinal(b) + \", \" + invoiced(tt, len(billing.cycles(b)))) ^0\n    \"Under the quota every month up to 161,290 a day; over it every month from 178,572 a day.\" ^0\n\n\"== Subscription billing ==\" ^0\n\"An API plan whose quota resets on the 1st, invoiced over a cycle that starts on the billing day.\" ^0\n18 => day\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_plan(day)\n    elif choice == \"2\":\n        show_months()\n    elif choice == \"3\":\n        show_invoices(day)\n    elif choice == \"4\":\n        set_day(day) => day\n    elif choice == \"5\":\n        own(day)\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import billing\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 rstrip(s):\n    j = len(s)\n    while j > 0 and s[j - 1] == \" \":\n        j = j - 1\n    return s[0:j]\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 money(cents):\n    c = cents % 100\n    if c < 10:\n        return commas(int((cents - c) / 100)) + \".0\" + str(c)\n    return commas(int((cents - c) / 100)) + \".\" + str(c)\n\ndef ordinal(n):\n    if n % 10 == 1 and n != 11:\n        return str(n) + \"st\"\n    if n % 10 == 2 and n != 12:\n        return str(n) + \"nd\"\n    if n % 10 == 3 and n != 13:\n        return str(n) + \"rd\"\n    return str(n) + \"th\"\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 label(cy):\n    return billing.month_name(cy[0]) + \" \" + str(cy[1]) + \" - \" + billing.month_name(cy[2]) + \" \" + str(cy[3])\n\ndef show_plan(b):\n    print(\"The plan: \" + commas(billing.quota()) + \" calls a calendar month; the count resets at midnight on the 1st, and every response says how many are left.\")\n    print(\"Calls over the quota cost 0.40 for each 1,000 or part of 1,000.\")\n    print(\"The invoice covers a cycle from the billing day to the day before it a month later. The billing day is the \" + ordinal(b) + \".\")\n    print(\"The customers, through 2027:\")\n    print(\"  steady     165,000 calls every day\")\n    print(\"  month-end  480,000 calls a day over the last 10 days of Jan, Mar, May, Jul, Sep and Nov,\")\n    print(\"             350,000 a day over the first 14 days of the month after, 5,000 a day otherwise\")\n\ndef show_months():\n    print(\"Calls in each month of 2027, against the quota of \" + commas(billing.quota()) + \" (* over it):\")\n    print(\"  \" + pad(\"month\", 5) + lpad(\"days\", 6) + lpad(\"steady\", 11) + \"  \" + lpad(\"month-end\", 12))\n    cus = billing.customers()\n    n_over = [0, 0]\n    busiest = [0, 0]\n    m = 1\n    while m <= 12:\n        line = \"  \" + pad(billing.month_name(m), 5) + lpad(str(billing.days_in(m)), 6)\n        k = 0\n        while k < 2:\n            n = billing.month_calls(cus[k], m)\n            mark = \"  \"\n            if n > billing.quota():\n                mark = \" *\"\n                n_over[k] = n_over[k] + 1\n            if n > busiest[k]:\n                busiest[k] = n\n            line = line + lpad(commas(n), 11 + k) + mark\n            k = k + 1\n        print(rstrip(line))\n        m = m + 1\n    print(\"steady: over the quota in \" + str(n_over[0]) + \" of 12 months, all of them 31 days long, at the same 165,000 calls every day; February holds \" + commas(billing.month_calls(cus[0], 2)) + \". A monthly quota is 28 to 31 days of use.\")\n    print(\"month-end: over the quota in \" + str(n_over[1]) + \" of 12 months; its busiest month holds \" + commas(busiest[1]) + \". The header never showed its quota run out.\")\n\ndef cell(n):\n    out = lpad(commas(n), 10)\n    if billing.over(n) > 0:\n        out = out + \", \" + commas(billing.over(n)) + \" over\"\n    return out\n\ndef tally(cu, b):\n    t = [0, 0, 0]\n    for cy in billing.cycles(b):\n        o = billing.over(billing.span(cu, cy[0], cy[1], cy[2], cy[3]))\n        if o > 0:\n            t[0] = t[0] + 1\n            t[1] = t[1] + o\n            t[2] = t[2] + billing.charge(o)\n    return t\n\ndef invoiced(t, n):\n    if t[0] == 0:\n        return \"over in 0 of \" + str(n) + \" invoices, billed 0.00.\"\n    return \"over in \" + str(t[0]) + \" of \" + str(n) + \" invoices, \" + commas(t[1]) + \" calls in all, billed \" + money(t[2]) + \".\"\n\ndef show_invoices(b):\n    cys = billing.cycles(b)\n    print(\"Invoices with the billing day the \" + ordinal(b) + \", for the \" + str(len(cys)) + \" cycles inside 2027, each allowing \" + commas(billing.quota()) + \":\")\n    print(\"  \" + pad(\"cycle\", 17) + lpad(\"days\", 4) + \"  \" + pad(\"steady\", 30) + \"month-end\")\n    cus = billing.customers()\n    for cy in cys:\n        s = billing.span(cus[0], cy[0], cy[1], cy[2], cy[3])\n        e = billing.span(cus[1], cy[0], cy[1], cy[2], cy[3])\n        print(\"  \" + pad(label(cy), 17) + lpad(str(billing.cycle_days(cy)), 4) + \"  \" + pad(cell(s), 30) + cell(e))\n    te = tally(cus[1], b)\n    print(\"steady: \" + invoiced(tally(cus[0], b), len(cys)))\n    print(\"month-end: \" + invoiced(te, len(cys)))\n    if te[0] > 0:\n        print(\"The month-end customer never ran out of quota: a cycle that holds the 1st holds two quota months, and the invoice allows one.\")\n    else:\n        print(\"With the billing day on the 1st each cycle is a quota month, and the invoices bill what the quota counted.\")\n\ndef set_day(b):\n    t = plain(input(\"billing day (1 to 28)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return b\n    if len(t) > 2 or not digits(t) or int(t) < 1 or int(t) > 28:\n        print(\"Type a whole number from 1 to 28.\")\n        return b\n    print(\"The billing day is now the \" + ordinal(int(t)) + \".\")\n    return int(t)\n\ndef own(b):\n    t = plain(input(\"calls a day (1 to 999999)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    if len(t) > 6 or not digits(t) or int(t) < 1:\n        print(\"Type a whole number from 1 to 999999.\")\n        return\n    r = int(t)\n    cu = [\"yours\", r]\n    print(\"At \" + commas(r) + \" calls a day: \" + commas(28 * r) + \" in a 28-day month, \" + commas(30 * r) + \" in 30 days, \" + commas(31 * r) + \" in 31.\")\n    n = 0\n    m = 1\n    while m <= 12:\n        if billing.month_calls(cu, m) > billing.quota():\n            n = n + 1\n        m = m + 1\n    tt = tally(cu, b)\n    print(\"Over the quota in \" + str(n) + \" of 12 months of 2027. With the billing day the \" + ordinal(b) + \", \" + invoiced(tt, len(billing.cycles(b))))\n    print(\"Under the quota every month up to 161,290 a day; over it every month from 178,572 a day.\")\n\nprint(\"== Subscription billing ==\")\nprint(\"An API plan whose quota resets on the 1st, invoiced over a cycle that starts on the billing day.\")\nday = 18\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_plan(day)\n    elif choice == \"2\":\n        show_months()\n    elif choice == \"3\":\n        show_invoices(day)\n    elif choice == \"4\":\n        day = set_day(day)\n    elif choice == \"5\":\n        own(day)\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "billing.eml",
      "eml": "# P161 subscription billing - the plan, the customers and the calendar of\n# 2027. As in the corpus case the-quota-renewed-monthly-and-the-billing-cycle-\n# did-not, the quota is counted per calendar month and resets on the 1st,\n# while the invoice is computed over a billing cycle that starts on the\n# billing day: two windows a month long that are not the same month. And as\n# in the-target-was-per-calendar-month-and-the-months-were-unequal, a monthly\n# number is a different number of days each month, 28 to 31.\n\ndef quota():\n    return 5000000\n\ndef price_cents():\n    # for each 1,000 calls over the quota, or part of 1,000\n    return 40\n\ndef month_name(m):\n    return [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"][m - 1]\n\ndef days_in(m):\n    # 2027 is not a leap year; m is 1 to 12\n    return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1]\n\ndef customers():\n    # [name, calls a day]; the month-end customer's calls follow its jobs\n    return [[\"steady\", 165000], [\"month-end\", 0]]\n\ndef calls(cu, m, d):\n    # the calls customer cu makes on day d of month m\n    if cu[0] != \"month-end\":\n        return cu[1]\n    if m % 2 == 1 and d > days_in(m) - 10:\n        return 480000\n    if m % 2 == 0 and d <= 14:\n        return 350000\n    return 5000\n\ndef span(cu, m1, d1, m2, d2):\n    # calls from day d1 of month m1 to day d2 of month m2, both included\n    0 => t\n    m1 => m\n    d1 => d\n    while m < m2 or (m == m2 and d <= d2):\n        t + calls(cu, m, d) => t\n        if d == days_in(m):\n            m + 1 => m\n            1 => d\n        else:\n            d + 1 => d\n    return t\n\ndef month_calls(cu, m):\n    return span(cu, m, 1, m, days_in(m))\n\ndef cycles(b):\n    # the billing cycles inside 2027 for billing day b, as [m1, d1, m2, d2]:\n    # each runs from the billing day to the day before it a month later\n    [] => out\n    1 => m\n    while m <= 12:\n        if b == 1:\n            out + [[m, 1, m, days_in(m)]] => out\n        elif m < 12:\n            out + [[m, b, m + 1, b - 1]] => out\n        m + 1 => m\n    return out\n\ndef cycle_days(cy):\n    if cy[0] == cy[2]:\n        return cy[3] - cy[1] + 1\n    return days_in(cy[0]) - cy[1] + 1 + cy[3]\n\ndef over(n):\n    # calls over the quota in a window of n calls\n    if n > quota():\n        return n - quota()\n    return 0\n\ndef charge(n):\n    # cents for n calls over the quota: 40 for each 1,000 or part of 1,000\n    int((n - n % 1000) / 1000) => k\n    if n % 1000 > 0:\n        k + 1 => k\n    return k * price_cents()\n",
      "python": "def quota():\n    return 5000000\n\ndef price_cents():\n    return 40\n\ndef month_name(m):\n    return [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"][m - 1]\n\ndef days_in(m):\n    return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1]\n\ndef customers():\n    return [[\"steady\", 165000], [\"month-end\", 0]]\n\ndef calls(cu, m, d):\n    if cu[0] != \"month-end\":\n        return cu[1]\n    if m % 2 == 1 and d > days_in(m) - 10:\n        return 480000\n    if m % 2 == 0 and d <= 14:\n        return 350000\n    return 5000\n\ndef span(cu, m1, d1, m2, d2):\n    t = 0\n    m = m1\n    d = d1\n    while m < m2 or m == m2 and d <= d2:\n        t = t + calls(cu, m, d)\n        if d == days_in(m):\n            m = m + 1\n            d = 1\n        else:\n            d = d + 1\n    return t\n\ndef month_calls(cu, m):\n    return span(cu, m, 1, m, days_in(m))\n\ndef cycles(b):\n    out = []\n    m = 1\n    while m <= 12:\n        if b == 1:\n            out = out + [[m, 1, m, days_in(m)]]\n        elif m < 12:\n            out = out + [[m, b, m + 1, b - 1]]\n        m = m + 1\n    return out\n\ndef cycle_days(cy):\n    if cy[0] == cy[2]:\n        return cy[3] - cy[1] + 1\n    return days_in(cy[0]) - cy[1] + 1 + cy[3]\n\ndef over(n):\n    if n > quota():\n        return n - quota()\n    return 0\n\ndef charge(n):\n    k = int((n - n % 1000) / 1000)\n    if n % 1000 > 0:\n        k = k + 1\n    return k * price_cents()\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n4\n\n4\n0\n4\n29\n4\nx\n5\n\n5\n0\n5\n1000000\n5\nx\n5\n161290\n5\n161291\n6\n",
      "screen": "== Subscription billing ==\nAn API plan whose quota resets on the 1st, invoiced over a cycle that starts on the billing day.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 4\nbilling day (1 to 28)> \nCancelled.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 4\nbilling day (1 to 28)> 0\nType a whole number from 1 to 28.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 4\nbilling day (1 to 28)> 29\nType a whole number from 1 to 28.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 4\nbilling day (1 to 28)> x\nType a whole number from 1 to 28.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> \nCancelled.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> 0\nType a whole number from 1 to 999999.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> 1000000\nType a whole number from 1 to 999999.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> x\nType a whole number from 1 to 999999.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> 161290\nAt 161,290 calls a day: 4,516,120 in a 28-day month, 4,838,700 in 30 days, 4,999,990 in 31.\nOver the quota in 0 of 12 months of 2027. With the billing day the 18th, over in 0 of 11 invoices, billed 0.00.\nUnder the quota every month up to 161,290 a day; over it every month from 178,572 a day.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> 161291\nAt 161,291 calls a day: 4,516,148 in a 28-day month, 4,838,730 in 30 days, 5,000,021 in 31.\nOver the quota in 7 of 12 months of 2027. With the billing day the 18th, over in 6 of 11 invoices, 126 calls in all, billed 2.40.\nUnder the quota every month up to 161,290 a day; over it every month from 178,572 a day.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n4\n1\n3\n5\n165000\n5\n170000\n6\n",
      "screen": "== Subscription billing ==\nAn API plan whose quota resets on the 1st, invoiced over a cycle that starts on the billing day.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 1\nThe plan: 5,000,000 calls a calendar month; the count resets at midnight on the 1st, and every response says how many are left.\nCalls over the quota cost 0.40 for each 1,000 or part of 1,000.\nThe invoice covers a cycle from the billing day to the day before it a month later. The billing day is the 18th.\nThe customers, through 2027:\n  steady     165,000 calls every day\n  month-end  480,000 calls a day over the last 10 days of Jan, Mar, May, Jul, Sep and Nov,\n             350,000 a day over the first 14 days of the month after, 5,000 a day otherwise\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 2\nCalls in each month of 2027, against the quota of 5,000,000 (* over it):\n  month  days     steady     month-end\n  Jan      31  5,115,000 *   4,905,000\n  Feb      28  4,620,000     4,970,000\n  Mar      31  5,115,000 *   4,905,000\n  Apr      30  4,950,000     4,980,000\n  May      31  5,115,000 *   4,905,000\n  Jun      30  4,950,000     4,980,000\n  Jul      31  5,115,000 *   4,905,000\n  Aug      31  5,115,000 *   4,985,000\n  Sep      30  4,950,000     4,900,000\n  Oct      31  5,115,000 *   4,985,000\n  Nov      30  4,950,000     4,900,000\n  Dec      31  5,115,000 *   4,985,000\nsteady: over the quota in 7 of 12 months, all of them 31 days long, at the same 165,000 calls every day; February holds 4,620,000. A monthly quota is 28 to 31 days of use.\nmonth-end: over the quota in 0 of 12 months; its busiest month holds 4,985,000. The header never showed its quota run out.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 3\nInvoices with the billing day the 18th, for the 11 cycles inside 2027, each allowing 5,000,000:\n  cycle            days  steady                        month-end\n  Jan 18 - Feb 17    31   5,115,000, 115,000 over       9,735,000, 4,735,000 over\n  Feb 18 - Mar 17    28   4,620,000                       140,000\n  Mar 18 - Apr 17    31   5,115,000, 115,000 over       9,735,000, 4,735,000 over\n  Apr 18 - May 17    30   4,950,000                       150,000\n  May 18 - Jun 17    31   5,115,000, 115,000 over       9,735,000, 4,735,000 over\n  Jun 18 - Jul 17    30   4,950,000                       150,000\n  Jul 18 - Aug 17    31   5,115,000, 115,000 over       9,735,000, 4,735,000 over\n  Aug 18 - Sep 17    31   5,115,000, 115,000 over         155,000\n  Sep 18 - Oct 17    30   4,950,000                     9,730,000, 4,730,000 over\n  Oct 18 - Nov 17    31   5,115,000, 115,000 over         155,000\n  Nov 18 - Dec 17    30   4,950,000                     9,730,000, 4,730,000 over\nsteady: over in 6 of 11 invoices, 690,000 calls in all, billed 276.00.\nmonth-end: over in 6 of 11 invoices, 28,400,000 calls in all, billed 11,360.00.\nThe month-end customer never ran out of quota: a cycle that holds the 1st holds two quota months, and the invoice allows one.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 4\nbilling day (1 to 28)> 1\nThe billing day is now the 1st.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 3\nInvoices with the billing day the 1st, for the 12 cycles inside 2027, each allowing 5,000,000:\n  cycle            days  steady                        month-end\n  Jan 1 - Jan 31     31   5,115,000, 115,000 over       4,905,000\n  Feb 1 - Feb 28     28   4,620,000                     4,970,000\n  Mar 1 - Mar 31     31   5,115,000, 115,000 over       4,905,000\n  Apr 1 - Apr 30     30   4,950,000                     4,980,000\n  May 1 - May 31     31   5,115,000, 115,000 over       4,905,000\n  Jun 1 - Jun 30     30   4,950,000                     4,980,000\n  Jul 1 - Jul 31     31   5,115,000, 115,000 over       4,905,000\n  Aug 1 - Aug 31     31   5,115,000, 115,000 over       4,985,000\n  Sep 1 - Sep 30     30   4,950,000                     4,900,000\n  Oct 1 - Oct 31     31   5,115,000, 115,000 over       4,985,000\n  Nov 1 - Nov 30     30   4,950,000                     4,900,000\n  Dec 1 - Dec 31     31   5,115,000, 115,000 over       4,985,000\nsteady: over in 7 of 12 invoices, 805,000 calls in all, billed 322.00.\nmonth-end: over in 0 of 12 invoices, billed 0.00.\nWith the billing day on the 1st each cycle is a quota month, and the invoices bill what the quota counted.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> 165000\nAt 165,000 calls a day: 4,620,000 in a 28-day month, 4,950,000 in 30 days, 5,115,000 in 31.\nOver the quota in 7 of 12 months of 2027. With the billing day the 1st, over in 7 of 12 invoices, 805,000 calls in all, billed 322.00.\nUnder the quota every month up to 161,290 a day; over it every month from 178,572 a day.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 5\ncalls a day (1 to 999999)> 170000\nAt 170,000 calls a day: 4,760,000 in a 28-day month, 5,100,000 in 30 days, 5,270,000 in 31.\nOver the quota in 11 of 12 months of 2027. With the billing day the 1st, over in 11 of 12 invoices, 2,290,000 calls in all, billed 916.00.\nUnder the quota every month up to 161,290 a day; over it every month from 178,572 a day.\n\n1) the plan  2) quota months  3) invoices  4) billing day  5) a steady use of your own  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "the-quota-renewed-monthly-and-the-billing-cycle-did-not",
      "caseId": "665-the-quota-renewed-monthly-and-the-billing-cycle-did-not",
      "title": "The quota renewed monthly and the billing cycle did not"
    },
    {
      "slug": "the-target-was-per-calendar-month-and-the-months-were-unequal",
      "caseId": "818-the-target-was-per-calendar-month-and-the-months-were-unequal",
      "title": "The target was per calendar month and the months were unequal"
    }
  ],
  "updated": "2026-10-19"
}
