Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
* A utility class to find the non-repeating number in an array where every other number repeats.
* This class contains a method to identify the single unique number using bit manipulation.
*
* The solution leverages the properties of the XOR operation, which states that:
* - x ^ x = 0 for any integer x (a number XORed with itself is zero)
* - x ^ 0 = x for any integer x (a number XORed with zero is the number itself)
* <p>The solution leverages the properties of the XOR operation:
* <ul>
* <li>{@code x ^ x = 0} for any integer x (self-inverse property)</li>
* <li>{@code x ^ 0 = x} for any integer x (identity property)</li>
* <li>Associative and commutative: ordering of inputs does not affect the final result</li>
* </ul>
*
* Using these properties, we can find the non-repeating number in linear time with constant space.
*
* Example:
* Given the input array [2, 3, 5, 2, 3], the output will be 5 since it does not repeat.
* <p>Example:
* Given the input array {@code [2, 3, 5, 2, 3]}, the output will be {@code 5} since it does not repeat.
*
* @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi)
*/
Expand All @@ -26,10 +27,22 @@ private NonRepeatingNumberFinder() {
* @return the integer that appears only once in the array or 0 if the array is empty
*/
public static int findNonRepeatingNumber(int[] arr) {
int result = 0;
return reduceXor(arr);
}

/**
* Reduces the array by sequentially accumulating bitwise XOR on each element.
* Paired duplicate values cancel each other out to zero, leaving the lone non-repeating value
* or 0 if the array is empty.
*
* @param arr the input array of integers
* @return the XOR-accumulated sum of all elements
*/
private static int reduceXor(int[] arr) {
int xorSum = 0;
for (int num : arr) {
result ^= num;
xorSum ^= num;
}
return result;
return xorSum;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.thealgorithms.bitmanipulation;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.lang.reflect.Constructor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

/**
* Test case for Non Repeating Number Finder
* This test class validates the functionality of the
* NonRepeatingNumberFinder by checking various scenarios.
* Test case for Non Repeating Number Finder.
* Validates the functionality and refactored XOR reduction responsibility.
*
* @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi)
*/
Expand All @@ -23,14 +26,102 @@ void testNonRepeatingNumberFinder(int[] arr, int expected) {

private static Arguments[] testCases() {
return new Arguments[] {
Arguments.of(new int[] {1, 2, 1, 2, 6}, 6), Arguments.of(new int[] {1, 2, 1, 2}, 0), // All numbers repeat
Arguments.of(new int[] {1, 2, 1, 2, 6}, 6),
Arguments.of(new int[] {1, 2, 1, 2}, 0), // All numbers repeat
Arguments.of(new int[] {12}, 12), // Single non-repeating number
Arguments.of(new int[] {3, 5, 3, 4, 4}, 5), // More complex case
Arguments.of(new int[] {7, 8, 7, 9, 8, 10, 10}, 9), // Non-repeating in the middle
Arguments.of(new int[] {0, -1, 0, -1, 2}, 2), // Testing with negative numbers
Arguments.of(new int[] {Integer.MAX_VALUE, 1, 1}, Integer.MAX_VALUE), // Edge case with max int
Arguments.of(new int[] {2, 2, 3, 3, 4, 5, 4}, 5), // Mixed duplicates
Arguments.of(new int[] {}, 0) // Edge case: empty array (should be handled as per design)
Arguments.of(new int[] {}, 0) // Edge case: empty array
};
}

/**
* Smallest valid input fixture: empty array.
* Preconditions established at:
* - NonRepeatingNumberFinder.java line 26 (@return ... or 0 if the array is empty)
* - NonRepeatingNumberFinder.java line 39 (xorSum initialized to 0)
*/
@Test
void testSmallestValidInputEmptyArrayFixture() {
int[] arr = new int[] {};
int expected = 0;

int actual = NonRepeatingNumberFinder.findNonRepeatingNumber(arr);
assertEquals(expected, actual, "Empty array must return 0 per documented specification.");
}

/**
* Smallest valid non-empty input fixture: single element array.
* Preconditions established at:
* - NonRepeatingNumberFinder.java line 29 (findNonRepeatingNumber delegates to reduceXor)
* - NonRepeatingNumberFinder.java lines 39-43 (single loop iteration: 0 ^ 12 = 12)
*/
@Test
void testSmallestValidInputSingleElementFixture() {
int[] arr = new int[] {12};
int expected = 12;

int actual = NonRepeatingNumberFinder.findNonRepeatingNumber(arr);
assertEquals(expected, actual, "Single-element array must return the element itself.");
}

/**
* Ordinary input fixture: [2, 3, 5, 2, 3] from class documentation example.
* Step-by-step XOR reduction trace:
* 0 ^ 2 = 2
* 2 ^ 3 = 1
* 1 ^ 5 = 4
* 4 ^ 2 = 6
* 6 ^ 3 = 5
* Preconditions established at:
* - NonRepeatingNumberFinder.java lines 29-31
* - NonRepeatingNumberFinder.java lines 39-44 (reduceXor loop)
*/
@Test
void testOrdinaryInputDocumentationFixture() {
int[] arr = new int[] {2, 3, 5, 2, 3};
int expected = 5;

int actual = NonRepeatingNumberFinder.findNonRepeatingNumber(arr);
assertEquals(expected, actual, "Must return 5 as the unique non-repeating element.");
}

/**
* Domain boundary fixture: negative numbers and cancellation.
* Preconditions established at:
* - NonRepeatingNumberFinder.java lines 39-43
*/
@Test
void testDomainBoundaryNegativeNumbers() {
int[] arr = new int[] {-42, 99, -42};
int expected = 99;

assertEquals(expected, NonRepeatingNumberFinder.findNonRepeatingNumber(arr));
}

/**
* Boundary fixture: null input precondition.
* Preconditions established at:
* - NonRepeatingNumberFinder.java line 40 (looping over null array triggers NullPointerException)
*/
@Test
void testNullArrayThrowsNullPointerException() {
assertThrows(NullPointerException.class, () -> NonRepeatingNumberFinder.findNonRepeatingNumber(null));
}

/**
* Reflection fixture for private constructor contract.
* Preconditions established at:
* - NonRepeatingNumberFinder.java lines 19-20
*/
@Test
void testPrivateConstructorContract() throws Exception {
Constructor<NonRepeatingNumberFinder> constructor = NonRepeatingNumberFinder.class.getDeclaredConstructor();
constructor.setAccessible(true);
NonRepeatingNumberFinder instance = constructor.newInstance();
assertNotNull(instance);
}
}
192 changes: 192 additions & 0 deletions work/R45-T006/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#!/usr/bin/env python3
"""
Focused runner and refactoring verification check for R45-T006:
Simplify one responsibility in NonRepeatingNumberFinder (NonRepeatingNumberFinder.java).
"""

import json
import os
import re
import subprocess
import sys
from pathlib import Path


def ensure_environment():
java_bin_dir = "/opt/homebrew/opt/openjdk@21/bin"
java_home = "/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
current_path = os.environ.get("PATH", "")
if java_bin_dir not in current_path:
os.environ["PATH"] = f"{java_bin_dir}:/opt/homebrew/bin:{current_path}"
if "JAVA_HOME" not in os.environ or not os.environ["JAVA_HOME"]:
os.environ["JAVA_HOME"] = java_home


def get_tool_versions():
ensure_environment()
versions = {}

# Python version
versions["python"] = sys.version.replace("\n", " ")

# Java version
try:
res = subprocess.run(
["java", "-version"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
versions["java"] = (res.stderr or res.stdout).strip().splitlines()[0]
except Exception as e:
versions["java"] = f"Error: {e}"

# Maven version
try:
res = subprocess.run(
["mvn", "-version"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=True
)
versions["maven"] = res.stdout.strip().splitlines()[0]
except Exception as e:
versions["maven"] = f"Error: {e}"

return versions


def run_checks():
work_dir = Path(__file__).resolve().parent
repo_root = work_dir.parent.parent

print(f"[*] Running R45-T006 refactoring verification in {repo_root}")

tool_versions = get_tool_versions()
print(f"[*] Python: {tool_versions.get('python')}")
print(f"[*] Java: {tool_versions.get('java')}")
print(f"[*] Maven: {tool_versions.get('maven')}")

test_cmd = ["mvn", "test", "-Dtest=NonRepeatingNumberFinderTest"]
actual_command_str = " ".join(test_cmd)
print(f"[*] Executing native check: {actual_command_str}")

proc = subprocess.run(
test_cmd,
cwd=repo_root,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)

combined_output = proc.stdout + "\n" + proc.stderr

# Parse JUnit test count and results
match = re.search(r"Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)", combined_output)
if not match:
print("[!] Failed to locate JUnit test execution summary in maven output!")
print(combined_output)
sys.exit(1)

tests_run = int(match.group(1))
failures = int(match.group(2))
errors = int(match.group(3))
skipped = int(match.group(4))

print(f"[*] Test results: Tests run={tests_run}, Failures={failures}, Errors={errors}, Skipped={skipped}")

if tests_run == 0:
print("[!] Non-zero test count required, but found 0!")
sys.exit(1)

if failures != 0 or errors != 0 or proc.returncode != 0:
print(f"[!] Test failures or errors detected (exit code {proc.returncode})!")
sys.exit(1)

refactoring_fixtures = [
{
"name": "Smallest Valid Input (Empty Array)",
"precondition_lines": "NonRepeatingNumberFinder.java:26 (@return ... or 0 if array is empty), 39 (xorSum = 0)",
"literal_inputs": "new int[] {}",
"expected_results": "0",
"actual_results": "0; passed in testSmallestValidInputEmptyArrayFixture"
},
{
"name": "Smallest Valid Input (Single Element)",
"precondition_lines": "NonRepeatingNumberFinder.java:28-30, 39-43 (single loop iteration: 0 ^ 12 = 12)",
"literal_inputs": "new int[] {12}",
"expected_results": "12",
"actual_results": "12; passed in testSmallestValidInputSingleElementFixture"
},
{
"name": "Ordinary Input (Documentation Example)",
"precondition_lines": "NonRepeatingNumberFinder.java:28-30, 39-44 (reduceXor loop)",
"literal_inputs": "new int[] {2, 3, 5, 2, 3}",
"expected_results": "5 (pairwise cancellation of 2 and 3)",
"actual_results": "5; passed in testOrdinaryInputDocumentationFixture"
},
{
"name": "Domain Boundary (Negative Numbers)",
"precondition_lines": "NonRepeatingNumberFinder.java:39-43",
"literal_inputs": "new int[] {-42, 99, -42}",
"expected_results": "99",
"actual_results": "99; passed in testDomainBoundaryNegativeNumbers"
},
{
"name": "Boundary Precondition (Null Input)",
"precondition_lines": "NonRepeatingNumberFinder.java:40 (for-each over null throws NullPointerException)",
"literal_inputs": "null",
"expected_results": "NullPointerException",
"actual_results": "NullPointerException thrown; passed in testNullArrayThrowsNullPointerException"
}
]

result_data = {
"task": "R45-T006",
"target_file": "src/main/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinder.java",
"test_file": "src/test/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinderTest.java",
"refactored_responsibility": "Extracted reduceXor(int[] arr) method with dedicated xorSum accumulator separating the public query entry point from bitwise XOR reduction.",
"tool_versions": tool_versions,
"actual_command": actual_command_str,
"nonzero_test_count": tests_run,
"failures": failures,
"errors": errors,
"skipped": skipped,
"status": "SUCCESS",
"fixtures": refactoring_fixtures
}

results_path = work_dir / "results.json"
with open(results_path, "w", encoding="utf-8") as f:
json.dump(result_data, f, indent=2)
print(f"[*] Saved test results to {results_path}")

evidence_path = work_dir / "evidence.md"
with open(evidence_path, "w", encoding="utf-8") as f:
f.write("# Verification Evidence for R45-T006\n\n")
f.write("- **Target**: `src/main/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinder.java`\n")
f.write("- **Test Suite**: `src/test/java/com/thealgorithms/bitmanipulation/NonRepeatingNumberFinderTest.java`\n")
f.write(f"- **Command**: `{actual_command_str}`\n")
f.write(f"- **Tests Run**: {tests_run} (Failures: {failures}, Errors: {errors}, Skipped: {skipped})\n")
f.write("- **Status**: SUCCESS\n")
f.write("- **Refactored Responsibility**: Extracted `reduceXor(int[] arr)` isolating bitwise XOR accumulation.\n\n")
f.write("## Tool Versions\n\n")
for tool, ver in tool_versions.items():
f.write(f"- **{tool.capitalize()}**: `{ver}`\n")
f.write("\n## Fixtures and Regression Verification\n\n")
for fix in refactoring_fixtures:
f.write(f"### {fix['name']}\n")
f.write(f"- **Precondition Lines**: {fix['precondition_lines']}\n")
f.write(f"- **Literal Inputs**: `{fix['literal_inputs']}`\n")
f.write(f"- **Expected Results**: {fix['expected_results']}\n")
f.write(f"- **Actual Results**: {fix['actual_results']}\n\n")
print(f"[*] Saved evidence to {evidence_path}")

print("[*] All checks passed successfully.")
sys.exit(0)


if __name__ == "__main__":
run_checks()
Loading