Category: Data & Systems

  • Ladder Logic Symbols Explained: A Practical PLC Reference

    Ladder Logic Symbols Explained: A Practical PLC Reference

    Ladder logic symbols form a visual language for PLC programs. Read each rung from the left rail to the right: contacts and instruction blocks evaluate conditions, while coils write results. A rung is logically true when its path has continuity from the left rail to the output instruction.

    The most useful reference separates ladder diagram symbols by their job: carrying conditions, creating output actions, or changing a path based on time, counts, or values.

    Ladder Logic Symbols: How Rails, Rungs, and Branches Carry Logic

    Vertical rails represent the two sides of the control circuit, and each horizontal rung represents one logic statement. The left rail is the logical starting point; the right side usually contains an output coil or instruction. A PLC scans the program repeatedly, evaluating each rung with the current input and memory values.

    • Series path: Conditions are evaluated in sequence. Every contact in the path must be true for continuity to reach the output, which creates an AND relationship.
    • Parallel branch: Two or more paths provide alternate routes. If any complete branch is true, the output can be reached, creating an OR relationship.
    • Branch connection: A branch starts and rejoins at points on the rung. It can combine permissive conditions, create a seal-in path, or bypass one condition under a defined circumstance.

    Branches do not automatically mean that every instruction runs independently. The PLC evaluates the instructions in their program order, and the resulting Boolean state continues through the connected path.

    Ladder Diagram Symbols: Normally Open and Normally Closed Contacts

    Contacts test a Boolean address, such as a physical input, internal bit, timer status, or counter status. Their drawn appearance describes the instruction’s test, not necessarily the physical position of a field device.

    • Normally open contact: Drawn as –| |–. It evaluates true when its addressed bit is 1 or on, allowing rung continuity. When the bit is 0, the contact is logically open.
    • Normally closed contact: Drawn as –|/|–. It evaluates true when its addressed bit is 0 or off, allowing continuity. When the bit is 1, the contact is logically open.

    For example, an open contact tied to a Start input passes logic while Start is on. A closed contact tied to a Stop input passes logic until Stop becomes on. This distinction matters because a closed contact in the ladder does not mean the monitored device is physically closed; it means the instruction is testing for a false bit.

    Output Coils: Ordinary, Latched, Set, and Reset Actions

    Coils write the result of a rung to an output or internal Boolean address. The common ordinary coil, shown conceptually as –( )–, turns on while the rung is true and turns off when the rung becomes false. It reflects the rung continuously during the scan cycle.

    • Ordinary coil: Directly follows rung continuity. Use it when the output should track its conditions.
    • Latched output: Uses a holding, or seal-in, branch so the output remains on after the initiating contact turns off. A separate break condition removes the holding path.
    • Set coil: Writes the addressed bit on and leaves it on after the rung goes false. A separate reset action is required.
    • Reset coil: Writes the addressed bit off, normally overriding a previously set state or clearing a retained condition.

    Latch, set, and reset behavior can differ in naming and priority across PLC platforms. Treat the instruction’s documented write behavior as authoritative, especially when set and reset commands can be true in the same scan.

    PLC Symbols for Timers, Counters, and Comparison Blocks

    Timer, counter, and comparison instructions do more than represent a simple on-or-off input. They calculate a status that later contacts can test, so they alter when continuity is available.

    • Timer: A timer starts or maintains elapsed-time tracking when its enabling rung is true. A typical on-delay timer turns its Done status on after the preset time expires. A nonretentive timer usually clears its elapsed value when the enabling rung goes false; a retentive timer keeps it until reset.
    • Counter: An up counter increments on qualifying input transitions, while a down counter decrements them. Preset, done, and accumulated-value statuses can control later contacts or outputs. A reset instruction clears the count according to the configured behavior.
    • Comparison block: A block tests values such as greater than, equal to, or less than. Its Boolean result allows a rung to continue only when the comparison is true, such as when tank level is above a limit.

    These are core PLC symbols, but extended instruction shapes and names are not identical across vendors. Identify the instruction by its evaluated inputs, status outputs, preset values, and reset behavior rather than by its graphic alone.

  • Karnaugh Map: Simplify Boolean Expressions Step by Step

    Karnaugh Map: Simplify Boolean Expressions Step by Step

    A Karnaugh map turns truth-table output values into a layout where adjacent cells represent input combinations that differ in only one variable. By grouping neighboring 1s, you remove variables that change within each group and produce a shorter sum-of-products expression.

    The reliable process is: label the map in Gray-code order, copy each truth-table output into its matching cell, form the largest valid groups, and write one product term for each group.

    How is a Karnaugh map arranged in Gray-code order?

    For three variables, use one variable for the rows and two for the columns. Let A label the rows and BC label the columns:

    • Rows: A = 0 and A = 1
    • Columns: BC = 00, 01, 11, 10

    The column labels use Gray-code order. Each neighboring label changes by one bit, including the transition from the last column, 10, back to the first column, 00. Therefore, the left and right edges are adjacent. The same wraparound rule applies to the top and bottom edges in maps with more row variables.

    Cells that touch along an edge are adjacent. Cells that meet only at a corner are diagonal and are not adjacent. This distinction controls which cells may form a group.

    How do you fill Karnaugh maps from a truth table?

    Start with the truth table’s variable order and match each input combination to its map coordinates. Place a 1 where the function output is 1 and a 0 where it is 0. For example, use this three-variable function with 1s at minterms 0, 1, 2, 4, and 6:

    • BC = 00: row A = 0 has 1; row A = 1 has 1
    • BC = 01: row A = 0 has 1; row A = 1 has 0
    • BC = 11: row A = 0 has 0; row A = 1 has 0
    • BC = 10: row A = 0 has 1; row A = 1 has 1

    The resulting rows are therefore 1 1 0 1 for A = 0 and 1 0 0 1 for A = 1. Keep the labels visible while filling the map; using ordinary binary order such as 00, 01, 10, 11 would incorrectly change the adjacency pattern.

    How does K-map simplification use wraparound, overlap, and power-of-two groups?

    Each group must be a rectangle containing only 1s. Its size must be a power of two: 1, 2, 4, 8, or more cells. Groups may span an edge, and a 1 may belong to more than one group when overlap creates larger or simpler terms.

    In this map, group the two outer columns, 00 and 10, across both rows. They are adjacent through horizontal wraparound, creating a four-cell rectangle. Across this group, A changes and B changes, but C remains 0. This group produces C′.

    The remaining 1 at row A = 0, column BC = 01 can pair with the 1 at column 00 in the same row. That two-cell group overlaps the four-cell group. Within it, A remains 0 and B remains 0, while C changes, so it produces A′B′.

    How do you read the simplified Boolean expression?

    For every group, keep only variables whose values stay constant. Write an uncomplemented variable when it remains 1 and a complemented variable when it remains 0. Omit variables that change inside the group. Then join the retained variables within each term with AND and join the group terms with OR.

    For the example:

    F = C′ + A′B′

    The first term covers all cells where C = 0. The second covers the remaining required 1 where A = 0 and B = 0. This is the result of the Karnaugh-map reduction, with each original 1 covered by at least one valid group.

  • How to Clear the Global Environment in R

    How to Clear the Global Environment in R

    Use rm() to remove selected objects or clear the objects listed in R’s global environment. A targeted command is safer when you need to preserve some work; a full rm(list = ls()) clears the ordinary, visible objects in the current environment.

    How to clear the global environment in R with rm(list = ls())

    Run this command at the Global Environment prompt:

    rm(list = ls())

    It removes every object returned by ls() in that environment. Check the contents before and after to confirm the result:

    ls()
    [1] “data” “model” “results”

    rm(list = ls())

    ls()
    character(0)

    The result character(0) means that no visible objects remain. The command is evaluated in the current environment, so run it from the Global Environment if that is the environment you intend to clear.

    By default, ls() does not list hidden names beginning with a period. To include those names in a more comprehensive cleanup, use:

    rm(list = ls(all.names = TRUE))

    Use the extended form only when you also intend to remove hidden objects managed or created in that environment.

    Clear environment in R selectively with rm(object) and rm(list = c(‘data’, ‘model’))

    Remove one object by passing its name to rm():

    object <- 42
    rm(object)

    Verify that the object is gone:

    ls()
    character(0)

    For a real workspace, inspect the names first and remove only the objects you no longer need:

    ls()
    [1] “data” “model” “results”

    rm(list = c(‘data’, ‘model’))

    ls()
    [1] “results”

    The names supplied to rm(list = c(…)) must be character strings. If a name does not exist, R can report an error. Check names with ls() first, or use rm(data, model) when the objects are known to exist.

    Clear data in R: what rm() removes and what it does not

    In R, a data frame, vector, list, function, or fitted model is an object. Removing it deletes its binding from the selected environment:

    rm(data)

    This does not delete an original CSV file, database table, or other external source used to create the object. It also does not remove individual columns from a data frame. To change the data frame itself, assign a revised object or remove a column with a separate operation.

    Removing a large object makes it eligible for memory cleanup, but it does not restart R or necessarily return memory to the operating system immediately. Use targeted removal when you want to keep analysis results, imported data, or model objects.

    Verify with ls() or objects(): what packages, plots, and session state remain?

    objects() is an alternative to ls() for listing objects. Use either command after removal:

    objects()
    character(0)

    Clearing objects does not detach loaded packages. Functions from attached packages remain available, and package namespaces are not unloaded. It also does not close graphics devices: a displayed plot remains on its device until you close it with a graphics command such as dev.off(). A plot stored as an object, however, is removed if its name is included in the rm() call.

    Working-directory settings, options, open connections, and the R session itself also remain. Clearing the global environment is therefore not a complete R session restart. Restarting R is a separate action that ends and starts the session again.

  • 3Sum: Find Unique Triples with Sorting and Two Pointers

    3Sum: Find Unique Triples with Sorting and Two Pointers

    The 3Sum task takes an array of integers and a target value, then returns every value triple whose three numbers add to that target. Each triple must be unique by value, so the same combination is returned once even when duplicate indices can form it.

    The efficient approach sorts the array, fixes one number, and scans the remaining range with two pointers. It reduces the search from cubic time to O(n²), while duplicate skipping preserves the output contract.

    What Does 3Sum Return?

    Define the input, target, and output contract

    Given an integer array nums and an integer target, return a list of triples [a, b, c] such that a + b + c = target. The order inside a triple is normally ascending, and the result can be empty when no combination qualifies.

    Require value-unique triples, not index-unique matches

    Uniqueness applies to values rather than positions. For example, an array containing several copies of -1 must not produce [-1, 0, 1] repeatedly merely because different copies occupy different indices. Sorting makes those repeated values adjacent, which makes them easy to skip.

    Why Is the Three-Sum Problem Expensive to Brute Force?

    Count the cubic search space

    The direct solution tests every combination of three indices. With n values, that is roughly n × (n – 1) × (n – 2) / 6 checks, or O(n³) time. Each check adds three values and compares the sum with the target.

    Use brute force as a correctness baseline

    Brute force is useful for small inputs and test validation because its logic is straightforward. For production-sized arrays, the cubic growth becomes expensive. A set can remove duplicate outputs, but it does not eliminate the cost of examining nearly every triple.

    How the 3Sum algorithm Uses Sorting and Two Pointers

    Sort the array and fix one value

    Sort nums in ascending order. For each index i, treat nums[i] as the first value, set left = i + 1, and set right to the final index. The remaining task is a two-sum search for target – nums[i].

    Move pointers based on the current sum

    • If the three-value sum is too small, increase left to make the sum larger.
    • If the sum is too large, decrease right to make the sum smaller.
    • If the sum matches, record the triple, then move both pointers inward.

    Walk through [-4, -1, -1, 0, 1, 2] with target 0

    Start with -4. The pointers begin at -1 and 2, producing -3, so move left rightward. The next sums are -3, -2, and -1; each is too small, so left continues forward until the scan ends.

    Next, fix -1 at index 1. With the second -1 and 2, the sum is 0, so record [-1, -1, 2]. Move both pointers: 0 and 1 also produce 0, so record [-1, 0, 1]. The next pointer positions cross. The second -1 at index 2 is skipped because it repeats the fixed value. The final result contains those two triples.

    Implement the sorted scan

    1. Sort the input array.
    2. Loop through each possible first index while at least two values remain.
    3. Skip the current index when its value equals the previous fixed value.
    4. Use left and right pointers, calculate the sum, and move the appropriate pointer.
    5. After recording a match, advance past equal left values and retreat past equal right values.

    How Does Duplicate Skipping Affect Complexity?

    Skip repeated fixed values and pointer values

    Before each scan, skip a fixed value that equals the value at the preceding index. After finding a match, move left past every identical value and move right past every identical value. This prevents duplicate triples without relying on a set.

    Compare O(n²) time with sorting and extra-space costs

    Sorting costs O(n log n). The outer loop and two-pointer scans cost O(n²), so the complete algorithm remains O(n²). An in-place sort uses O(1) auxiliary space apart from the output; copying the array first adds O(n) space. The returned triples require additional output space proportional to their number.

  • Ladder Logic for PLC Beginners: Read a Basic Control Rung

    Ladder Logic for PLC Beginners: Read a Basic Control Rung

    Ladder logic is a graphical language for controlling machines with a programmable logic controller (PLC). It uses relay-style symbols so you can follow a control decision from input conditions to an output.

    In PLC ladder logic, the controller repeatedly reads field inputs, evaluates instructions, and updates outputs. Learning to follow that sequence makes a basic ladder logic diagram predictable rather than a collection of unfamiliar symbols.

    Ladder logic in the PLC scan cycle

    A ladder diagram is arranged between two vertical lines called rails. The left rail represents the beginning of logical power flow, and the right rail represents the destination. Horizontal lines between them are rungs. Each rung contains instructions that determine whether an output instruction becomes true.

    The PLC does not physically send power through the drawing as a relay panel would. Instead, it evaluates each instruction as a Boolean condition. A rung is true when at least one complete path from the left rail to the output is true. A rung is false when every possible path is blocked.

    The controller generally evaluates rungs from top to bottom and instructions from left to right. The exact scan details vary by PLC, but this ordering is the essential model for following control logic.

    Contacts, coils, rails, and rungs in a ladder logic diagram

    A contact tests a Boolean value, usually an input, internal bit, timer, counter, or output status.

    • Normally open (NO) contact: This instruction is true when its referenced bit is on. It passes logic when the bit equals 1.
    • Normally closed (NC) contact: This instruction is true when its referenced bit is off. It passes logic when the bit equals 0.
    • Output coil: This instruction writes the rung result to an output or internal bit. A true coil turns its assigned bit on; a false coil turns it off.
    • Rail: A vertical boundary that frames the logical path.
    • Rung: A horizontal line containing the conditions and result for one control decision.

    “Normally open” and “normally closed” describe the instruction’s logic behavior or the associated device’s unactuated design. The drawn contact does not prove the live field device’s current state. Check the referenced input or bit to know whether the instruction is currently true.

    How ladder logic programming follows input, logic, and output stages

    Ladder logic programming follows a repeating PLC scan with three practical stages:

    1. Input read: The PLC samples connected input devices, such as push buttons, switches, and sensors, and stores their current states in an input image or memory area.
    2. Logic execution: The PLC evaluates the program, including ladder rungs, using the stored input states and current internal values. It calculates whether each contact path and output coil is true.
    3. Output update: The PLC transfers calculated output states to physical outputs, energizing or de-energizing devices such as contactors, solenoid valves, and indicator lamps.

    Because outputs are commonly updated after logic execution, a change at a push button normally affects the controlled device during the scan in which the PLC reads that change and completes the program. The next scan then uses the updated output or internal status where applicable.

    How to read ladder logic in a simple start-stop circuit

    Consider a motor-control rung with this arrangement:

    Left rail → NC Stop → (NO Start in parallel with NO Motor Auxiliary) → Motor coil → Right rail

    The parallel branch is a seal-in, or holding, circuit. The motor’s auxiliary status keeps the motor command true after the Start button is released.

    1. At rest: The Stop button is not pressed, so its physical input is on when the button uses normally closed wiring. The NC Stop instruction is therefore true. Start is not pressed, so the NO Start instruction is false. The motor auxiliary bit is also false, leaving both parallel paths open. The rung is false, and the Motor coil is off.
    2. When Start is pressed: The input read stage records Start as on. During logic execution, the NC Stop instruction remains true and the NO Start instruction becomes true. A complete path now reaches the Motor coil, so the coil becomes true. During output update, the motor output energizes.
    3. After Start is released: The Start input returns off, making its NO instruction false. The energized motor’s auxiliary bit becomes on during the relevant scan, so the parallel auxiliary contact becomes true. The rung remains true and the motor stays energized.
    4. When Stop is pressed: The Stop input changes off. The NC Stop instruction becomes false, breaking the rung before either start path can reach the coil. The Motor coil becomes false, and the output update de-energizes the motor. On a later scan, the auxiliary bit also returns off.

    This left-to-right trace is the core method for how to read ladder logic: identify each referenced bit, determine whether each instruction is true, follow every complete path, and then check which output coil receives the rung result.

  • Standard Deviation in R: Calculate It Correctly

    Standard Deviation in R: Calculate It Correctly

    Use sd() to calculate standard deviation in R for a numeric vector or a data-frame column. For standard deviation in R, the main interpretation decision is whether your data represent a sample or an entire population. R’s default result is the sample standard deviation.

    The function also returns NA when missing values are present unless you explicitly remove them during the calculation.

    Standard deviation in R with sd()

    Pass a numeric vector to sd():

    x <- c(12, 15, 14, 10, 9)

    sd(x)

    This returns approximately 2.54951. The values have a mean of 12, and R divides the sum of squared deviations by 4, which is the sample denominator: the number of observations minus one.

    You can apply the same standard deviation function to a numeric column in a data frame:

    scores <- data.frame(score = c(12, 15, 14, 10, 9))

    sd(scores$score)

    This also returns approximately 2.54951. Use scores[[“score”]] as an alternative to scores$score. Both expressions select the underlying numeric vector. By contrast, scores[“score”] returns a one-column data frame, which is not the intended input for this calculation.

    The standard deviation function in R handles missing values

    By default, sd() does not ignore missing observations:

    measurements <- c(4, 7, NA, 10)

    sd(measurements)

    The result is NA because the missing value propagates through the calculation. Add na.rm = TRUE to exclude missing values:

    sd(measurements, na.rm = TRUE)

    This calculates the sample standard deviation of 4, 7, and 10. The argument removes only the NA values; it does not replace them or estimate what they might have been.

    Use the same argument with a data-frame column:

    sd(scores$score, na.rm = TRUE)

    If no nonmissing values remain, or only one nonmissing value remains, a standard deviation cannot be estimated and R returns NA.

    Sample versus population standard deviation

    sd() uses the sample standard deviation formula:

    sqrt(sum((x – mean(x))^2) / (n – 1))

    Here, n is the number of observed values. The n – 1 denominator estimates the variability of a larger population from a sample, so do not label R’s default result as a population standard deviation.

    Use a population standard deviation when your vector contains every member of the population being measured. The population formula divides by n:

    sqrt(sum((x – mean(x))^2) / n)

    In R, calculate it directly with:

    population <- c(12, 15, 14, 10, 9)

    sqrt(mean((population – mean(population))^2))

    If missing values are possible, remove them first:

    complete <- population[!is.na(population)]

    sqrt(mean((complete – mean(complete))^2))

    You can also convert a sample result to a population result with sd(complete) * sqrt((n – 1) / n), where n <- length(complete).

    Check R standard deviation with a worked vector

    This vector makes the denominator difference easy to verify:

    values <- c(2, 4, 4, 4, 5, 5, 7, 9)

    mean(values)

    sd(values)

    The mean is 5. The sum of squared deviations from that mean is 32, and there are eight observations. Therefore:

    • Sample standard deviation: sqrt(32 / 7), which is approximately 2.13809 and matches sd(values).
    • Population standard deviation: sqrt(32 / 8), which equals 2.

    Use sd() for the usual sample estimate, add na.rm = TRUE when missing values should be excluded, and use the denominator-n formula when the data represent the complete population.

  • Append to pandas DataFrame with pd.concat

    Append to pandas DataFrame with pd.concat

    Use pd.concat to append to pandas DataFrame objects in current pandas versions. It replaces the removed df.append pattern and can combine DataFrames, dictionaries converted to rows, or batches of records. For most row additions, use ignore_index=True so the result receives one continuous index.

    pd.concat returns a new DataFrame; it does not modify either input in place. The basic pattern is pd.concat([df, new_data], ignore_index=True).

    Replace df.append with pd.concat: side-by-side legacy and current code with ignore_index

    The pandas DataFrame append method was removed in pandas 2.0. If older code contains an append call, replace it with a list passed to pd.concat.

    1. Legacy code, no longer available: df.append(new_df, ignore_index=True)
    2. Current code: pd.concat([df, new_df], ignore_index=True)

    The replacement is usually a direct change. Both operations place the rows from new_df below the rows in df. Assign the result if you need the combined object:

    result = pd.concat([df, new_df], ignore_index=True)

    Use ignore_index=True when the original index labels are not meaningful and the output should run from 0 through the number of rows minus 1. Without it, pandas preserves the existing labels. That can produce duplicate index values, especially when both inputs use the default range index.

    For example, concatenating two three-row DataFrames without ignore_index can create an index of 0, 1, 2, 0, 1, 2. This is valid, but label-based selection and joins may become ambiguous. Use result.reset_index(drop=True) later if resetting the index is more convenient.

    Append DataFrame objects with pd.concat: column alignment, missing values, and duplicate indexes

    To append DataFrame objects, pass them in order inside a list. By default, pd.concat combines rows with axis=0 and aligns columns by column name:

    result = pd.concat([sales_january, sales_february], ignore_index=True)

    Column order in the result generally follows the first DataFrame, followed by columns introduced by later DataFrames. If the inputs do not contain the same columns, pandas uses the union of their column names. A missing value appears as NaN or another appropriate missing-value marker.

    • If the first DataFrame has customer and total, while the second has customer and currency, the result contains all three columns.
    • January rows have missing values in currency.
    • February rows have missing values in total.

    This name-based alignment prevents values from being assigned to the wrong field when column order differs. If you want only columns shared by every input, use join=”inner”:

    result = pd.concat([df_a, df_b], join=”inner”, ignore_index=True)

    Use the default join=”outer” when preserving every column matters. Check the resulting dtypes after concatenation if one input contains numbers and another contains strings or missing values; pandas may broaden a column’s dtype to accommodate both.

    Duplicate index labels are retained unless you explicitly request a new index. They are not automatically errors. Preserve them when the labels identify source records, or set ignore_index=True when the combined DataFrame represents a new sequential collection.

    Add a dictionary or one row when appending to a pandas DataFrame

    A dictionary represents one row when each key is a column name and each value is that row’s value. Wrap it in a one-item list before creating a DataFrame:

    row = {“product”: “Notebook”, “quantity”: 3, “price”: 4.50}
    result = pd.concat([df, pd.DataFrame([row])], ignore_index=True)

    The list is important because pd.DataFrame([row]) creates one record. Calling pd.DataFrame(row) with scalar values does not provide enough information for pandas to determine the row index.

    Keys are matched to existing columns by name. If the dictionary omits a column, the new row receives a missing value there. If it introduces a new key, pd.concat adds a new column and fills that column with missing values for earlier rows:

    new_row = {“product”: “Pen”, “quantity”: 10}
    df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)

    For one-row additions inside a controlled, interactive workflow, direct assignment such as df.loc[len(df)] = row can be concise. For a consistent append DataFrame workflow, converting the record and using pd.concat makes column alignment explicit.

    Build many rows efficiently with pandas concat for rows

    Do not repeatedly concatenate a growing DataFrame inside a loop. Each operation can allocate and copy the accumulated data, making a long sequence much slower than one final combination. The efficient pandas concat rows approach is to collect DataFrames or records first.

    Collect records as dictionaries, convert the complete batch once, and concatenate once:

    records = []
    for item in source:
        records.append({“id”: item.id, “status”: item.status})
    new_rows = pd.DataFrame(records)
    result = pd.concat([df, new_rows], ignore_index=True)

    This works well when each iteration produces a dictionary. If each iteration already produces a DataFrame, store those frames in a list instead:

    frames = [df]
    for batch in batches:
        frames.append(batch)
    result = pd.concat(frames, ignore_index=True)

    For an empty batch, decide whether to return the original DataFrame or concatenate it with an explicitly shaped empty DataFrame. When column types matter, define the expected columns and dtypes before processing so missing or empty inputs do not unexpectedly change the result.

  • decimal to two’s complement: Convert Signed Values at a Fixed Width

    decimal to two’s complement: Convert Signed Values at a Fixed Width

    To perform decimal to two’s complement conversion, declare the bit width first. For an 8-bit signed value, the range is −128 through +127. Positive values use ordinary binary padded with leading zeros; negative values use the invert-and-add-one method.

    For two’s complement to decimal decoding, inspect the leftmost bit. A 0 means use ordinary positive binary weights. A 1 means the value is negative, so either use a negative sign-bit weight or invert the bits, add one, and negate the result.

    Choose an 8-bit width and represent positive values such as +13

    Bit width is part of the representation because the same visible bits can have different meanings at different widths. For example, 1101 is an unsigned binary value, but an 8-bit signed representation must contain exactly eight bits.

    To represent +13 in 8-bit two’s complement:

    1. Convert the magnitude, 13, to binary: 1101.
    2. Pad on the left with zeros until there are eight bits: 00001101.
    3. Because the first bit is 0, the pattern represents a positive value.

    Check the result by adding the weights of the 1 bits: 8 + 4 + 1 = 13. Thus, the 8-bit pattern 00001101 converts back to +13.

    Convert a negative decimal value using decimal to two’s complement and binary to two’s complement steps

    For a negative decimal value, first write the positive magnitude at the declared width. Then apply the standard binary to two’s complement process: invert every bit and add one.

    Convert −13 to an 8-bit pattern as follows:

    1. Write positive 13 in eight bits: 00001101.
    2. Invert every bit: 11110010.
    3. Add one: 11110011.

    Therefore, −13 is represented as 11110011 in 8-bit two’s complement. The addition is binary addition, so 11110010 + 1 produces 11110011 without changing the width.

    The round-trip check confirms the result. Start with 11110011, invert it to 00001100, add one to get 00001101, and read that magnitude as 13. Since the original pattern had a sign bit of 1, the decoded result is −13.

    Decode two’s complement to decimal using signed bit weights

    For two’s complement to decimal conversion, use the leftmost bit as the sign bit and apply signed weights. In an 8-bit value, the weights from left to right are:

    −128, 64, 32, 16, 8, 4, 2, 1

    This makes the decoding path direct:

    • For 00001101, add the weights under the 1 bits: 8 + 4 + 1 = +13.
    • For 11110011, add the signed weights under the 1 bits: −128 + 64 + 32 + 16 + 2 + 1 = −13.

    The sign bit changes the interpretation rather than simply adding a positive 128. A leading 0 contributes no sign weight, while a leading 1 contributes −128 in an 8-bit representation. This weighted method provides a second check against the invert-and-add-one method.

    Check the 8-bit signed range, overflow, and round-trip results

    An n-bit two’s-complement representation has the range −2n−1 through 2n−1 − 1. With eight bits, that is −27 through 27 − 1, or −128 to +127. The negative side has one extra value because zero uses the positive sign pattern.

    • Valid positive limit: +127 is 01111111.
    • Valid negative limit: −128 is 10000000.
    • Overflow: +128 and −129 cannot be represented as signed 8-bit values.

    Before converting, verify that the decimal value falls within the selected range. After converting, decode the resulting bit pattern back to decimal. The positive round trip is +13 → 00001101 → +13, and the negative round trip is −13 → 11110011 → −13. If the final value differs, check the declared width, padding, bit inversion, and add-one step.

  • SQL DELETE statement: syntax, WHERE filters, and NOT EXISTS

    SQL DELETE statement: syntax, WHERE filters, and NOT EXISTS

    Use the SQL DELETE statement to remove existing rows from a table. Its basic form names the target table, then optionally adds a WHERE condition. Supplying a qualifying condition removes only matching rows; omitting WHERE removes every row in the target table. These are different operations, not interchangeable forms.

    A DELETE in SQL statement can select rows by their own column values or by the absence of related rows. A SQL DELETE query using NOT EXISTS is useful when deletion should occur only if no matching record exists in another table.

    SQL DELETE statement syntax: table references and affected rows

    The minimal SQL DELETE syntax is:

    DELETE FROM table_name WHERE condition;

    DELETE FROM identifies the table affected by the operation. The optional WHERE clause determines which rows qualify. For example:

    DELETE FROM sessions WHERE expires_at < CURRENT_TIMESTAMP;

    This removes only sessions whose expiration time has passed. Rows with a future expiration time remain in the table. If no rows satisfy the condition, the statement completes without deleting anything.

    When WHERE is omitted, every row in the named table qualifies:

    DELETE FROM sessions;

    This empties the table’s rows but does not normally remove the table itself, its columns, or its indexes. Because the statement has no row filter, it must not be treated as equivalent to a DELETE with a qualifying condition. A condition that happens to match every current row is still a deliberate filter; an omitted condition provides no row-level protection.

    Table references become important when a statement uses more than one table or when aliases make column ownership clearer. A qualified reference such as orders.customer_id identifies the column from orders, rather than relying on an ambiguous unqualified name.

    Filtering DELETE in SQL with WHERE and aliases

    The WHERE clause can combine equality, ranges, Boolean logic, and null checks. For example, this statement deletes old cancelled orders while preserving recent or active orders:

    DELETE FROM orders WHERE status = ‘cancelled’ AND created_at < DATE ‘2024-01-01’;

    The database evaluates the condition for each candidate row. Both parts of the AND expression must be true for that row to be deleted. Use parentheses when combining AND with OR, so the intended precedence is explicit.

    An alias is useful when the target table appears in a correlated subquery. In systems that support this PostgreSQL-style form, the alias follows the table name:

    DELETE FROM customers AS c WHERE c.status = ‘inactive’;

    Here, c refers to the row being considered for deletion. Alias syntax varies by database system. For example, SQL Server commonly writes a target alias before the FROM clause:

    DELETE c FROM customers AS c WHERE c.status = ‘inactive’;

    Check the syntax for the specific database engine before moving a multi-table DELETE into production. The central rule remains the same: the target reference identifies the table, and the WHERE clause limits affected rows.

    How SQL NOT EXISTS evaluates a correlated absence test

    SQL NOT EXISTS tests whether a subquery returns no rows. It does not compare a value directly. Instead, the database evaluates the subquery as an existence test and returns true when the subquery produces zero matching rows.

    A correlated example identifies customers who have no orders:

    SELECT c.customer_id
    FROM customers AS c
    WHERE NOT EXISTS (SELECT 1 FROM orders AS o WHERE o.customer_id = c.customer_id);

    The inner query is correlated because it refers to c.customer_id from the outer query. Logically, the database considers each customer, searches orders for an order with the same customer ID, and applies NOT EXISTS. If at least one related order is found, the condition is false. If none is found, the condition is true and that customer appears in the result.

    SELECT 1 is conventional in an existence test. The selected value is irrelevant; only whether a row exists matters. The database can also stop searching after finding the first match. This makes the expression a direct way to represent “no related row satisfies this condition.”

    NOT EXISTS also avoids a common null issue associated with some NOT IN expressions. If the subquery can return null, NOT IN may produce an unknown result rather than the expected absence test. The correlated relationship should still use compatible keys and appropriate indexes.

    Worked SQL DELETE query examples for selected and unmatched rows

    To delete selected rows based on a related table, place the correlated absence test in the DELETE filter. This PostgreSQL-style example removes inactive customers who have never placed an order:

    DELETE FROM customers AS c
    WHERE c.status = ‘inactive’
    AND NOT EXISTS (SELECT 1 FROM orders AS o WHERE o.customer_id = c.customer_id);

    The customer must satisfy both conditions: the status must be inactive, and the subquery must find no order for that customer. An inactive customer with even one matching order is not deleted.

    The same pattern can identify unmatched rows before deletion. This query lists staging contacts that have no corresponding approved contact:

    SELECT s.contact_id, s.email
    FROM staging_contacts AS s
    WHERE NOT EXISTS (SELECT 1 FROM approved_contacts AS a WHERE a.email = s.email);

    After confirming that the result is correct, the selection can become a DELETE:

    DELETE FROM staging_contacts AS s
    WHERE NOT EXISTS (SELECT 1 FROM approved_contacts AS a WHERE a.email = s.email);

    This removes only staging contacts whose email has no match in approved_contacts. It does not delete approved contacts, and it does not delete staging contacts that do have a match.

    For a safer workflow, run the correlated SELECT with the exact intended filter first, inspect the returned keys, and then execute the corresponding DELETE in a transaction when the database supports transactional DML. Also verify that the relationship column is the correct business key; matching on an unrelated or nullable column can classify rows as unmatched incorrectly.