{
  "id": "P132",
  "slug": "fair-share",
  "key": "P132-fair-share",
  "title": "Fair share",
  "summary": "One worker and one-slot jobs from a heavy tenant and light ones, first come first served or tenants in turn: the light tenants' worst wait changes a lot, the mean per job not at all.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P132 - Fair share\n\nOne worker, jobs of one slot each, from one heavy tenant and several light\nones, run first come first served or taking the tenants in turn.\n\n- `main.eml` - the menu, the tenants, the comparison slot by slot and per\n  tenant, and the settings\n- `fair.eml` - the jobs, the two schedules, and the counts per tenant and\n  per job\n\nHow each part works:\n\n- Tenant H sends its jobs at slot 0; each light tenant sends one at a\n  later slot. The worker runs one job a slot and is never idle while a\n  job waits. In turn, it takes the next waiting job of the next tenant\n  that has one.\n- As in the corpus case `fair-share-vs-fifo`, first come is fair to jobs\n  and in turn is fair to tenants. With 20 jobs from H and one each from a\n  to e at slot 1, the light tenants' worst completion is 25 first come and\n  6 in turn; H's last job moves from 20 to 25.\n- Per job the two are the same: the worker is busy in the same slots\n  either way, so the mean completion and the mean wait per job do not\n  move at all. Taking turns never makes a light tenant's worst completion\n  later.\n\nWhat is checked: menu choices 1 to 4; 1 to 30 jobs for H, 1 to 8 light\ntenants, and a slot from 0 to 30 for their jobs. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the tenants and the comparison;\n- the light jobs at slot 25, after H's are done: the two run alike, the\n  worker idle in between;\n- 6 jobs from H and 3 light tenants at slot 0.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- settings: a cancel, 0, 31 and x for H, then 5 with a cancel and 9\n  light tenants, and 5 and 2 with a cancel and slot 31;\n- the most of everything: 30 jobs from H and 8 light tenants at slot 0.\n\nBuilt on the verified corpus case `fair-share-vs-fifo` (the same jobs in\ntwo orders, fair to requests or fair to tenants).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P132 fair share: one heavy tenant and several light ones through first\n# come first served and through taking tenants in turn, slot by slot and\n# per tenant.\nimport fair\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 letter(t):\n    \"Habcdefgh\" => ls\n    return ls[t]\n\ndef tenths(p, q):\n    # p / q to one decimal, rounded half up\n    int((20 * p + q) / (2 * q)) => t\n    while t * 2 * q > 20 * p + q:\n        t - 1 => t\n    while (t + 1) * 2 * q <= 20 * p + q:\n        t + 1 => t\n    return str(int(t / 10)) + \".\" + str(t % 10)\n\ndef show(heavy, light, late):\n    (\"Tenant H sends \" + str(heavy) + \" jobs at slot 0; tenants a to \" + letter(light) + \" send one each at slot \" + str(late) + \". Every job takes one slot.\") ^0\n\ndef compare(heavy, light, late):\n    fair.jobs(heavy, light, late) => js\n    light + 1 => tenants\n    \"Slot by slot (H the heavy tenant, a letter each light one):\" ^0\n    [] => orders\n    for plan in [\"fifo\", \"fair\"]:\n        fair.schedule(js, tenants, plan) => o\n        orders + [o] => orders\n        \"\" => line\n        for e in o:\n            line + letter(js[e[0]][0]) => line\n        \"first come\" => label\n        if plan == \"fair\":\n            \"in turn\" => label\n        (\"  \" + pad(label, 11) + line) ^0\n    \"  tenant   jobs   first come: worst  mean   in turn: worst  mean\" ^0\n    fair.per_tenant(js, tenants, orders[0]) => a\n    fair.per_tenant(js, tenants, orders[1]) => b\n    0 => worst_a\n    0 => worst_b\n    for t in [0:tenants - 1]:\n        (\"  \" + pad(letter(t), 7) + lpad(str(a[t][0]), 6) + lpad(str(a[t][1]), 19) + lpad(tenths(a[t][2], a[t][0]), 6) + lpad(str(b[t][1]), 16) + lpad(tenths(b[t][2], b[t][0]), 6)) ^0\n        if t > 0:\n            if a[t][1] > worst_a:\n                a[t][1] => worst_a\n            if b[t][1] > worst_b:\n                b[t][1] => worst_b\n    fair.per_job(js, orders[0]) => ja\n    fair.per_job(js, orders[1]) => jb\n    (\"Worst completion among the light tenants: \" + str(worst_a) + \" first come, \" + str(worst_b) + \" in turn; tenant H's: \" + str(a[0][1]) + \" and \" + str(b[0][1]) + \".\") ^0\n    (\"Mean completion per job: \" + tenths(ja[0], len(js)) + \" first come, \" + tenths(jb[0], len(js)) + \" in turn; mean wait per job: \" + tenths(ja[1], len(js)) + \" and \" + tenths(jb[1], len(js)) + \".\") ^0\n    if ja[0] == jb[0]:\n        \"Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.\" ^0\n\ndef ask_int(prompt, low, high):\n    plain(input(prompt)) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return 0 - 1\n    if len(t) > 2:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return 0 - 1\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n            return 0 - 1\n    if int(t) < low or int(t) > high:\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n        return 0 - 1\n    return int(t)\n\ndef settings(heavy, light, late):\n    ask_int(\"tenant H's jobs (1 to 30)> \", 1, 30) => h\n    if h < 0:\n        return [heavy, light, late]\n    ask_int(\"light tenants (1 to 8)> \", 1, 8) => l\n    if l < 0:\n        return [heavy, light, late]\n    ask_int(\"slot the light tenants send at (0 to 30)> \", 0, 30) => s\n    if s < 0:\n        return [heavy, light, late]\n    show(h, l, s)\n    return [h, l, s]\n\n20 => heavy\n5 => light\n1 => late\n\n\"== Fair share ==\" ^0\n\"One worker, jobs of one slot each, from one heavy tenant and several light ones.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the tenants  2) compare  3) settings  4) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show(heavy, light, late)\n    elif choice == \"2\":\n        compare(heavy, light, late)\n    elif choice == \"3\":\n        settings(heavy, light, late) => s\n        s[0] => heavy\n        s[1] => light\n        s[2] => late\n    elif choice == \"4\":\n        False => running\n    else:\n        \"Pick a number from 1 to 4.\" ^0\n\"Bye.\" ^0\n",
      "python": "import fair\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 letter(t):\n    ls = \"Habcdefgh\"\n    return ls[t]\n\ndef tenths(p, q):\n    t = int((20 * p + q) / (2 * q))\n    while t * 2 * q > 20 * p + q:\n        t = t - 1\n    while (t + 1) * 2 * q <= 20 * p + q:\n        t = t + 1\n    return str(int(t / 10)) + \".\" + str(t % 10)\n\ndef show(heavy, light, late):\n    print(\"Tenant H sends \" + str(heavy) + \" jobs at slot 0; tenants a to \" + letter(light) + \" send one each at slot \" + str(late) + \". Every job takes one slot.\")\n\ndef compare(heavy, light, late):\n    js = fair.jobs(heavy, light, late)\n    tenants = light + 1\n    print(\"Slot by slot (H the heavy tenant, a letter each light one):\")\n    orders = []\n    for plan in [\"fifo\", \"fair\"]:\n        o = fair.schedule(js, tenants, plan)\n        orders = orders + [o]\n        line = \"\"\n        for e in o:\n            line = line + letter(js[e[0]][0])\n        label = \"first come\"\n        if plan == \"fair\":\n            label = \"in turn\"\n        print(\"  \" + pad(label, 11) + line)\n    print(\"  tenant   jobs   first come: worst  mean   in turn: worst  mean\")\n    a = fair.per_tenant(js, tenants, orders[0])\n    b = fair.per_tenant(js, tenants, orders[1])\n    worst_a = 0\n    worst_b = 0\n    for t in range(0, tenants):\n        print(\"  \" + pad(letter(t), 7) + lpad(str(a[t][0]), 6) + lpad(str(a[t][1]), 19) + lpad(tenths(a[t][2], a[t][0]), 6) + lpad(str(b[t][1]), 16) + lpad(tenths(b[t][2], b[t][0]), 6))\n        if t > 0:\n            if a[t][1] > worst_a:\n                worst_a = a[t][1]\n            if b[t][1] > worst_b:\n                worst_b = b[t][1]\n    ja = fair.per_job(js, orders[0])\n    jb = fair.per_job(js, orders[1])\n    print(\"Worst completion among the light tenants: \" + str(worst_a) + \" first come, \" + str(worst_b) + \" in turn; tenant H's: \" + str(a[0][1]) + \" and \" + str(b[0][1]) + \".\")\n    print(\"Mean completion per job: \" + tenths(ja[0], len(js)) + \" first come, \" + tenths(jb[0], len(js)) + \" in turn; mean wait per job: \" + tenths(ja[1], len(js)) + \" and \" + tenths(jb[1], len(js)) + \".\")\n    if ja[0] == jb[0]:\n        print(\"Per job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.\")\n\ndef ask_int(prompt, low, high):\n    t = plain(input(prompt))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return 0 - 1\n    if len(t) > 2:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return 0 - 1\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n            return 0 - 1\n    if int(t) < low or int(t) > high:\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n        return 0 - 1\n    return int(t)\n\ndef settings(heavy, light, late):\n    h = ask_int(\"tenant H's jobs (1 to 30)> \", 1, 30)\n    if h < 0:\n        return [heavy, light, late]\n    l = ask_int(\"light tenants (1 to 8)> \", 1, 8)\n    if l < 0:\n        return [heavy, light, late]\n    s = ask_int(\"slot the light tenants send at (0 to 30)> \", 0, 30)\n    if s < 0:\n        return [heavy, light, late]\n    show(h, l, s)\n    return [h, l, s]\n\nheavy = 20\nlight = 5\nlate = 1\nprint(\"== Fair share ==\")\nprint(\"One worker, jobs of one slot each, from one heavy tenant and several light ones.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the tenants  2) compare  3) settings  4) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show(heavy, light, late)\n    elif choice == \"2\":\n        compare(heavy, light, late)\n    elif choice == \"3\":\n        s = settings(heavy, light, late)\n        heavy = s[0]\n        light = s[1]\n        late = s[2]\n    elif choice == \"4\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 4.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "fair.eml",
      "eml": "# P132 fair share - one worker, jobs of one slot each from several tenants.\n# As in the corpus case fair-share-vs-fifo, first come first served is fair\n# to jobs, and taking the tenants in turn is fair to tenants. Each job is\n# [tenant, arrival slot]; the worker runs one job a slot, never idle while a\n# job is waiting.\n\ndef jobs(heavy, light, late):\n    # tenant 0 sends heavy jobs at slot 0; tenants 1 to light one each at\n    # slot late\n    [] => out\n    for k in [1:heavy]:\n        out + [[0, 0]] => out\n    for t in [1:light]:\n        out + [[t, late]] => out\n    return out\n\ndef schedule(js, tenants, plan):\n    # the order the jobs run in, as [job index, start slot]\n    [] => done\n    for j in js:\n        done + [False] => done\n    [] => out\n    0 => slot\n    0 => turn\n    while len(out) < len(js):\n        0 - 1 => pick\n        if plan == \"fifo\":\n            for i in [0:len(js) - 1]:\n                if pick < 0 and not done[i] and js[i][1] <= slot:\n                    i => pick\n        else:\n            for step in [0:tenants - 1]:\n                (turn + step) % tenants => t\n                if pick < 0:\n                    for i in [0:len(js) - 1]:\n                        if pick < 0 and not done[i] and js[i][0] == t and js[i][1] <= slot:\n                            i => pick\n                    if pick >= 0:\n                        (t + 1) % tenants => turn\n        if pick >= 0:\n            True => done[pick]\n            out + [[pick, slot]] => out\n        slot + 1 => slot\n    return out\n\ndef per_tenant(js, tenants, order):\n    # per tenant [jobs, worst completion, total completion]; completion is the\n    # slot after the job's slot\n    [] => out\n    for t in [0:tenants - 1]:\n        out + [[0, 0, 0]] => out\n    for e in order:\n        js[e[0]][0] => t\n        e[1] + 1 => c\n        out[t][0] + 1 => out[t][0]\n        if c > out[t][1]:\n            c => out[t][1]\n        out[t][2] + c => out[t][2]\n    return out\n\ndef per_job(js, order):\n    # [total completion, total wait] over all jobs\n    0 => c\n    0 => w\n    for e in order:\n        c + e[1] + 1 => c\n        w + e[1] - js[e[0]][1] => w\n    return [c, w]\n",
      "python": "def jobs(heavy, light, late):\n    out = []\n    for k in range(1, heavy+1):\n        out = out + [[0, 0]]\n    for t in range(1, light+1):\n        out = out + [[t, late]]\n    return out\n\ndef schedule(js, tenants, plan):\n    done = []\n    for j in js:\n        done = done + [False]\n    out = []\n    slot = 0\n    turn = 0\n    while len(out) < len(js):\n        pick = 0 - 1\n        if plan == \"fifo\":\n            for i in range(0, len(js)):\n                if pick < 0 and not done[i] and js[i][1] <= slot:\n                    pick = i\n        else:\n            for step in range(0, tenants):\n                t = (turn + step) % tenants\n                if pick < 0:\n                    for i in range(0, len(js)):\n                        if pick < 0 and not done[i] and js[i][0] == t and js[i][1] <= slot:\n                            pick = i\n                    if pick >= 0:\n                        turn = (t + 1) % tenants\n        if pick >= 0:\n            done[pick] = True\n            out = out + [[pick, slot]]\n        slot = slot + 1\n    return out\n\ndef per_tenant(js, tenants, order):\n    out = []\n    for t in range(0, tenants):\n        out = out + [[0, 0, 0]]\n    for e in order:\n        t = js[e[0]][0]\n        c = e[1] + 1\n        out[t][0] = out[t][0] + 1\n        if c > out[t][1]:\n            out[t][1] = c\n        out[t][2] = out[t][2] + c\n    return out\n\ndef per_job(js, order):\n    c = 0\n    w = 0\n    for e in order:\n        c = c + e[1] + 1\n        w = w + e[1] - js[e[0]][1]\n    return [c, w]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n3\n\n3\n0\n3\n31\n3\nx\n3\n5\n\n3\n5\n9\n3\n5\n2\n\n3\n5\n2\n31\n3\n30\n8\n0\n2\n4\n",
      "screen": "== Fair share ==\nOne worker, jobs of one slot each, from one heavy tenant and several light ones.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 0\nPick a number from 1 to 4.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> x\nPick a number from 1 to 4.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> \nCancelled.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 0\nType a whole number from 1 to 30.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 31\nType a whole number from 1 to 30.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> x\nType a whole number from 1 to 30.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 5\nlight tenants (1 to 8)> \nCancelled.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 5\nlight tenants (1 to 8)> 9\nType a whole number from 1 to 8.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 5\nlight tenants (1 to 8)> 2\nslot the light tenants send at (0 to 30)> \nCancelled.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 5\nlight tenants (1 to 8)> 2\nslot the light tenants send at (0 to 30)> 31\nType a whole number from 0 to 30.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 30\nlight tenants (1 to 8)> 8\nslot the light tenants send at (0 to 30)> 0\nTenant H sends 30 jobs at slot 0; tenants a to h send one each at slot 0. Every job takes one slot.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 2\nSlot by slot (H the heavy tenant, a letter each light one):\n  first come HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHabcdefgh\n  in turn    HabcdefghHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\n  tenant   jobs   first come: worst  mean   in turn: worst  mean\n  H          30                 30  15.5              38  23.2\n  a           1                 31  31.0               2   2.0\n  b           1                 32  32.0               3   3.0\n  c           1                 33  33.0               4   4.0\n  d           1                 34  34.0               5   5.0\n  e           1                 35  35.0               6   6.0\n  f           1                 36  36.0               7   7.0\n  g           1                 37  37.0               8   8.0\n  h           1                 38  38.0               9   9.0\nWorst completion among the light tenants: 38 first come, 9 in turn; tenant H's: 30 and 38.\nMean completion per job: 19.5 first come, 19.5 in turn; mean wait per job: 18.5 and 18.5.\nPer job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n20\n5\n25\n2\n3\n6\n3\n0\n2\n4\n",
      "screen": "== Fair share ==\nOne worker, jobs of one slot each, from one heavy tenant and several light ones.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 1\nTenant H sends 20 jobs at slot 0; tenants a to e send one each at slot 1. Every job takes one slot.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 2\nSlot by slot (H the heavy tenant, a letter each light one):\n  first come HHHHHHHHHHHHHHHHHHHHabcde\n  in turn    HabcdeHHHHHHHHHHHHHHHHHHH\n  tenant   jobs   first come: worst  mean   in turn: worst  mean\n  H          20                 20  10.5              25  15.3\n  a           1                 21  21.0               2   2.0\n  b           1                 22  22.0               3   3.0\n  c           1                 23  23.0               4   4.0\n  d           1                 24  24.0               5   5.0\n  e           1                 25  25.0               6   6.0\nWorst completion among the light tenants: 25 first come, 6 in turn; tenant H's: 20 and 25.\nMean completion per job: 13.0 first come, 13.0 in turn; mean wait per job: 11.8 and 11.8.\nPer job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 20\nlight tenants (1 to 8)> 5\nslot the light tenants send at (0 to 30)> 25\nTenant H sends 20 jobs at slot 0; tenants a to e send one each at slot 25. Every job takes one slot.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 2\nSlot by slot (H the heavy tenant, a letter each light one):\n  first come HHHHHHHHHHHHHHHHHHHHabcde\n  in turn    HHHHHHHHHHHHHHHHHHHHabcde\n  tenant   jobs   first come: worst  mean   in turn: worst  mean\n  H          20                 20  10.5              20  10.5\n  a           1                 26  26.0              26  26.0\n  b           1                 27  27.0              27  27.0\n  c           1                 28  28.0              28  28.0\n  d           1                 29  29.0              29  29.0\n  e           1                 30  30.0              30  30.0\nWorst completion among the light tenants: 30 first come, 30 in turn; tenant H's: 20 and 20.\nMean completion per job: 14.0 first come, 14.0 in turn; mean wait per job: 8.0 and 8.0.\nPer job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 3\ntenant H's jobs (1 to 30)> 6\nlight tenants (1 to 8)> 3\nslot the light tenants send at (0 to 30)> 0\nTenant H sends 6 jobs at slot 0; tenants a to c send one each at slot 0. Every job takes one slot.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 2\nSlot by slot (H the heavy tenant, a letter each light one):\n  first come HHHHHHabc\n  in turn    HabcHHHHH\n  tenant   jobs   first come: worst  mean   in turn: worst  mean\n  H           6                  6   3.5               9   6.0\n  a           1                  7   7.0               2   2.0\n  b           1                  8   8.0               3   3.0\n  c           1                  9   9.0               4   4.0\nWorst completion among the light tenants: 9 first come, 4 in turn; tenant H's: 6 and 9.\nMean completion per job: 5.0 first come, 5.0 in turn; mean wait per job: 4.0 and 4.0.\nPer job the two are the same: the worker is busy in the same slots either way, and only who goes first changes.\n\n1) the tenants  2) compare  3) settings  4) quit\nchoice> 4\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "fair-share-vs-fifo",
      "caseId": "278-fair-share-vs-fifo",
      "title": "Fair share vs FIFO — a queue discipline picks who it is fair to"
    }
  ],
  "updated": "2026-10-17"
}
