Skip to content

Conversation

@BrianLusina
Copy link
Owner

@BrianLusina BrianLusina commented Sep 9, 2025

Describe your change:

Non constructible change algorithm

  • Add an algorithm?
  • Fix a bug or typo in an existing algorithm?

Checklist:

  • I have read CONTRIBUTING.md.
  • This pull request is all my own work -- I have not plagiarized.
  • I know that pull requests will not be merged if they fail the automated tests.
  • This PR only changes one algorithm file. To ease review, please open separate PRs for separate algorithms.
  • All new Python files are placed inside an existing directory.
  • All filenames are in all lowercase characters with no spaces or dashes.
  • All functions and variable names follow Python naming conventions.
  • All function parameters and return values are annotated with Python type hints.
  • All functions have doctests that pass the automated testing.

Summary by CodeRabbit

  • New Features
    • Added a utility to compute the smallest non-constructible change from a list of coin values.
  • Documentation
    • Added a guide explaining the Non-Constructible Change problem with examples.
    • Updated the algorithm directory to include the new entry.
  • Tests
    • Added a comprehensive test suite covering empty inputs, duplicates, single/multiple coins, edge cases, and input immutability.
@BrianLusina BrianLusina self-assigned this Sep 9, 2025
@BrianLusina BrianLusina added Algorithm Algorithm Problem Array Array data structure labels Sep 9, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Walkthrough

Adds a new "Non-Constructible Change" problem under Algorithms -> Arrays: documentation entry, problem README, a Python implementation exposing non_constructible_change(coins: List[int]) -> int, and a unittest suite validating multiple scenarios.

Changes

Cohort / File(s) Summary
Docs
DIRECTORY.md, algorithms/arrays/non_constructible_change/README.md
Added directory entry linking to the new problem; created README describing the problem, sample input/output, and basic description.
Implementation
algorithms/arrays/non_constructible_change/__init__.py
Added non_constructible_change(coins: List[int]) -> int implementing the greedy sorted-accumulation algorithm (returns smallest non-constructible amount).
Tests
algorithms/arrays/non_constructible_change/test_non_constructible_change.py
Added unittest module with 13 test cases plus an input-mutation test covering empty, single, duplicates, mixed values, and edge cases.

Sequence Diagram(s)

sequenceDiagram autonumber actor Caller participant Func as non_constructible_change Caller->>Func: call with coins: List[int] note over Func: make sorted copy\ncurrent_change_created = 0 loop for each coin in sorted coins alt coin > current_change_created + 1 Func-->>Caller: return current_change_created + 1 else note over Func: current_change_created += coin end end Func-->>Caller: return current_change_created + 1 
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks (1 passed, 1 warning, 1 inconclusive)

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description includes a brief title and partially follows the template by marking the “Add an algorithm” checkbox and populating other checklist items, but it omits the “Documentation change?” checkbox as well as the line for issue resolution via “Fixes: #”. It also lacks a more detailed narrative of what the change entails beyond its name. To fully adhere to the repository’s pull request template, the missing checkbox and issue resolution information should be included. Add the “Documentation change?” checkbox and the checklist item for closing issues with “Fixes: #{$ISSUE_NO}”, and expand the “Describe your change” section to briefly outline the new algorithm’s functionality, sample use cases, and test coverage.
Title Check ❓ Inconclusive The current title indicates the addition of the non-constructible change algorithm and aligns with the changeset’s purpose, but it uses a conventional commit style prefix (‘feat(algorithms)’) that may be unnecessary in a pull request context. It also omits an explicit action verb, making it less immediately descriptive for reviewers scanning history. A more concise and clear title would improve readability. Rename the PR title to something like “Add Non-Constructible Change algorithm” or “Implement Non-Constructible Change algorithm” to clearly convey the purpose and remove the conventional commit prefix.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Poem

I jingle my coins in a clovery glade,
Seeking the gap where no change can be made.
Sort, step, and hop—mind the +1 line,
When a coin leaps over, the answer is mine.
A rabbit tests, then hides the prize—small and fine. 🐇💰

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews: pre_merge_checks: custom_checks: - name: "Undocumented Breaking Changes" mode: "warning" instructions: |  Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/algorithms-non-constructible-change

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
algorithms/arrays/non_constructible_change/README.md (2)

12-20: Use a standard fence language identifier.

“plain” isn’t commonly recognized; prefer “text” (or omit).

Apply this diff:

-```plain +```text coins = [5,7,1,1,2,3,22]

@@
-plain +text
20


1-7: Add a brief approach and complexity section.

Two short lines for method (greedy with sorted coins) and time/space (O(n log n), O(1) or O(n) if not mutating) will help readers.

algorithms/arrays/non_constructible_change/test_non_constructible_change.py (1)

90-99: Add a test asserting the input list isn’t mutated.

Guards against accidental in-place sorting.

Apply this diff:

 def test_13(self): """should return 29 for input of [1, 2, 3, 4, 5, 6, 7]""" coins = [1, 2, 3, 4, 5, 6, 7] expected = 29 actual = non_constructible_change(coins) self.assertEqual(actual, expected) + def test_input_not_mutated(self): + """should not mutate the input coins list""" + coins = [5, 7, 1, 1, 2, 3, 22] + snapshot = coins[:] + _ = non_constructible_change(coins) + self.assertEqual(coins, snapshot) + if __name__ == '__main__': unittest.main()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ced3110 and ddd9989.

📒 Files selected for processing (4)
  • DIRECTORY.md (1 hunks)
  • algorithms/arrays/non_constructible_change/README.md (1 hunks)
  • algorithms/arrays/non_constructible_change/__init__.py (1 hunks)
  • algorithms/arrays/non_constructible_change/test_non_constructible_change.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
algorithms/arrays/non_constructible_change/test_non_constructible_change.py (1)
algorithms/arrays/non_constructible_change/__init__.py (1)
  • non_constructible_change (4-17)
🪛 markdownlint-cli2 (0.17.2)
DIRECTORY.md

12-12: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)


13-13: Unordered list indentation
Expected: 4; Actual: 6

(MD007, ul-indent)

🔇 Additional comments (2)
algorithms/arrays/non_constructible_change/__init__.py (1)

4-17: Algorithm is correct and minimal.

Greedy logic and boundary handling look good.

algorithms/arrays/non_constructible_change/test_non_constructible_change.py (1)

5-95: Good breadth of cases.

Coverage across empties, singles, duplicates, and large gaps is solid.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
algorithms/arrays/non_constructible_change/test_non_constructible_change.py (2)

5-96: Reduce duplication with table-driven subTests.

Same coverage, smaller surface area, easier to extend.

 class NonConstructibleChangeTestCase(unittest.TestCase): - def test_1(self): - """should return 20 for input of [5, 7, 1, 1, 2, 3, 22]""" - coins = [5, 7, 1, 1, 2, 3, 22] - expected = 20 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_2(self): - """should return 6 for input of [1, 1, 1, 1, 1]""" - coins = [1, 1, 1, 1, 1] - expected = 6 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_3(self): - """should return 55 for input of [1, 5, 1, 1, 1, 10, 15, 20, 100]""" - coins = [1, 5, 1, 1, 1, 10, 15, 20, 100] - expected = 55 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_4(self): - """should return 3 for input of [6, 4, 5, 1, 1, 8, 9]""" - coins = [6, 4, 5, 1, 1, 8, 9] - expected = 3 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_5(self): - """should return 1 for input of []""" - coins = [] - expected = 1 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_6(self): - """should return 1 for input of [87]""" - coins = [87] - expected = 1 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_7(self): - """should return 32 for input of [5, 6, 1, 1, 2, 3, 4, 9]""" - coins = [5, 6, 1, 1, 2, 3, 4, 9] - expected = 32 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_8(self): - """should return 19 for input of [5, 6, 1, 1, 2, 3, 43]""" - coins = [5, 6, 1, 1, 2, 3, 43] - expected = 19 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_9(self): - """should return 3 for input of [1, 1]""" - coins = [1, 1] - expected = 3 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_10(self): - """should return 1 for input of [2]""" - coins = [2] - expected = 1 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_11(self): - """should return 2 for input of [1]""" - coins = [1] - expected = 2 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_12(self): - """should return 87 for input of [109, 2000, 8765, 19, 18, 17, 16, 8, 1, 1, 2, 4]""" - coins = [109, 2000, 8765, 19, 18, 17, 16, 8, 1, 1, 2, 4] - expected = 87 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) - - def test_13(self): - """should return 29 for input of [1, 2, 3, 4, 5, 6, 7]""" - coins = [1, 2, 3, 4, 5, 6, 7] - expected = 29 - actual = non_constructible_change(coins) - self.assertEqual(actual, expected) + def test_cases(self): + cases = [ + ([5, 7, 1, 1, 2, 3, 22], 20), + ([1, 1, 1, 1, 1], 6), + ([1, 5, 1, 1, 1, 10, 15, 20, 100], 55), + ([6, 4, 5, 1, 1, 8, 9], 3), + ([], 1), + ([87], 1), + ([5, 6, 1, 1, 2, 3, 4, 9], 32), + ([5, 6, 1, 1, 2, 3, 43], 19), + ([1, 1], 3), + ([2], 1), + ([1], 2), + ([109, 2000, 8765, 19, 18, 17, 16, 8, 1, 1, 2, 4], 87), + ([1, 2, 3, 4, 5, 6, 7], 29), + ] + for coins, expected in cases: + with self.subTest(coins=coins): + self.assertEqual(non_constructible_change(coins), expected)

5-95: Clarify input contract; consider tests for invalid coins.

If zeros/negatives are invalid, add tests asserting ValueError (or document allowed domain). If allowed, add cases with 0 to ensure behavior remains correct.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ddd9989 and 8549713.

📒 Files selected for processing (3)
  • algorithms/arrays/non_constructible_change/README.md (1 hunks)
  • algorithms/arrays/non_constructible_change/__init__.py (1 hunks)
  • algorithms/arrays/non_constructible_change/test_non_constructible_change.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • algorithms/arrays/non_constructible_change/README.md
  • algorithms/arrays/non_constructible_change/init.py
🧰 Additional context used
🧬 Code graph analysis (1)
algorithms/arrays/non_constructible_change/test_non_constructible_change.py (1)
algorithms/arrays/non_constructible_change/__init__.py (1)
  • non_constructible_change (4-28)
🔇 Additional comments (2)
algorithms/arrays/non_constructible_change/test_non_constructible_change.py (2)

97-103: Nice: explicit non-mutation test.

This protects the contract around input immutability.


1-3: List import and usage verified; no changes needed.

@@ -0,0 +1,105 @@
import unittest
from . import non_constructible_change
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use absolute import so tests work via discovery and when run as a script.

The relative import breaks if this file is executed directly (your main block implies that use). Prefer an absolute import.

-from . import non_constructible_change +from algorithms.arrays.non_constructible_change import non_constructible_change
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from . import non_constructible_change
from algorithms.arrays.non_constructible_change import non_constructible_change
🤖 Prompt for AI Agents
In algorithms/arrays/non_constructible_change/test_non_constructible_change.py around line 2, the test uses a relative import which fails when the file is run directly; change the import to an absolute import using the full package path (algorithms.arrays.non_constructible_change) so test discovery and direct execution both work; update the import statement accordingly and run the test file directly to verify it imports correctly. 
@BrianLusina BrianLusina merged commit 3c5ac87 into main Sep 9, 2025
6 of 8 checks passed
@BrianLusina BrianLusina deleted the feat/algorithms-non-constructible-change branch September 9, 2025 08:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Algorithm Algorithm Problem Array Array data structure

2 participants