Skip to content
Merged
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
65 changes: 61 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ on:
branches: [ 'main', 'develop', 'feature/**', 'hotfix/**', 'bugfix/**' ]

permissions:
contents: read
packages: read
contents: write
packages: write
statuses: write # To report GitHub Actions status checks
actions: read # Needed for detection of GitHub Actions environment.
actions: write # Needed for detection of GitHub Actions environment.
id-token: write # Needed for provenance signing and ID.
pull-requests: write

Expand Down Expand Up @@ -73,4 +73,61 @@ jobs:
# if: steps.pmd.outputs.violations == 0
# uses: github/codeql-action/upload-sarif@v3
# with:
# sarif_file: pmd-report.sarif
# sarif_file: pmd-report.sarif
coverage:
name: JaCoCo Code Coverage Gate
runs-on: ubuntu-latest
permissions: write-all
steps:
- name: Checkout sources
uses: actions/checkout@v5
with:
clean: 'true'

- name: Set up JDK 21
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
cache: maven
architecture: x64

- name: Generate Coverage Report
run: |
mvn -B clean install test --file pom.xml

# - name: Upload Report
# uses: actions/upload-artifact@v3
# with:
# name: jacoco-report
# path: ${{ github.workspace }}/target/site/jacoco/jacoco.xml

- name: Add coverage to PR
id: jacoco
uses: madrapps/jacoco-report@v1.7.2
with:
paths: ${{ github.workspace }}/target/site/jacoco/jacoco.xml
token: ${{ secrets.GITHUB_TOKEN }}
min-coverage-overall: 80
min-coverage-changed-files: 80
title: Code Coverage
comment-type: 'pr_comment'

- name: Save Coverage To Environment Variable
run: |
echo "TOTAL_COVERAGE=${{ steps.jacoco.outputs.coverage-overall }}" >> $GITHUB_ENV
echo "CHANGED_FILES_COVERAGE=${{ steps.jacoco.outputs.coverage-changed-files }}" >> $GITHUB_ENV

- name: Print & Check Coverage Info
run: |
import os
import sys
total_coverage = os.environ["TOTAL_COVERAGE"]
changed_files_coverage = os.environ["CHANGED_FILES_COVERAGE"]
print(f"Total Coverage: {total_coverage}")
print(f"Changed Files Coverage: {changed_files_coverage}")
if float(total_coverage) < 80 or float(changed_files_coverage) < 80:
print("Insufficient Coverage!")
sys.exit(-1) # Cause Status Check Failure due to noncompliant coverage
sys.exit(0)
shell: python
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ PrimeKit Coding Challenges
- [LeetCode](https://leetcode.com)
- [HackerRank](https://hackerrank.com)
- [Codility](https://app.codility.com)
- [HackerEarth](https://www.hackerearth.com/)

![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/shortthirdman/PrimeKit-Challenges) ![GitHub repo size](https://img.shields.io/github/repo-size/shortthirdman/PrimeKit-Challenges)

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@
<executions>
<execution>
<id>attach-javadocs</id>
<phase>package</phase>
<phase>install</phase>
<goals>
<goal>jar</goal>
<goal>javadoc</goal>
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/shortthirdman/primekit/common/ListNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.shortthirdman.primekit.common;

public class ListNode {

public int value;
public ListNode next;

public ListNode() {
}

public ListNode(int value) {
this.value = value;
}

public ListNode(int value, ListNode next) {
this.value = value;
this.next = next;
}

// Convenience method for testing
public static ListNode of(int... values) {
ListNode dummy = new ListNode(0);
ListNode current = dummy;
for (int v : values) {
current.next = new ListNode(v);
current = current.next;
}
return dummy.next;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
ListNode curr = this;
while (curr != null) {
sb.append(curr.value);
if (curr.next != null) sb.append("->");
curr = curr.next;
}
return sb.toString();
}
}
99 changes: 99 additions & 0 deletions src/main/java/com/shortthirdman/primekit/hackerearth/JumpGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.shortthirdman.primekit.hackerearth;

import java.util.stream.IntStream;

/**
* @author ShortThirdMan
* @since 1.1.0
*/
public class JumpGame {

/**
* Calculates the minimum number of jumps required to reach the last index from index 0.
* <p>
* Each element in the array represents the maximum jump length from that position.
* Uses a greedy approach by expanding the current jump range and counting jumps
* whenever the current range is exhausted.
* </p>
*
* @param nums an integer array where each element represents maximum jump length from that index
* @return the minimum number of jumps needed to reach the last index
* @throws IllegalArgumentException if:
* <ul>
* <li>{@code nums} is {@code null}</li>
* <li>length of {@code nums} is not in [1, 10^4]</li>
* <li>any element in {@code nums} is not in [0, 1000]</li>
* </ul>
*/
public int jump(int[] nums) {
// Constraint checks
if (nums == null || nums.length < 1 || nums.length > 10_000) {
throw new IllegalArgumentException("Array length must be between 1 and 10^4");
}

boolean invalidValue = IntStream.of(nums)
.anyMatch(num -> num < 0 || num > 1_000);
if (invalidValue) {
throw new IllegalArgumentException("Array elements must be between 0 and 1000");
}

if (nums.length == 1) return 0; // already at last index

int jumps = 0;
int currentEnd = 0; // boundary of the current jump
int farthest = 0; // farthest index we can reach

for (int i = 0; i < nums.length - 1; i++) {
farthest = Math.max(farthest, i + nums[i]);

if (i == currentEnd) { // time to make another jump
jumps++;
currentEnd = farthest;
}
}

return jumps;
}

/**
* Determines if it is possible to reach the last index of the array starting from index 0.
* <p>
* Each element in the array represents the maximum jump length from that position.
* Uses a greedy approach to keep track of the furthest reachable index at every step.
* </p>
*
* @param nums an integer array where each element represents maximum jump length from that index
* @return {@code true} if it is possible to reach the last index, otherwise {@code false}
* @throws IllegalArgumentException if:
* <ul>
* <li>{@code nums} is {@code null}</li>
* <li>length of {@code nums} is not in [1, 10^4]</li>
* <li>any element in {@code nums} is not in [0, 10^5]</li>
* </ul>
*/
public boolean canJump(int[] nums) {
if (nums == null || nums.length < 1 || nums.length > 10_000) {
throw new IllegalArgumentException("Array length must be between 1 and 10^4");
}

boolean invalidValue = IntStream.of(nums)
.anyMatch(num -> num < 0 || num > 100_000);
if (invalidValue) {
throw new IllegalArgumentException("Array elements must be between 0 and 10^5");
}

// If there's only one element, we are already at the last index
if (nums.length == 1) return true;

int maxReach = 0;

for (int i = 0; i < nums.length && i <= maxReach; i++) {
maxReach = Math.max(maxReach, i + nums[i]);
if (maxReach >= nums.length - 1) {
return true;
}
}

return false;
}
}
Loading