Ngoding Devlog and Coding Tutorials

website·Indexed 32 days ago

https://ngoding.id/posts/

A technical blog featuring coding challenges, data structures and algorithms (DSA) tutorials, and development logs. Content includes the Hyperskills 12 Days of Coding challenges, Kotlin reflection and annotations, SQL basics, CUDA environment setup, and Google Cloud IAM configuration.

Tokens
10.2K
Snippets
42
Records
58
Agent score
62%

What's inside Ngoding

  1. Control FiiO KA13 DAC on Linux via USB HID

    Since the vendor only provides Windows drivers for the FiiO KA13 DAC, Linux control can be achieved by reverse engineering the USB HID protocol. This allows for the creation of custom control utilities (e.g., fiio-ka13-control) on Linux systems.
  2. Understand Kotlin Annotation basics

    Annotations in Kotlin provide metadata to the compiler, IDE, or frameworks (such as Room, Retrofit, or Spring Boot) to handle complex tasks. They are denoted by the @ symbol and are placed either directly above or immediately before the target element. Annotations apply only to the closest subsequent element; if multiple annotations are needed for a single target, they can be stacked.
    // Applied directly above
    @MyAnnotation
    class MyClass {}
    
    // Applied before the code line
    @MyAnnotation class MyClass {}
    
    // Multiple annotations on one target
    @NotNull
    @PrimaryKey(autoGenerate = true)
    var taskId: Long = 0L
  3. Understand the basics of the searching problem in programming

    In programming, searching is the process of retrieving desired data from a collection (data structure). There are two primary goals when searching:

    1. Value Search: Determining if a specific value exists within a list or array.
    2. Location Search: Finding the specific site where the data is held, such as an array index or a memory address.
  4. Install NVIDIA graphics drivers on openSUSE Tumbleweed

    To install NVIDIA drivers on openSUSE Tumbleweed, first add the NVIDIA repository. Depending on your GPU architecture, choose the appropriate driver package. For CUDA development, the latest driver version (G06) is required. For Turing architecture and newer, the Open GPU kernel modules are recommended.
    # Add the NVIDIA repository
    sudo zypper install openSUSE-repos-Tumbleweed-NVIDIA
    
    # For Maxwell and Pascal GPUs
    sudo zypper in nvidia-video-G06 nvidia-gl-G06 nvidia-compute-utils-G06 nvidia-libXNVCtrl nvidia-settings
    
    # For Turing and newer GPUs (using Open GPU kernel modules)
    sudo zypper in nvidia-open-driver-G06-signed-cuda-kmp-default nvidia-gl-G06 nvidia-compute-utils-G06 nvidia-libXNVCtrl nvidia-settings
  5. Find the Longest Common Substring across multiple strings

    To find the longest contiguous sequence present in all $K$ strings (Generalized Longest Common Substring), a practical approach for a small number of strings is to use a reference pattern. By iterating through all possible substrings of the first string from longest to shortest, you can verify if a candidate substring exists in all other strings. The first match found is guaranteed to be the maximum length, allowing for an early return.
    import java.io.File
    
    fun main() {
        // 1. Read input and remove delimiters (e.g., commas)
        val patterns = File("dataset.txt").readLines().map { it.replace(",", "") }
        
        // 2. Use the first pattern as the reference
        val reference = patterns[0]
        val others = patterns.drop(1)
        
        // 3. Iterate length from largest to smallest to return early
        for (length in reference.length downTo 1) {
            for (startIndex in 0..reference.length - length) {
                val substring = reference.substring(startIndex, startIndex + length)
                
                // 4. Check if this substring exists in ALL other patterns
                val existsInAll = others.all { it.contains(substring) }
                
                if (existsInAll) {
                    println(substring.length)
                    return // Found the longest possible, exit immediately
                }
            }
        }
        
        println(0) // No common substring found
    }
  6. Use Interval Search for sorted data

    Interval search is an efficient searching strategy used when data is sorted or follows a specific pattern. Instead of checking every element, interval search narrows down the search area based on criteria (e.g., discarding half of the remaining possibilities if the target is higher or lower than the current guess), making it significantly faster than sequential search.
  7. Retrieve values using SELECT and aliases

    The SELECT statement can return a set of values (a tuple). You can use the AS keyword to provide an alias for the resulting column. SQL is case-insensitive, but keywords are typically written in uppercase for readability.
    SELECT
        'Kotlin' AS name,
        2025 AS year,
        123*1 AS "weight in kilos"
    ;
  8. Modify linear search for specific search requirements

    The basic linear search algorithm can be adapted to handle different use cases:

    • Existence Check: Return a boolean (true/false) instead of an index.
    • Occurrence Tracking: Modify the loop to find the first occurrence, the last occurrence, or count all occurrences of a value.
    • Collection: Return a list of all indices where the value occurs.
    • Subarray Search: Limit the loop range to specific start and end indices.
    • Sorted Array Optimization: If the array is sorted (e.g., ascending), the search can stop early if the current element exceeds the target value, as the target cannot exist in the remainder of the array.
  9. Calculate minimum additional crossings for an Eulerian Path

    To determine if a network of bidirectional bridges can be traversed by crossing every bridge exactly once (an Eulerian Path), count the number of vertices with an odd degree.

    • If the number of odd vertices is 0 or 2, an Eulerian Path exists, and 0 additional crossings are required.
    • If there are more than 2 odd vertices (K), additional edges must be added to reduce the odd count to 2. Each added edge reduces the odd count by 2.

    The formula for minimum additional crossings is: (OddCount - 2) / 2.

    import java.io.File
    
    fun main() {
        val degrees = mutableMapOf<String, Int>()
        
        // 1. Build the graph degrees from the edge list (format: LandMass1,LandMass2)
        File("dataset.txt").forEachLine { line ->
            val (u, v) = line.split(",")
            degrees[u] = degrees.getOrDefault(u, 0) + 1
            degrees[v] = degrees.getOrDefault(v, 0) + 1
        }
    
        // 2. Count vertices with odd degrees
        val oddVertices = degrees.values.count { it % 2 != 0 }
        
        // 3. Calculate minimum extra crossings needed
        val extraCrossings = if (oddVertices <= 2) {
            0
        } else {
            (oddVertices - 2) / 2
        }
        
        println(extraCrossings)
    }
  10. Implement Dijkstra's Algorithm for a 2D grid pathfinding problem

    To find the path with the minimum cost (fewest lives lost) in a 2D grid where each cell has a weight (hazard level), use Dijkstra's Algorithm with a Priority Queue. The algorithm ensures that the first time the destination cell is reached, the path taken is the absolute minimum cost.

    Key implementation details:

    • Priority Queue: Stores the current total cost and coordinates, ordered by the lowest cost first.
    • Distance Array: A 2D array (minCosts) initialized to infinity to track the cheapest known cost to reach each cell.
    • Movement: Explore neighbors in four directions (Up, Down, Left, Right).
    • Starting Point: The initial cost includes the hazard level of the starting cell (0,0).
    import java.io.File
    import java.util.PriorityQueue
    
    fun main() {
        // 1. Parse the grid (20x20 comma-separated values)
        val grid = File("dataset.txt").readLines().map { line ->
            line.split(",").map { it.toInt() }
        }
        val rows = grid.size
        val cols = grid[0].size
    
        // 2. Dijkstra's Algorithm
        // PriorityQueue stores Triple(TotalCost, Row, Col), ordered by Cost
        val pq = PriorityQueue<Triple<Int, Int, Int>>(compareBy { it.first })
        
        // Distance array to track the cheapest way to reach each cell found so far
        val minCosts = Array(rows) { IntArray(cols) { Int.MAX_VALUE } }
    
        // Start at (0,0). Cost includes the starting cell.
        minCosts[0][0] = grid[0][0]
        pq.add(Triple(grid[0][0], 0, 0))
    
        val directions = arrayOf(-1 to 0, 1 to 0, 0 to -1, 0 to 1) // Up, Down, Left, Right
    
        while (pq.isNotEmpty()) {
            val (currentCost, r, c) = pq.poll()
    
            // If we reached the target (bottom-right), we are done
            if (r == rows - 1 && c == cols - 1) {
                println(currentCost)
                return
            }
    
            // Optimization: If we found a cheaper way to this cell already, skip
            if (currentCost > minCosts[r][c]) continue
    
            // Check neighbors
            for ((dr, dc) in directions) {
                val nr = r + dr
                val nc = c + dc
    
                if (nr in 0 until rows && nc in 0 until cols) {
                    val newCost = currentCost + grid[nr][nc]
                    
                    if (newCost < minCosts[nr][nc]) {
                        minCosts[nr][nc] = newCost
                        pq.add(Triple(newCost, nr, nc))
                    }
                }
            }
        }
    }
  11. Project data from a table using SELECT FROM

    Use SELECT combined with FROM to extract specific columns (projection) from a logical table. You can perform calculations or logical comparisons directly within the SELECT list to create derived columns.
    SELECT
        'London' AS place,
        day, 
        hour,
        phenomena,
        temperature*9/5+32 AS "temperature in Fahrenheit",
        feels_like < temperature AS "feels colder",
        wind_speed AS "wind speed in m/s"
    FROM
        weather
    ;
  12. Count resource contentions from logs using state tracking

    To count contentions (instances where a resource is requested while already held), implement a state tracking mechanism. Use a mutable map or boolean array to track the 'held' status of each resource. When processing logs: if a 'pick' action occurs and the resource is already marked as held, increment the contention counter and do not change the state. If the resource is free, mark it as held. When a 'release' action occurs, mark the resource as free.
    import java.io.File
    
    fun main() {
        val forksHeld = BooleanArray(5) // Index 1..4 for forks (0 unused)
        var contentions = 0
    
        File("dataset.txt").forEachLine { line ->
            // Format: Name,action,forkId
            val parts = line.split(",")
            val action = parts[1]
            val forkId = parts[2].toInt()
    
            if (action == "pick") {
                if (forksHeld[forkId]) {
                    // Fork is already held by someone else -> Contention!
                    contentions++
                } else {
                    // Fork is free -> Elf successfully picks it up
                    forksHeld[forkId] = true
                }
            } else if (action == "release") {
                // Elf puts the fork down
                forksHeld[forkId] = false
            }
        }
    
        println(contentions)
    }