From 9dbc613ca53575bbb8999af5e76495e22bb763fc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 09:10:08 +0000 Subject: [PATCH 1/2] =?UTF-8?q?Add=20quant=20combinatorics=20bundle:=20les?= =?UTF-8?q?son=20+=207=20questions=20(easy=E2=86=92hard)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the previously-empty counting-probability/combinatorics subtopic (only a single probability question existed before): - Lesson on the fundamental counting principle, factorials, permutations vs. combinations, identical-item arrangements, and together/apart restrictions. - 7 original questions (0017-0023) with an easy->hard progression, each with a worked explanation, two hints, and distractors keyed to specific common errors (add-vs-multiply, permutation-vs-combination, forgetting to divide out repeats, at-least-one over-count, gap-method slips). All status: in-review. Rebuilt docs/data/content.json (zero warnings). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01WknFxpevhwGoMJPnoUW8bd --- ...uant-counting-probability-combinatorics.md | 88 +++++++ ...counting-probability-combinatorics-0017.md | 45 ++++ ...counting-probability-combinatorics-0018.md | 46 ++++ ...counting-probability-combinatorics-0019.md | 45 ++++ ...counting-probability-combinatorics-0020.md | 47 ++++ ...counting-probability-combinatorics-0021.md | 49 ++++ ...counting-probability-combinatorics-0022.md | 49 ++++ ...counting-probability-combinatorics-0023.md | 60 +++++ docs/data/content.json | 236 +++++++++++++++++- 9 files changed, 661 insertions(+), 4 deletions(-) create mode 100644 content/lessons/quant/quant-counting-probability-combinatorics.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0017.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0018.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0019.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0020.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0021.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0022.md create mode 100644 content/questions/quant/quant-counting-probability-combinatorics-0023.md diff --git a/content/lessons/quant/quant-counting-probability-combinatorics.md b/content/lessons/quant/quant-counting-probability-combinatorics.md new file mode 100644 index 0000000..48eda76 --- /dev/null +++ b/content/lessons/quant/quant-counting-probability-combinatorics.md @@ -0,0 +1,88 @@ +--- +id: quant-counting-probability-combinatorics +section: quant +topic: counting-probability +subtopic: combinatorics +title: "Combinatorics: Counting Without Listing" +tags: [combinatorics, counting, permutations, combinations, factorials] +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Overview + +Combinatorics is the art of counting arrangements and selections without writing them all out. +Almost every Focus counting problem reduces to two questions: **do I multiply or add?** and **does +order matter?** Get those two right and the formulas fall into place. + +## Core concepts + +**Fundamental counting principle (multiply for "and").** If one choice can be made \(m\) ways and +an independent next choice \(n\) ways, the two together can be made \(m \times n\) ways. Multiply +stages that happen *together*; **add** only mutually exclusive *cases* ("or"). + +**Factorial — arranging distinct things in a row.** \(n\) distinct items in a row: + +\[n! = n \times (n-1) \times \cdots \times 2 \times 1\] + +By convention \(0! = 1\). + +**Permutations — order matters.** Choosing and arranging \(k\) of \(n\) distinct items: + +\[P(n, k) = \frac{n!}{(n-k)!} = n(n-1)\cdots(n-k+1)\] + +**Combinations — order does not matter.** Choosing \(k\) of \(n\) with no regard to order: + +\[C(n, k) = \binom{n}{k} = \frac{n!}{k!\,(n-k)!} = \frac{P(n,k)}{k!}\] + +A combination is a permutation with the \(k!\) internal orderings divided out. "Committee," +"group," "selection," "handshake" → combination. "Ranking," "arrangement," "line," "code" → +permutation. + +**Identical items.** Arranging \(n\) items where some repeat (\(a\) alike, \(b\) alike, …): + +\[\frac{n!}{a!\,b!\,\cdots}\] + +Divide out the interchangeable duplicates. (This is why `LEVEL` has \(5!/(2!\,2!)\) arrangements, +not \(5!\).) + +**Restrictions — two standard moves.** +- *Must be together:* glue the group into one block, arrange the blocks, then multiply by the + internal arrangements of the block. +- *Must be apart (no two adjacent):* seat the unrestricted items first, then drop the restricted + ones into the **gaps** between and around them. + +## Worked examples + +**Multiply.** A menu has 4 breads, 3 fillings, 2 spreads; one of each → \(4\times 3\times 2 = 24\) +sandwiches. + +**Combination vs. permutation.** From 8 people, a *committee* of 3 is \(C(8,3) = 56\); a *ranked* +top-3 is \(P(8,3) = 336\). The ratio is \(3! = 6\), the orderings a committee ignores. + +**Together.** Six people in a row with two specific people adjacent: treat the pair as one block → +\(5!\) arrangements of the 5 units, times \(2!\) for the pair's order \(= 240\). + +## Common traps + +- **Adding when you should multiply.** Independent successive choices multiply; \(4+3+2\) is wrong + for the sandwich count. +- **Permutation vs. combination.** Using \(P(n,k)\) for an unordered selection over-counts by + \(k!\); using \(C(n,k)\) for an ordered one under-counts. +- **Forgetting to divide out identical items.** \(5!\) over-counts `LEVEL` because swapping the two + L's (or two E's) changes nothing. +- **"At least one" head-on.** Counting cases one by one invites double-counting. Use the + complement: (total) − (none) is almost always cleaner. +- **Restriction, then forget the internal order.** After gluing a block, remember to multiply by + the block's own arrangements. + +## Key takeaways + +- Decide multiply-vs-add and order-matters-vs-not *before* reaching for a formula. +- \(C(n,k) = P(n,k)/k!\) — a combination is a permutation with the orderings removed. +- Identical items: divide \(n!\) by the factorial of each repeat count. +- "At least one" → subtract the complement from the total. +- Restrictions: glue for "together," use gaps for "apart." diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0017.md b/content/questions/quant/quant-counting-probability-combinatorics-0017.md new file mode 100644 index 0000000..c73cb27 --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0017.md @@ -0,0 +1,45 @@ +--- +id: quant-counting-probability-combinatorics-0017 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: easy +tags: [combinatorics, counting-principle] +choices: + A: "8" + B: "9" + C: "12" + D: "24" + E: "48" +answer: D +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +A café builds a sandwich by choosing exactly one of 4 breads, one of 3 cheeses, and one of 2 +spreads. How many distinct sandwiches can be made? + +## Explanation + +The three choices are made **together** (one bread *and* one cheese *and* one spread), so by the +fundamental counting principle you **multiply**: + +\[4 \times 3 \times 2 = 24\] + +**Traps:** +- **B (9)** *adds* the options \((4 + 3 + 2)\) — addition is for mutually exclusive cases ("or"), + not for independent successive choices ("and"). +- **C (12)** multiplies only bread and cheese \((4 \times 3)\), forgetting the spread. +- **A (8)** multiplies only bread and spread \((4 \times 2)\), forgetting the cheese. +- **E (48)** double-counts the spread \((4 \times 3 \times 2 \times 2)\). + +## Hints + +- Each independent choice made together multiplies the count — decide multiply vs. add first. +- Include every stage: bread × cheese × spread. diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0018.md b/content/questions/quant/quant-counting-probability-combinatorics-0018.md new file mode 100644 index 0000000..7f72862 --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0018.md @@ -0,0 +1,46 @@ +--- +id: quant-counting-probability-combinatorics-0018 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: easy +tags: [combinatorics, permutations, factorial] +choices: + A: "15" + B: "20" + C: "24" + D: "25" + E: "120" +answer: E +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +In how many different ways can 5 distinct books be arranged in a row on a shelf? + +## Explanation + +Arranging \(n\) distinct items in a row is \(n!\). With 5 books: + +\[5! = 5 \times 4 \times 3 \times 2 \times 1 = 120\] + +Think of it as filling positions left to right: 5 choices for the first slot, 4 for the next, then +3, 2, 1 — multiplied together. + +**Traps:** +- **A (15)** *adds* \(5 + 4 + 3 + 2 + 1\) instead of multiplying. +- **B (20)** stops after two factors \((5 \times 4)\). +- **C (24)** is \(4!\) — arranging only 4 of the books. +- **D (25)** is \(5^2\), treating each slot as having all 5 books available (no removal after + placing one). + +## Hints + +- "Arrange all \(n\) distinct items in a row" is exactly \(n!\). +- Each book used up reduces the choices for the next slot: \(5 \times 4 \times 3 \times 2 \times 1\). diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0019.md b/content/questions/quant/quant-counting-probability-combinatorics-0019.md new file mode 100644 index 0000000..662ee95 --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0019.md @@ -0,0 +1,45 @@ +--- +id: quant-counting-probability-combinatorics-0019 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: medium +tags: [combinatorics, combinations] +choices: + A: "24" + B: "56" + C: "112" + D: "168" + E: "336" +answer: B +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +A committee of 3 people is to be selected from a group of 8. How many different committees are +possible? + +## Explanation + +A committee is an **unordered** selection — the same three people form the same committee no matter +what order they're picked in — so this is a combination: + +\[C(8,3) = \frac{8!}{3!\,5!} = \frac{8 \times 7 \times 6}{3 \times 2 \times 1} = \frac{336}{6} = 56\] + +**Traps:** +- **E (336)** is \(P(8,3) = 8 \times 7 \times 6\) — treating order as mattering, so each committee is + counted \(3! = 6\) times. +- **C (112)** divides by 3 instead of \(3! = 6\) \((336/3)\). +- **D (168)** divides by \(2!\) instead of \(3!\) \((336/2)\). +- **A (24)** just computes \(8 \times 3\). + +## Hints + +- "Committee," "group," "selection" → order doesn't matter → combination. +- A combination is the permutation \(P(n,k)\) divided by \(k!\) to remove the internal orderings. diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0020.md b/content/questions/quant/quant-counting-probability-combinatorics-0020.md new file mode 100644 index 0000000..c9e64ca --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0020.md @@ -0,0 +1,47 @@ +--- +id: quant-counting-probability-combinatorics-0020 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: medium +tags: [combinatorics, permutations, identical-items] +choices: + A: "10" + B: "20" + C: "30" + D: "60" + E: "120" +answer: C +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +How many distinct arrangements can be made of the letters in the word LEVEL? + +## Explanation + +LEVEL has 5 letters, but they are not all distinct: **L** appears twice, **E** appears twice, and +**V** once. When items repeat, divide \(n!\) by the factorial of each repeat count: + +\[\frac{5!}{2!\,2!} = \frac{120}{2 \times 2} = \frac{120}{4} = 30\] + +The two L's are interchangeable (swapping them gives the same word), and likewise the two E's, so +each distinct arrangement was counted \(2! \times 2! = 4\) times in the raw \(5!\). + +**Traps:** +- **E (120)** is \(5!\) — treating all five letters as distinct. +- **D (60)** divides by only one \(2!\) \((120/2)\), correcting for the L's but not the E's (or vice + versa). +- **A (10)** over-divides, e.g. by an extra factor. +- **B (20)** divides by \(3!\) \((120/6)\), as if three letters repeated. + +## Hints + +- Repeated, interchangeable letters mean the plain \(5!\) over-counts. +- Divide \(5!\) by \(2!\) for the two L's **and** another \(2!\) for the two E's. diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0021.md b/content/questions/quant/quant-counting-probability-combinatorics-0021.md new file mode 100644 index 0000000..83dda30 --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0021.md @@ -0,0 +1,49 @@ +--- +id: quant-counting-probability-combinatorics-0021 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: medium +tags: [combinatorics, permutations, restrictions] +choices: + A: "120" + B: "240" + C: "360" + D: "480" + E: "720" +answer: B +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +Six friends are to be seated in a row of 6 chairs. If two particular friends insist on sitting +next to each other, how many seating arrangements are possible? + +## Explanation + +Use the "glue" method for a must-be-together restriction. Treat the two friends as a single block, +so you're now arranging **5 units** (the block plus the other 4 people): + +\[5! = 120 \text{ ways to arrange the units}\] + +The two friends inside the block can also swap seats, which is \(2! = 2\) arrangements: + +\[120 \times 2 = 240\] + +**Traps:** +- **E (720)** is \(6!\) — ignoring the restriction entirely. +- **A (120)** is \(5!\) — gluing the pair but forgetting they can be ordered two ways inside the + block. +- **C (360)** halves \(6!\) \((720/2)\), a common but incorrect fix. +- **D (480)** doubles the correct count, applying the internal \(\times 2\) one time too many. + +## Hints + +- "Must sit together" → glue the pair into one block and arrange the resulting units. +- Don't forget the pair can be ordered two ways inside their block. diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0022.md b/content/questions/quant/quant-counting-probability-combinatorics-0022.md new file mode 100644 index 0000000..37070db --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0022.md @@ -0,0 +1,49 @@ +--- +id: quant-counting-probability-combinatorics-0022 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: hard +tags: [combinatorics, combinations, at-least, complement] +choices: + A: "40" + B: "121" + C: "126" + D: "131" + E: "224" +answer: B +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +A committee of 4 is to be formed from 5 men and 4 women, and it must include at least 1 woman. How +many different committees are possible? + +## Explanation + +"At least 1 woman" is cleanest by the **complement**: count all committees, then subtract the ones +with **no** women. + +- Total committees of 4 from 9 people: \(C(9,4) = \dfrac{9 \times 8 \times 7 \times 6}{4!} = 126\). +- Committees with no women (all 4 from the 5 men): \(C(5,4) = 5\). + +\[126 - 5 = 121\] + +**Traps:** +- **C (126)** ignores the "at least 1 woman" requirement — that's the unrestricted total. +- **D (131)** *adds* the all-men committees instead of subtracting them \((126 + 5)\). +- **E (224)** is the classic over-count \(C(4,1)\times C(8,3) = 4 \times 56\): "pick 1 woman, then + any 3 more" counts committees with multiple women several times. +- **A (40)** counts only committees with *exactly* one woman \(\left(C(4,1)\times C(5,3) = 4\times + 10\right)\), missing those with 2, 3, or 4 women. + +## Hints + +- For "at least one," subtract the complement — total committees minus committees with no women. +- Avoid "pick one required item, then fill the rest freely" — it double-counts. diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0023.md b/content/questions/quant/quant-counting-probability-combinatorics-0023.md new file mode 100644 index 0000000..c6c7e91 --- /dev/null +++ b/content/questions/quant/quant-counting-probability-combinatorics-0023.md @@ -0,0 +1,60 @@ +--- +id: quant-counting-probability-combinatorics-0023 +section: quant +topic: counting-probability +subtopic: combinatorics +type: problem-solving +difficulty: hard +tags: [combinatorics, permutations, restrictions, gap-method] +choices: + A: "720" + B: "2400" + C: "4320" + D: "14400" + E: "40320" +answer: D +author: openmat +reviewers: [] +status: in-review +original: true +license: CC-BY-SA-4.0 +--- + +## Question + +Five boys and three girls are to stand in a single row. In how many arrangements will no two girls +stand next to each other? (All eight people are distinct.) + +## Explanation + +Use the **gap method** for a "no two together" restriction. First seat the unrestricted people — +the 5 boys — in a row: + +\[5! = 120 \text{ ways}\] + +Five boys create **6 gaps** (one before each boy, between each pair, and one after the last): + +\[\_\,B\,\_\,B\,\_\,B\,\_\,B\,\_\,B\,\_\] + +To keep the girls apart, place each of the 3 distinct girls in a **different** gap. Choosing 3 of +the 6 gaps *with order* (the girls are distinct) is a permutation: + +\[P(6,3) = 6 \times 5 \times 4 = 120\] + +Multiply the independent stages: + +\[5! \times P(6,3) = 120 \times 120 = 14{,}400\] + +**Traps:** +- **E (40320)** is \(8!\) — every arrangement, ignoring the restriction. +- **C (4320)** is \(6! \times 3!\) — the count for all three girls standing *together* (a block), + the opposite restriction. +- **B (2400)** uses \(5! \times C(6,3) = 120 \times 20\), forgetting the girls are distinct, so the + gap choice should be a permutation, not a combination. +- **A (720)** is \(5! \times 3!\) — arranging the two groups internally but never interleaving them. + +## Hints + +- "No two together" → seat the others first, then drop the restricted people into the gaps between + them. +- Five boys leave 6 gaps; the 3 girls are distinct, so choosing gaps for them is a permutation. diff --git a/docs/data/content.json b/docs/data/content.json index 9ef981c..a413c3d 100644 --- a/docs/data/content.json +++ b/docs/data/content.json @@ -7,8 +7,8 @@ "stats": { "quant": { "label": "Quantitative", - "questions": 16, - "lessons": 5 + "questions": 23, + "lessons": 6 }, "verbal": { "label": "Verbal", @@ -22,8 +22,8 @@ } }, "counts": { - "questions": 32, - "lessons": 8 + "questions": 39, + "lessons": 9 }, "questions": [ { @@ -359,6 +359,216 @@ "Multiply the factors together and compare to 1." ] }, + { + "id": "quant-counting-probability-combinatorics-0017", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "easy", + "tags": [ + "combinatorics", + "counting-principle" + ], + "choices": { + "A": "8", + "B": "9", + "C": "12", + "D": "24", + "E": "48" + }, + "answer": "D", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "A café builds a sandwich by choosing exactly one of 4 breads, one of 3 cheeses, and one of 2\nspreads. How many distinct sandwiches can be made?", + "explanation": "The three choices are made **together** (one bread *and* one cheese *and* one spread), so by the\nfundamental counting principle you **multiply**:\n\n\\[4 \\times 3 \\times 2 = 24\\]\n\n**Traps:**\n- **B (9)** *adds* the options \\((4 + 3 + 2)\\) — addition is for mutually exclusive cases (\"or\"),\n not for independent successive choices (\"and\").\n- **C (12)** multiplies only bread and cheese \\((4 \\times 3)\\), forgetting the spread.\n- **A (8)** multiplies only bread and spread \\((4 \\times 2)\\), forgetting the cheese.\n- **E (48)** double-counts the spread \\((4 \\times 3 \\times 2 \\times 2)\\).", + "hints": [ + "Each independent choice made together multiplies the count — decide multiply vs. add first.", + "Include every stage: bread × cheese × spread." + ] + }, + { + "id": "quant-counting-probability-combinatorics-0018", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "easy", + "tags": [ + "combinatorics", + "permutations", + "factorial" + ], + "choices": { + "A": "15", + "B": "20", + "C": "24", + "D": "25", + "E": "120" + }, + "answer": "E", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "In how many different ways can 5 distinct books be arranged in a row on a shelf?", + "explanation": "Arranging \\(n\\) distinct items in a row is \\(n!\\). With 5 books:\n\n\\[5! = 5 \\times 4 \\times 3 \\times 2 \\times 1 = 120\\]\n\nThink of it as filling positions left to right: 5 choices for the first slot, 4 for the next, then\n3, 2, 1 — multiplied together.\n\n**Traps:**\n- **A (15)** *adds* \\(5 + 4 + 3 + 2 + 1\\) instead of multiplying.\n- **B (20)** stops after two factors \\((5 \\times 4)\\).\n- **C (24)** is \\(4!\\) — arranging only 4 of the books.\n- **D (25)** is \\(5^2\\), treating each slot as having all 5 books available (no removal after\n placing one).", + "hints": [ + "\"Arrange all \\(n\\) distinct items in a row\" is exactly \\(n!\\).", + "Each book used up reduces the choices for the next slot: \\(5 \\times 4 \\times 3 \\times 2 \\times 1\\)." + ] + }, + { + "id": "quant-counting-probability-combinatorics-0019", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "medium", + "tags": [ + "combinatorics", + "combinations" + ], + "choices": { + "A": "24", + "B": "56", + "C": "112", + "D": "168", + "E": "336" + }, + "answer": "B", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "A committee of 3 people is to be selected from a group of 8. How many different committees are\npossible?", + "explanation": "A committee is an **unordered** selection — the same three people form the same committee no matter\nwhat order they're picked in — so this is a combination:\n\n\\[C(8,3) = \\frac{8!}{3!\\,5!} = \\frac{8 \\times 7 \\times 6}{3 \\times 2 \\times 1} = \\frac{336}{6} = 56\\]\n\n**Traps:**\n- **E (336)** is \\(P(8,3) = 8 \\times 7 \\times 6\\) — treating order as mattering, so each committee is\n counted \\(3! = 6\\) times.\n- **C (112)** divides by 3 instead of \\(3! = 6\\) \\((336/3)\\).\n- **D (168)** divides by \\(2!\\) instead of \\(3!\\) \\((336/2)\\).\n- **A (24)** just computes \\(8 \\times 3\\).", + "hints": [ + "\"Committee,\" \"group,\" \"selection\" → order doesn't matter → combination.", + "A combination is the permutation \\(P(n,k)\\) divided by \\(k!\\) to remove the internal orderings." + ] + }, + { + "id": "quant-counting-probability-combinatorics-0020", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "medium", + "tags": [ + "combinatorics", + "permutations", + "identical-items" + ], + "choices": { + "A": "10", + "B": "20", + "C": "30", + "D": "60", + "E": "120" + }, + "answer": "C", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "How many distinct arrangements can be made of the letters in the word LEVEL?", + "explanation": "LEVEL has 5 letters, but they are not all distinct: **L** appears twice, **E** appears twice, and\n**V** once. When items repeat, divide \\(n!\\) by the factorial of each repeat count:\n\n\\[\\frac{5!}{2!\\,2!} = \\frac{120}{2 \\times 2} = \\frac{120}{4} = 30\\]\n\nThe two L's are interchangeable (swapping them gives the same word), and likewise the two E's, so\neach distinct arrangement was counted \\(2! \\times 2! = 4\\) times in the raw \\(5!\\).\n\n**Traps:**\n- **E (120)** is \\(5!\\) — treating all five letters as distinct.\n- **D (60)** divides by only one \\(2!\\) \\((120/2)\\), correcting for the L's but not the E's (or vice\n versa).\n- **A (10)** over-divides, e.g. by an extra factor.\n- **B (20)** divides by \\(3!\\) \\((120/6)\\), as if three letters repeated.", + "hints": [ + "Repeated, interchangeable letters mean the plain \\(5!\\) over-counts.", + "Divide \\(5!\\) by \\(2!\\) for the two L's **and** another \\(2!\\) for the two E's." + ] + }, + { + "id": "quant-counting-probability-combinatorics-0021", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "medium", + "tags": [ + "combinatorics", + "permutations", + "restrictions" + ], + "choices": { + "A": "120", + "B": "240", + "C": "360", + "D": "480", + "E": "720" + }, + "answer": "B", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "Six friends are to be seated in a row of 6 chairs. If two particular friends insist on sitting\nnext to each other, how many seating arrangements are possible?", + "explanation": "Use the \"glue\" method for a must-be-together restriction. Treat the two friends as a single block,\nso you're now arranging **5 units** (the block plus the other 4 people):\n\n\\[5! = 120 \\text{ ways to arrange the units}\\]\n\nThe two friends inside the block can also swap seats, which is \\(2! = 2\\) arrangements:\n\n\\[120 \\times 2 = 240\\]\n\n**Traps:**\n- **E (720)** is \\(6!\\) — ignoring the restriction entirely.\n- **A (120)** is \\(5!\\) — gluing the pair but forgetting they can be ordered two ways inside the\n block.\n- **C (360)** halves \\(6!\\) \\((720/2)\\), a common but incorrect fix.\n- **D (480)** doubles the correct count, applying the internal \\(\\times 2\\) one time too many.", + "hints": [ + "\"Must sit together\" → glue the pair into one block and arrange the resulting units.", + "Don't forget the pair can be ordered two ways inside their block." + ] + }, + { + "id": "quant-counting-probability-combinatorics-0022", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "hard", + "tags": [ + "combinatorics", + "combinations", + "at-least", + "complement" + ], + "choices": { + "A": "40", + "B": "121", + "C": "126", + "D": "131", + "E": "224" + }, + "answer": "B", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "A committee of 4 is to be formed from 5 men and 4 women, and it must include at least 1 woman. How\nmany different committees are possible?", + "explanation": "\"At least 1 woman\" is cleanest by the **complement**: count all committees, then subtract the ones\nwith **no** women.\n\n- Total committees of 4 from 9 people: \\(C(9,4) = \\dfrac{9 \\times 8 \\times 7 \\times 6}{4!} = 126\\).\n- Committees with no women (all 4 from the 5 men): \\(C(5,4) = 5\\).\n\n\\[126 - 5 = 121\\]\n\n**Traps:**\n- **C (126)** ignores the \"at least 1 woman\" requirement — that's the unrestricted total.\n- **D (131)** *adds* the all-men committees instead of subtracting them \\((126 + 5)\\).\n- **E (224)** is the classic over-count \\(C(4,1)\\times C(8,3) = 4 \\times 56\\): \"pick 1 woman, then\n any 3 more\" counts committees with multiple women several times.\n- **A (40)** counts only committees with *exactly* one woman \\(\\left(C(4,1)\\times C(5,3) = 4\\times\n 10\\right)\\), missing those with 2, 3, or 4 women.", + "hints": [ + "For \"at least one,\" subtract the complement — total committees minus committees with no women.", + "Avoid \"pick one required item, then fill the rest freely\" — it double-counts." + ] + }, + { + "id": "quant-counting-probability-combinatorics-0023", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "type": "problem-solving", + "difficulty": "hard", + "tags": [ + "combinatorics", + "permutations", + "restrictions", + "gap-method" + ], + "choices": { + "A": "720", + "B": "2400", + "C": "4320", + "D": "14400", + "E": "40320" + }, + "answer": "D", + "author": "openmat", + "reviewers": [], + "status": "in-review", + "prompt": "Five boys and three girls are to stand in a single row. In how many arrangements will no two girls\nstand next to each other? (All eight people are distinct.)", + "explanation": "Use the **gap method** for a \"no two together\" restriction. First seat the unrestricted people —\nthe 5 boys — in a row:\n\n\\[5! = 120 \\text{ ways}\\]\n\nFive boys create **6 gaps** (one before each boy, between each pair, and one after the last):\n\n\\[\\_\\,B\\,\\_\\,B\\,\\_\\,B\\,\\_\\,B\\,\\_\\,B\\,\\_\\]\n\nTo keep the girls apart, place each of the 3 distinct girls in a **different** gap. Choosing 3 of\nthe 6 gaps *with order* (the girls are distinct) is a permutation:\n\n\\[P(6,3) = 6 \\times 5 \\times 4 = 120\\]\n\nMultiply the independent stages:\n\n\\[5! \\times P(6,3) = 120 \\times 120 = 14{,}400\\]\n\n**Traps:**\n- **E (40320)** is \\(8!\\) — every arrangement, ignoring the restriction.\n- **C (4320)** is \\(6! \\times 3!\\) — the count for all three girls standing *together* (a block),\n the opposite restriction.\n- **B (2400)** uses \\(5! \\times C(6,3) = 120 \\times 20\\), forgetting the girls are distinct, so the\n gap choice should be a permutation, not a combination.\n- **A (720)** is \\(5! \\times 3!\\) — arranging the two groups internally but never interleaving them.", + "hints": [ + "\"No two together\" → seat the others first, then drop the restricted people into the gaps between", + "Five boys leave 6 gaps; the 3 girls are distinct, so choosing gaps for them is a permutation." + ] + }, { "id": "quant-counting-probability-probability-0007", "section": "quant", @@ -984,6 +1194,24 @@ "status": "in-review", "body": "## Overview\n\nPercents and ratios are the most frequently tested arithmetic ideas on GMAT Focus Quant, and\nthey hide inside word problems everywhere. Master three moves: converting between forms, handling\npercent *change*, and scaling ratios.\n\n## Core concepts\n\n**Percent as a factor.** A percent is just a number over 100. The fastest way to apply a percent\nchange is to turn it into a **multiplier**:\n\n- Increase by \\(r\\%\\): multiply by \\(1 + \\tfrac{r}{100}\\). (+25% → \\(\\times 1.25\\))\n- Decrease by \\(r\\%\\): multiply by \\(1 - \\tfrac{r}{100}\\). (−20% → \\(\\times 0.80\\))\n\n**Successive changes multiply.** Two changes in a row are multiplied, never added:\n\n\\[(+25\\%)\\text{ then }(-20\\%): \\quad 1.25 \\times 0.80 = 1.00 \\quad (\\text{no net change})\\]\n\n**Percent change formula.**\n\n\\[\\text{percent change} = \\frac{\\text{new} - \\text{old}}{\\text{old}} \\times 100\\%\\]\n\nAlways divide by the **original** value, not the new one.\n\n**Ratios scale together.** A ratio \\(a : b\\) means the quantities are \\(ak\\) and \\(bk\\) for some\nmultiplier \\(k\\). If boys : girls \\(= 3 : 5\\) and there are 24 boys, then \\(k = 8\\), so there are\n\\(5 \\times 8 = 40\\) girls.\n\n## Worked examples\n\n**Percent change.** A stock rises from $80 to $100. Percent increase \\(= \\tfrac{100 - 80}{80} = \\tfrac{20}{80} = 25\\%\\).\nNote it later falls from $100 back to $80: that's \\(\\tfrac{-20}{100} = -20\\%\\) — a *smaller* percent, because the base is now larger.\n\n**Ratio scaling.** A recipe uses flour : sugar \\(= 7 : 2\\). To use 21 cups of flour, \\(k = 3\\), so\nyou need \\(2 \\times 3 = 6\\) cups of sugar.\n\n## Common traps\n\n- **Adding successive percents.** +25% then −20% is *not* +5%; it's \\(1.25 \\times 0.80 = 1.00\\).\n- **Wrong base.** Percent change always divides by the original amount. A rise then an equal-percent fall does not return to the start.\n- **Ratio ≠ actual count.** \\(3 : 5\\) does not mean 3 and 5 — it means \\(3k\\) and \\(5k\\). Find \\(k\\) first.\n\n## Key takeaways\n\n- Convert percent changes to multipliers and multiply them for successive changes.\n- Percent change = (new − old) / old.\n- A ratio \\(a : b\\) represents \\(ak\\) and \\(bk\\); solve for the multiplier \\(k\\), then scale." }, + { + "id": "quant-counting-probability-combinatorics", + "section": "quant", + "topic": "counting-probability", + "subtopic": "combinatorics", + "title": "Combinatorics: Counting Without Listing", + "tags": [ + "combinatorics", + "counting", + "permutations", + "combinations", + "factorials" + ], + "author": "openmat", + "reviewers": [], + "status": "in-review", + "body": "## Overview\n\nCombinatorics is the art of counting arrangements and selections without writing them all out.\nAlmost every Focus counting problem reduces to two questions: **do I multiply or add?** and **does\norder matter?** Get those two right and the formulas fall into place.\n\n## Core concepts\n\n**Fundamental counting principle (multiply for \"and\").** If one choice can be made \\(m\\) ways and\nan independent next choice \\(n\\) ways, the two together can be made \\(m \\times n\\) ways. Multiply\nstages that happen *together*; **add** only mutually exclusive *cases* (\"or\").\n\n**Factorial — arranging distinct things in a row.** \\(n\\) distinct items in a row:\n\n\\[n! = n \\times (n-1) \\times \\cdots \\times 2 \\times 1\\]\n\nBy convention \\(0! = 1\\).\n\n**Permutations — order matters.** Choosing and arranging \\(k\\) of \\(n\\) distinct items:\n\n\\[P(n, k) = \\frac{n!}{(n-k)!} = n(n-1)\\cdots(n-k+1)\\]\n\n**Combinations — order does not matter.** Choosing \\(k\\) of \\(n\\) with no regard to order:\n\n\\[C(n, k) = \\binom{n}{k} = \\frac{n!}{k!\\,(n-k)!} = \\frac{P(n,k)}{k!}\\]\n\nA combination is a permutation with the \\(k!\\) internal orderings divided out. \"Committee,\"\n\"group,\" \"selection,\" \"handshake\" → combination. \"Ranking,\" \"arrangement,\" \"line,\" \"code\" →\npermutation.\n\n**Identical items.** Arranging \\(n\\) items where some repeat (\\(a\\) alike, \\(b\\) alike, …):\n\n\\[\\frac{n!}{a!\\,b!\\,\\cdots}\\]\n\nDivide out the interchangeable duplicates. (This is why `LEVEL` has \\(5!/(2!\\,2!)\\) arrangements,\nnot \\(5!\\).)\n\n**Restrictions — two standard moves.**\n- *Must be together:* glue the group into one block, arrange the blocks, then multiply by the\n internal arrangements of the block.\n- *Must be apart (no two adjacent):* seat the unrestricted items first, then drop the restricted\n ones into the **gaps** between and around them.\n\n## Worked examples\n\n**Multiply.** A menu has 4 breads, 3 fillings, 2 spreads; one of each → \\(4\\times 3\\times 2 = 24\\)\nsandwiches.\n\n**Combination vs. permutation.** From 8 people, a *committee* of 3 is \\(C(8,3) = 56\\); a *ranked*\ntop-3 is \\(P(8,3) = 336\\). The ratio is \\(3! = 6\\), the orderings a committee ignores.\n\n**Together.** Six people in a row with two specific people adjacent: treat the pair as one block →\n\\(5!\\) arrangements of the 5 units, times \\(2!\\) for the pair's order \\(= 240\\).\n\n## Common traps\n\n- **Adding when you should multiply.** Independent successive choices multiply; \\(4+3+2\\) is wrong\n for the sandwich count.\n- **Permutation vs. combination.** Using \\(P(n,k)\\) for an unordered selection over-counts by\n \\(k!\\); using \\(C(n,k)\\) for an ordered one under-counts.\n- **Forgetting to divide out identical items.** \\(5!\\) over-counts `LEVEL` because swapping the two\n L's (or two E's) changes nothing.\n- **\"At least one\" head-on.** Counting cases one by one invites double-counting. Use the\n complement: (total) − (none) is almost always cleaner.\n- **Restriction, then forget the internal order.** After gluing a block, remember to multiply by\n the block's own arrangements.\n\n## Key takeaways\n\n- Decide multiply-vs-add and order-matters-vs-not *before* reaching for a formula.\n- \\(C(n,k) = P(n,k)/k!\\) — a combination is a permutation with the orderings removed.\n- Identical items: divide \\(n!\\) by the factorial of each repeat count.\n- \"At least one\" → subtract the complement from the total.\n- Restrictions: glue for \"together,\" use gaps for \"apart.\"" + }, { "id": "quant-statistics-descriptive", "section": "quant", From 05f0effa24bf5aa3c78515a261b63522bf3da4c8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 12:44:02 +0000 Subject: [PATCH 2/2] Fix daily-run duplication: per-subtopic numbering + coordination protocol Root cause: daily-run agents each branch off main and never merge, so a run only sees main and is blind to other in-flight runs. Combined with a section-wide NNNN counter, every run grabbed the same next number and re-seeded the same subtopics -> ~15 overlapping, colliding open PRs. Fixes: - Numbering is now per-subtopic (NNNN unique within the subtopic, new subtopics start at 0001) instead of section-wide. Two branches now only collide if they target the SAME subtopic, which the protocol forbids. Documented in CONTENT_SCHEMA.md and CONTRIBUTING.md. No existing files need renaming (they are already unique within their subtopic). - New 'Automated & daily-run contributions' protocol in CONTRIBUTING.md: check OPEN pull requests (not just main) before claiming a subtopic, one subtopic per run, prefer genuine gaps, keep each run on its own PR. - New scripts/coverage.mjs: prints per-subtopic question counts, lesson presence, and gaps from the checkout (and flags mistagged content), so a run can find gaps fast. It notes it only sees the current checkout. - Renumbered this branch's combinatorics questions 0017-0023 -> 0001-0007 to follow the new scheme; rebuilt content.json (zero warnings). Note: this fixes the go-forward process. The existing open-PR backlog still needs the maintainer to merge or close it to fully clear the overlaps already created. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01WknFxpevhwGoMJPnoUW8bd --- CONTENT_SCHEMA.md | 2 +- CONTRIBUTING.md | 38 ++++- ...ounting-probability-combinatorics-0001.md} | 2 +- ...ounting-probability-combinatorics-0002.md} | 2 +- ...ounting-probability-combinatorics-0003.md} | 2 +- ...ounting-probability-combinatorics-0004.md} | 2 +- ...ounting-probability-combinatorics-0005.md} | 2 +- ...ounting-probability-combinatorics-0006.md} | 2 +- ...ounting-probability-combinatorics-0007.md} | 2 +- docs/data/content.json | 14 +- scripts/coverage.mjs | 150 ++++++++++++++++++ 11 files changed, 202 insertions(+), 16 deletions(-) rename content/questions/quant/{quant-counting-probability-combinatorics-0017.md => quant-counting-probability-combinatorics-0001.md} (96%) rename content/questions/quant/{quant-counting-probability-combinatorics-0018.md => quant-counting-probability-combinatorics-0002.md} (95%) rename content/questions/quant/{quant-counting-probability-combinatorics-0019.md => quant-counting-probability-combinatorics-0003.md} (96%) rename content/questions/quant/{quant-counting-probability-combinatorics-0020.md => quant-counting-probability-combinatorics-0004.md} (96%) rename content/questions/quant/{quant-counting-probability-combinatorics-0021.md => quant-counting-probability-combinatorics-0005.md} (96%) rename content/questions/quant/{quant-counting-probability-combinatorics-0022.md => quant-counting-probability-combinatorics-0006.md} (96%) rename content/questions/quant/{quant-counting-probability-combinatorics-0023.md => quant-counting-probability-combinatorics-0007.md} (97%) create mode 100644 scripts/coverage.mjs diff --git a/CONTENT_SCHEMA.md b/CONTENT_SCHEMA.md index ffc01bb..b2fe4d4 100644 --- a/CONTENT_SCHEMA.md +++ b/CONTENT_SCHEMA.md @@ -56,7 +56,7 @@ license: CC-BY-SA-4.0 | Field | Required | Type | Notes | |-------|----------|------|-------| -| `id` | yes | string | Unique. Must equal the filename without `.md`. Format: `
---NNNN`. | +| `id` | yes | string | Unique. Must equal the filename without `.md`. Format: `
---NNNN`. `NNNN` is a 4-digit sequence number **unique within the subtopic** — pick the next unused number for that exact subtopic (a brand-new subtopic starts at `0001`). Per-subtopic numbering keeps parallel branches from colliding; see [CONTRIBUTING.md](CONTRIBUTING.md#file-naming--location). | | `section` | yes | enum | `quant` \| `verbal` \| `data-insights`. | | `topic` | yes | string | A topic slug from [`curriculum.md`](curriculum.md). | | `subtopic` | yes | string | A subtopic slug from [`curriculum.md`](curriculum.md). | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f896da8..c709133 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,10 +70,19 @@ content/lessons/
/-.md - `
` is `quant`, `verbal`, or `data-insights`. - `` / `` must be slugs that exist in [`curriculum.md`](curriculum.md). -- `NNNN` is a zero-padded sequence number, unique within the section (pick the next unused one). +- `NNNN` is a zero-padded sequence number, **unique within the subtopic** — pick the next unused + number *for that exact subtopic*. A brand-new subtopic starts at `0001`; a subtopic that already + has `...-0001` and `...-0002` continues at `0003`. Example: `content/questions/quant/quant-algebra-linear-equations-0001.md` +> **Why per-subtopic and not per-section?** Contributors (and automated runs) work on separate +> branches that haven't merged yet. A single section-wide counter makes every open branch grab the +> same "next" number, so two PRs collide the moment they touch the same section. Numbering *within +> the subtopic* means two PRs only ever collide if they target the **same subtopic** — which the +> coordination rules below already tell you to avoid. Existing files are already unique within their +> subtopic, so nothing needs renaming. + --- ## The review workflow @@ -119,6 +128,33 @@ If unsure, tag `medium` and note your uncertainty in the PR — reviewers can ad --- +## Automated & daily-run contributions + +Some content is added by automated "daily run" agents. Because every run works on its **own +branch** and those branches are **not merged between runs**, a run that only looks at `main` is +blind to what other in-flight runs have already produced — which historically led to several PRs +re-seeding the same subtopic and colliding on sequence numbers. If you run one of these agents (or +review their output), follow this protocol so parallel work doesn't duplicate: + +1. **Check what's already in flight, not just `main`.** Before choosing a subtopic, list the + repository's **open pull requests** and read their titles/branches. Treat any subtopic that an + open PR already covers as *taken* — do not seed it again. (`main` alone is not the current state + of the world; most content lives in unmerged branches.) +2. **One subtopic per run, and prefer genuine gaps.** Pick a subtopic that is uncovered on `main` + **and** not claimed by any open PR. Run `node scripts/coverage.mjs` to see per-subtopic counts + and gaps on the current checkout, then cross-check against open PRs for the in-flight ones. +3. **Number within the subtopic.** Follow the [file-naming rule](#file-naming--location): + `NNNN` is the next unused number *for that subtopic*. Since rule 2 keeps runs on distinct + subtopics, per-subtopic numbering makes filename collisions between branches impossible without + having to inspect other branches' numbers. +4. **Keep each run on its own branch and its own PR.** Don't stack a new run's content onto another + run's branch, and don't reuse a merged PR — start fresh from the latest default branch. +5. **Stop at "open PR."** Opening the draft PR is the end of a run's job. Don't re-seed a subtopic + that any open PR (including your own from a previous run) already handles. + +Reviewers/maintainers: merging or closing the open backlog promptly is what makes rules 1–2 +reliable — the longer PRs sit unmerged, the more the "check open PRs" step has to compensate for. + ## Code of conduct Participation is governed by our [Code of Conduct](CODE_OF_CONDUCT.md). Be kind; assume good diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0017.md b/content/questions/quant/quant-counting-probability-combinatorics-0001.md similarity index 96% rename from content/questions/quant/quant-counting-probability-combinatorics-0017.md rename to content/questions/quant/quant-counting-probability-combinatorics-0001.md index c73cb27..4a7ce7f 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0017.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0001.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0017 +id: quant-counting-probability-combinatorics-0001 section: quant topic: counting-probability subtopic: combinatorics diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0018.md b/content/questions/quant/quant-counting-probability-combinatorics-0002.md similarity index 95% rename from content/questions/quant/quant-counting-probability-combinatorics-0018.md rename to content/questions/quant/quant-counting-probability-combinatorics-0002.md index 7f72862..5f5b9b8 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0018.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0002.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0018 +id: quant-counting-probability-combinatorics-0002 section: quant topic: counting-probability subtopic: combinatorics diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0019.md b/content/questions/quant/quant-counting-probability-combinatorics-0003.md similarity index 96% rename from content/questions/quant/quant-counting-probability-combinatorics-0019.md rename to content/questions/quant/quant-counting-probability-combinatorics-0003.md index 662ee95..4ecf2ec 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0019.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0003.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0019 +id: quant-counting-probability-combinatorics-0003 section: quant topic: counting-probability subtopic: combinatorics diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0020.md b/content/questions/quant/quant-counting-probability-combinatorics-0004.md similarity index 96% rename from content/questions/quant/quant-counting-probability-combinatorics-0020.md rename to content/questions/quant/quant-counting-probability-combinatorics-0004.md index c9e64ca..4d329fb 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0020.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0004.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0020 +id: quant-counting-probability-combinatorics-0004 section: quant topic: counting-probability subtopic: combinatorics diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0021.md b/content/questions/quant/quant-counting-probability-combinatorics-0005.md similarity index 96% rename from content/questions/quant/quant-counting-probability-combinatorics-0021.md rename to content/questions/quant/quant-counting-probability-combinatorics-0005.md index 83dda30..12bb92d 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0021.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0005.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0021 +id: quant-counting-probability-combinatorics-0005 section: quant topic: counting-probability subtopic: combinatorics diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0022.md b/content/questions/quant/quant-counting-probability-combinatorics-0006.md similarity index 96% rename from content/questions/quant/quant-counting-probability-combinatorics-0022.md rename to content/questions/quant/quant-counting-probability-combinatorics-0006.md index 37070db..9790958 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0022.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0006.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0022 +id: quant-counting-probability-combinatorics-0006 section: quant topic: counting-probability subtopic: combinatorics diff --git a/content/questions/quant/quant-counting-probability-combinatorics-0023.md b/content/questions/quant/quant-counting-probability-combinatorics-0007.md similarity index 97% rename from content/questions/quant/quant-counting-probability-combinatorics-0023.md rename to content/questions/quant/quant-counting-probability-combinatorics-0007.md index c6c7e91..2109664 100644 --- a/content/questions/quant/quant-counting-probability-combinatorics-0023.md +++ b/content/questions/quant/quant-counting-probability-combinatorics-0007.md @@ -1,5 +1,5 @@ --- -id: quant-counting-probability-combinatorics-0023 +id: quant-counting-probability-combinatorics-0007 section: quant topic: counting-probability subtopic: combinatorics diff --git a/docs/data/content.json b/docs/data/content.json index a413c3d..4bcec09 100644 --- a/docs/data/content.json +++ b/docs/data/content.json @@ -360,7 +360,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0017", + "id": "quant-counting-probability-combinatorics-0001", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", @@ -389,7 +389,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0018", + "id": "quant-counting-probability-combinatorics-0002", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", @@ -419,7 +419,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0019", + "id": "quant-counting-probability-combinatorics-0003", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", @@ -448,7 +448,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0020", + "id": "quant-counting-probability-combinatorics-0004", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", @@ -478,7 +478,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0021", + "id": "quant-counting-probability-combinatorics-0005", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", @@ -508,7 +508,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0022", + "id": "quant-counting-probability-combinatorics-0006", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", @@ -539,7 +539,7 @@ ] }, { - "id": "quant-counting-probability-combinatorics-0023", + "id": "quant-counting-probability-combinatorics-0007", "section": "quant", "topic": "counting-probability", "subtopic": "combinatorics", diff --git a/scripts/coverage.mjs b/scripts/coverage.mjs new file mode 100644 index 0000000..a779ed0 --- /dev/null +++ b/scripts/coverage.mjs @@ -0,0 +1,150 @@ +#!/usr/bin/env node +// OpenMat coverage report. +// Cross-references the taxonomy in curriculum.md against the content actually +// present in content/questions and content/lessons, and prints per-subtopic +// counts plus a list of gaps (subtopics with no questions). +// +// IMPORTANT: this only sees the CURRENT checkout. Content that lives in +// unmerged pull-request branches is invisible here — before claiming a +// subtopic, also check the repository's OPEN pull requests (see the +// "Automated & daily-run contributions" section of CONTRIBUTING.md). +// +// Usage: node scripts/coverage.mjs + +import { readdir, readFile } from 'node:fs/promises'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); +const QUESTIONS_DIR = join(ROOT, 'content', 'questions'); +const LESSONS_DIR = join(ROOT, 'content', 'lessons'); +const CURRICULUM = join(ROOT, 'curriculum.md'); + +const SECTIONS = new Set(['quant', 'verbal', 'data-insights']); + +async function walk(dir) { + let entries; + try { + entries = await readdir(dir, { withFileTypes: true }); + } catch { + return []; + } + const files = []; + for (const e of entries) { + const p = join(dir, e.name); + if (e.isDirectory()) files.push(...(await walk(p))); + else if (e.isFile() && e.name.endsWith('.md')) files.push(p); + } + return files; +} + +// Pull the front-matter scalar fields we care about (no YAML dep needed). +function frontMatter(raw) { + const text = raw.replace(/\r\n?/g, '\n'); + if (!text.startsWith('---')) return {}; + const end = text.indexOf('\n---', 3); + if (end === -1) return {}; + const block = text.slice(3, end); + const out = {}; + for (const line of block.split('\n')) { + const m = /^([a-zA-Z_]+):\s*(.*)$/.exec(line); + if (!m) continue; + let v = m[2].trim().replace(/^["']|["']$/g, ''); + out[m[1]] = v; + } + return out; +} + +const backticks = (s) => Array.from(s.matchAll(/`([^`]+)`/g), (m) => m[1]); + +// Parse curriculum.md tables into taxonomy[section][topic] = Set(subtopics). +async function parseTaxonomy() { + const raw = (await readFile(CURRICULUM, 'utf8')).replace(/\r\n?/g, '\n'); + const taxonomy = {}; + let section = null; + for (const line of raw.split('\n')) { + const h = /^##\s+.*\(`([a-z0-9-]+)`\)/.exec(line); + if (h) { + section = SECTIONS.has(h[1]) ? h[1] : null; + continue; + } + if (!section || !line.trimStart().startsWith('|')) continue; + const cells = line.split('|').map((c) => c.trim()).filter(Boolean); + if (cells.length < 2) continue; + const topicTok = backticks(cells[0])[0]; + if (!topicTok || topicTok === 'topic' || topicTok === 'section') continue; // header rows + const subs = backticks(cells[cells.length - 1]).filter((s) => s !== 'subtopic'); + if (!subs.length) continue; + (taxonomy[section] ??= {}); + (taxonomy[section][topicTok] ??= new Set()); + for (const s of subs) taxonomy[section][topicTok].add(s); + } + return taxonomy; +} + +async function main() { + const taxonomy = await parseTaxonomy(); + + // Tally content by "section/topic/subtopic". + const qCount = {}; // key -> {easy,medium,hard,total} + const hasLesson = new Set(); + const unknown = []; // content tagged with a subtopic not in the taxonomy + + const inTaxonomy = (s, t, st) => taxonomy[s]?.[t]?.has(st); + const key = (s, t, st) => `${s}/${t}/${st}`; + + for (const file of await walk(QUESTIONS_DIR)) { + const fm = frontMatter(await readFile(file, 'utf8')); + if (!fm.section) continue; + const k = key(fm.section, fm.topic, fm.subtopic); + const c = (qCount[k] ??= { easy: 0, medium: 0, hard: 0, total: 0 }); + if (c[fm.difficulty] !== undefined) c[fm.difficulty]++; + c.total++; + if (!inTaxonomy(fm.section, fm.topic, fm.subtopic)) unknown.push(`Q ${k}`); + } + for (const file of await walk(LESSONS_DIR)) { + const fm = frontMatter(await readFile(file, 'utf8')); + if (!fm.section) continue; + const k = key(fm.section, fm.topic, fm.subtopic); + hasLesson.add(k); + if (!inTaxonomy(fm.section, fm.topic, fm.subtopic)) unknown.push(`L ${k}`); + } + + // Report. + let subtotal = 0; + let covered = 0; + const gaps = []; + console.log('OpenMat coverage — current checkout only (not other branches)\n'); + for (const section of Object.keys(taxonomy)) { + console.log(section.toUpperCase()); + for (const topic of Object.keys(taxonomy[section])) { + console.log(` ${topic}`); + for (const st of taxonomy[section][topic]) { + subtotal++; + const k = key(section, topic, st); + const c = qCount[k]; + const n = c ? c.total : 0; + if (n > 0) covered++; + else gaps.push(k); + const bd = c ? `(e${c.easy} m${c.medium} h${c.hard})` : ''; + const lesson = hasLesson.has(k) ? 'lesson' : ' '; + const flag = n === 0 ? ' << GAP (no questions)' : ''; + console.log(` ${st.padEnd(26)} Q:${String(n).padStart(2)} ${bd.padEnd(12)} ${lesson}${flag}`); + } + } + console.log(''); + } + + console.log(`Subtopics with ≥1 question: ${covered}/${subtotal}`); + console.log(`Gaps (no questions): ${gaps.length ? gaps.join(', ') : 'none'}`); + if (unknown.length) { + console.log(`\n⚠ Content tagged outside the taxonomy (check curriculum.md slugs):`); + for (const u of unknown) console.log(` - ${u}`); + } + console.log('\nReminder: also check OPEN pull requests before claiming a subtopic — see CONTRIBUTING.md.'); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +});