import java.util.Arrays;

/*
================================================================================
AIRPORT BOARDING SIMULATION - BOOLEAN IDENTITY VERSION
================================================================================
Purpose of this version:
- Keep Amit's original aisle-shifting structure very close.
- Convert the story to airport boarding.
- Preserve the important boolean-style identity logic inline.
- Keep original code fragments commented beside the airport equivalent where possible.

Core mapping:
 people[] -> boardingAisle[]
 1 -> anonymous passenger in aisle
 0 -> empty aisle tile
 person -> passenger
 move right -> board toward rear seats
 move left -> deplane / move toward front door
 scan row -> boarding tick / boarding phase
 finished moving -> reached final packed boarding zone
 recordPersonMoved[] -> recordPassengerFinishedAt[]

Important limitation:
This version intentionally keeps passengers anonymous as 1s.
That is why the boolean logic remains important: identity must be inferred through scans.

OUTPUT NOTATION - VOLUME 1
Volume 1 has one dataset only:

boardingAisle[]
 0 = empty aisle tile
 1 = anonymous passenger standing in the aisle

Therefore, in Volume 1, the number 1 always refers to a passenger inside the
aisle movement dataset. There is no separate seat dataset in this version.

ARCHITECTURAL NOTE - WHY SOME BEHAVIOURS ARE PRESERVED
This code is written as a domain translation, not as a production airline system.
Some behaviours preserved below, such as comparing movement toward the rear versus
toward the front, are not usually dynamic decisions in real aircraft boarding.
Airlines are constrained by door availability, jet bridges, stairs, boarding groups,
crew procedures, aircraft layout and safety rules.

The reason these mechanisms remain in the code is architectural: they demonstrate
that the same deterministic movement engine can evaluate alternative directions,
local clustering, compacting and boundary completion when a domain permits those
choices. In the aircraft cabin, those behaviours are often constrained or only
partly observable. In less constrained future domains, such as warehouse logistics,
hospital movement, manufacturing flow or container handling, the same mechanisms
may become operationally useful rather than merely explanatory.
================================================================================
*/

/*
Example:

boardingAisle = [0, 1, 1, 0, 1, 0]

means
tile 0 : empty
tile 1 : passenger
tile 2 : passenger
tile 3 : empty
tile 4 : passenger
tile 5 : empty

Nothing more than that.

The program then repeatedly scans the aisle and moves passengers one tile whenever the next tile is empty.

Plain English interpretation
Initial

Passenger at tile 1.
Passenger at tile 2.
Passenger at tile 4.

Boarding tick 1

Passenger at tile 2 moves to tile 3.
Passenger at tile 4 moves to tile 5.

Boarding tick 2

Passenger at tile 1 moves to tile 2.
Passenger at tile 3 moves to tile 4.
*/


class Main {
    public static void main(String[] args) {
        AirportBoardingBooleanIdentity sim = new AirportBoardingBooleanIdentity();
        sim.boardLeftOrRightVerdict();
        sim.reportMinimumAdjacentBoardingZoneMoves();
    }
}

class AirportBoardingBooleanIdentity {
    // =========================================================
    // TEST CASES AT TOP - airport boarding equivalents
    // =========================================================
    // 0 = empty aisle tile
    // 1 = anonymous passenger standing in aisle
    // The active test case below is intentionally the same shape as the original.
    // Try the alternative test cases one at a time. This is the best way to learn
    // how spacing, adjacency and blocking alter the movement verdict.
    int[] boardingAisle = new int[]{0, 1, 1, 0, 1, 0, 0, 0, 1};

    // Original equivalent:
    // int[] people = new int[]{0, 1, 1, 0, 1, 0, 0, 0, 1};

    // Additional boarding test cases:
    // int[] boardingAisle = new int[]{1, 0, 1, 0, 1};              // alternating passengers
    // int[] boardingAisle = new int[]{0, 0, 1, 0, 0, 0, 1, 0};     // sparse boarding aisle
    // int[] boardingAisle = new int[]{1, 1, 1, 1, 1};              // fully blocked aisle
    // int[] boardingAisle = new int[]{0, 1, 0, 0, 1, 0, 1};        // chain continuation case
    // int[] boardingAisle = new int[]{0, 1, 0, 0, 1, 0};           // two passengers, open aisle
    // int[] boardingAisle = new int[]{0, 0};                       // empty aisle
    // int[] boardingAisle = new int[]{0, 1, 0};                    // one passenger

    // The aisle array is the only movement dataset in Volume 1.
    // Volume 2 later adds seat-bank datasets, but this version deliberately keeps
    // the model one-dimensional so the original movement backbone stays visible.
    //
    // DATASET INTERPRETATION NOTE:
    // In this file, clustering and movement both apply to boardingAisle[].
    // In future domain translations, the same idea may apply to a different dataset:
    // stock locations, hospital rooms, workstations, containers, robots or tasks.
    int aisleLength = boardingAisle.length;
    int numberPassengers = countPassengers();

    // =========================================================
    // ORIGINAL STATE VARIABLES, AIRPORT RENAMED
    // =========================================================
    boolean isStartAtFrontDoor = false;
    // ORIGINAL: boolean isStartZero = false;
    // AIRPORT MEANING:
    // true  => scan from front door / left side toward rear / right side.
    // false => scan from rear / right side toward front door / left side.

    boolean hasMultiplePassengers = false;
    // ORIGINAL: boolean hasMultiplePeople = false;
    // AIRPORT MEANING:
    // Set true if the proximity scan finds two passengers on one side.

    boolean hasMinimumSinglePassenger = false;
    // ORIGINAL: boolean hasMinimumSinglePerson = false;
    // AIRPORT MEANING:
    // Set true if at least one passenger exists anywhere in the aisle.

    int count = 0;
    int distanceFromFront = 0;
    int distanceFromRear = 0;
    // ORIGINAL: int distanceLeft = 0; int distanceRight = 0;

    // Stores only the first two passengers found from the current scan side.
    // This is intentionally local evidence, not a full global optimisation.
    int[] passengerPositions = new int[2];
    int singlePassengerLocation;
    // ORIGINAL: int pos[] = new int[2]; int singlePersonLocation;

    double averageMoves;
    int totalMovesTowardRear = 0;
    int totalMovesTowardFront = 0;
    int totalPassengersMovedTowardRear = 0;
    int totalPassengersMovedTowardFront = 0;
    double averageMovesTowardRear = Double.NaN;
    double averageMovesTowardFront = Double.NaN;

    // ORIGINAL EQUIVALENT:
    // int totalMovesRight = 0;
    // int totalMovesLeft = 0;
    // int totalPeopleMovedRight = 0;
    // int totalPeopleMovedLeft = 0;
    // double averageMovesRight = Double.NaN;
    // double averageMovesLeft = Double.NaN;

    public AirportBoardingBooleanIdentity() {
        int farEnd;
        int start = 0;
        String boardingDirection;

        System.out.println("AIRPORT BOARDING BOOLEAN IDENTITY SIMULATION");
        System.out.println("Aisle length is: " + aisleLength);
        System.out.println("Number passengers is: " + numberPassengers);
        System.out.println("Original boarding aisle: " + Arrays.toString(boardingAisle));
        System.out.println("0 = empty aisle tile, 1 = anonymous passenger");
        System.out.println("Volume 1 notation: all values belong to boardingAisle[] only.");
        System.out.println();
        System.out.println("A proximity technique from front and rear estimates a likely boarding direction.");
        System.out.println("IMPORTANT: this heuristic is not always correct, so full simulations are compared.");

        // ARCHITECTURAL NOTE - PROXIMITY HEURISTIC VS FULL SIMULATION
        // The first check looks only at the nearest passenger pair from each side.
        // In a real aircraft, an airline would not normally make live front/rear
        // boarding decisions from such a scan. The retained value is that the
        // algorithm can compare local evidence with a full deterministic outcome.
        // This is useful as a teaching example and more powerful in domains where
        // entry direction can truly be selected dynamically.

        // Scan from the front-door side first. In aircraft language this represents
        // movement toward the rear. In a less constrained domain, this would be
        // equivalent to asking whether one entry direction is preferable.
        System.out.println("\n**** Calculating proximity of two passengers from FRONT DOOR side ****");
        farEnd = aisleLength;
        if (start == 0) {
            isStartAtFrontDoor = true;
            boardingDirection = "towardRear";
        } else {
            boardingDirection = "towardFront";
        }
        scanSideToSide(start, farEnd);
        System.out.println("************** BOARDING ALL PASSENGERS: " + boardingDirection);
        beginBoardingMove(start, farEnd, boardingDirection);

        isStartAtFrontDoor = false;
        // Scan from the rear side second. This is preserved to keep the algorithmic
        // comparison symmetrical, even though real aircraft boarding normally follows
        // fixed operational procedures rather than switching direction live.
        System.out.println("\n**** Calculating proximity of two passengers from REAR side ****");
        start = aisleLength - 1;
        farEnd = 0;
        if (start == 0) {
            isStartAtFrontDoor = true;
            boardingDirection = "towardRear";
        } else {
            boardingDirection = "towardFront";
        }
        scanSideToSide(start, farEnd);
        System.out.println("************** BOARDING ALL PASSENGERS: " + boardingDirection);
        beginBoardingMove(start, farEnd, boardingDirection);
    }

    public int countPassengers() {
        // Counts anonymous 1s. No passenger identity exists at this stage;
        // identity is reconstructed later through movement continuation logic.
        int total = 0;
        for (int i = 0; i < aisleLength; i++) {
            if (boardingAisle[i] == 1) {
                total++;
            }
        }
        return total;
    }

    public void scanSideToSide(int start, int otherEnd) {
        // ORIGINAL: public void sideToSide(int start, int otherEnd)
        // ARCHITECTURAL NOTE - LOCAL EVIDENCE SCAN
        // This method deliberately inspects the aisle from one side until it finds
        // the first two passengers. It is not a complete boarding optimisation by
        // itself. It is a preserved local-evidence mechanism that can later be
        // compared against the complete movement simulation.
        for (int i = start;
             isStartAtFrontDoor ? i < otherEnd : i >= otherEnd;
             i = isStartAtFrontDoor ? i + 1 : i - 1) {

            System.out.println("aisle tile " + i + " contains: " + boardingAisle[i]);
            if (count == 2) {
                break;
            }
            if (boardingAisle[i] == 1) {
                passengerPositions[count] = i;
                count++;
                if (count == 1) {
                    singlePassengerLocation = i;
                    hasMinimumSinglePassenger = true;
                }
            }
        }

        if (count == 2) {
            if (start == 0) {
                // Number of empty tiles between the first two passengers from the front side.
                // This is a local spacing measure: larger gaps imply more room before clustering.
                distanceFromFront = passengerPositions[1] - passengerPositions[0] - 1;
                System.out.println("Distance from front-side pair is: " + distanceFromFront + "\n");
            } else {
                // Number of empty tiles between the first two passengers from the rear side.
                // This mirrors the front-side calculation so both directions are comparable.
                distanceFromRear = passengerPositions[0] - passengerPositions[1] - 1;
                System.out.println("Distance from rear-side pair is: " + distanceFromRear + "\n");
            }
            hasMultiplePassengers = true;
        } else if (count == 1) {
            System.out.println("One passenger in aisle: location " + singlePassengerLocation);
        } else {
            System.out.println("Empty aisle");
        }
        count = 0;
    }

    public void boardLeftOrRightVerdict() {
        // ORIGINAL: public void leftOrRight()
        // ARCHITECTURAL NOTE - DIRECTIONAL MOVEMENT
        // The airport interpretation uses "towardRear" and "towardFront" labels.
        // In real boarding, these directions are usually not dynamically chosen
        // from a live aisle array. The comparison remains here because it preserves
        // the more general architectural principle: when a domain permits movement
        // in alternative directions, the same engine can evaluate which direction
        // produces fewer moves or a better average movement cost.
        System.out.println("\nAMIT AMLANI ORIGINAL-STYLE BOARDING VERDICT");
        System.out.println("Reason: only inspects the first two passengers from each side.");

        if (hasMinimumSinglePassenger) {
            // Heuristic verdict: this uses only the local spacing evidence gathered
            // from both ends. The simulation verdict below is more reliable because
            // it actually executes the movement process.
            if (distanceFromFront > distanceFromRear) {
                System.out.println("\n******VERDICT: Board passengers toward rear\n");
            } else if (distanceFromRear > distanceFromFront) {
                System.out.println("\n******VERDICT: Move passengers toward front\n");
            } else {
                if (distanceFromFront == 0 && distanceFromRear == 0 && !hasMultiplePassengers) {
                    if ((aisleLength - 1 - singlePassengerLocation) < singlePassengerLocation) {
                        System.out.println("\n******VERDICT: Move single passenger toward rear\n");
                    } else if (singlePassengerLocation < (aisleLength - 1) - singlePassengerLocation) {
                        System.out.println("\n******VERDICT: Move single passenger toward front\n");
                    } else {
                        System.out.println("\n******VERDICT: no difference moving single passenger\n");
                    }
                } else {
                    System.out.println("\n******VERDICT: no difference moving front or rear\n");
                }
            }
        }

        System.out.println("---------------------------------------------------------");
        System.out.println("SIMULATION VERDICT => compares actual full boarding outcomes");
        if (!hasMinimumSinglePassenger) {
            return;
        }

        // Full simulation verdict: this compares the completed deterministic outcomes.
        // This is why both directions are executed in the constructor before this method reports.
        if (totalMovesTowardRear < totalMovesTowardFront) {
            System.out.println("\n******VERDICT: Board passengers toward rear\n");
        } else if (totalMovesTowardFront < totalMovesTowardRear) {
            System.out.println("\n******VERDICT: Move passengers toward front\n");
        } else {
            if (!Double.isNaN(averageMovesTowardRear) && !Double.isNaN(averageMovesTowardFront)) {
                if (averageMovesTowardRear < averageMovesTowardFront) {
                    System.out.println("\n******VERDICT: Board passengers toward rear\n");
                } else if (averageMovesTowardFront < averageMovesTowardRear) {
                    System.out.println("\n******VERDICT: Move passengers toward front\n");
                } else {
                    System.out.println("\n******VERDICT: no difference moving front or rear\n");
                }
            } else {
                System.out.println("\n******VERDICT: no difference moving front or rear\n");
            }
        }
        System.out.println("---------------------------------------------------------");
    }

    public boolean checkAllPassengersFinished(boolean hasConfirmedFinishedBoarding, String boardingDirection) {
        // ORIGINAL: public boolean checkHasFinished(boolean hasConfirmedFinishedMoving, String directionMove)
        // ARCHITECTURAL NOTE - BOUNDARY COMPLETION
        // Volume 1 stops when the aisle has reached a packed terminal condition at
        // the chosen boundary. This does not mean the whole aircraft population has
        // been modelled. It means the local aisle dataset has completed the movement
        // objective for this simulation.
        int interestedLocation;
        for (int i = 0; i < numberPassengers; i++) {
            if ("towardRear".equals(boardingDirection)) {
                interestedLocation = aisleLength - (i + 1);
            } else {
                interestedLocation = i;
            }
            // The terminal zone must contain all passengers. If any required terminal tile
            // is still empty, the movement simulation continues.
            if (!(boardingAisle[interestedLocation] == 1)) {
                hasConfirmedFinishedBoarding = false;
            }
        }
        return hasConfirmedFinishedBoarding;
    }

    public void beginBoardingMove(int start, int otherEnd, String boardingDirection) {
        // ORIGINAL: public void beginMove(int start, int otherEnd, String directionMove)
        // ARCHITECTURAL NOTE - DETERMINISTIC MOVEMENT ENGINE
        // This method performs the actual repeated scan-and-move process. The aisle
        // is updated one tile at a time. Passengers only move into empty aisle tiles.
        // The aircraft story is constrained, but the underlying scan loop is reusable
        // for any one-dimensional movement corridor.
        boolean hasFinishedBoarding = true;
        // ORIGINAL: boolean hasFinishedMoving = true;

        boolean hasPassengerPrevMove = false;
        // ORIGINAL: boolean hasPersonPrevMove = false;
        // Meaning: used to identify whether this is a continuation of a passenger movement in this scan.

        boolean hasPreviousPassenger = false;
        // ORIGINAL: boolean hasPreviousPerson = false;
        // Meaning: previous anonymous passenger has just been registered as finished.

        boolean hasPassengerMove = false;
        // ORIGINAL: boolean hasPersonMove = false;
        // Meaning: a passenger has started or is currently moving during this scan.

        boolean hasPassengerFinishedBoarding = true;
        // ORIGINAL: boolean hasPersonFinishedMoving = true;
        // Meaning: current anonymous passenger has reached a stop/finish condition.

        int numPassengersMoving = 0;
        // ORIGINAL: int numPeopleMoving = 0;

        int moves = 0;
        int[] original = new int[aisleLength];
        int k = 0;
        System.arraycopy(boardingAisle, 0, original, 0, aisleLength);

        // Fixed-size tracking array retained from the original style.
        // It is sufficient for these teaching datasets; a production-quality version
        // could replace it with a dynamically sized list.
        int[] recordPassengerFinishedAt = new int[50];
        // ORIGINAL: int[] recordPersonMoved = new int[50];
        // Airport meaning: because passengers are anonymous 1s, this approximates identity by recording finish positions.

        int numberBoardingTicks = 0;
        // ORIGINAL: int numberScansRow = 0;
        // Airport meaning: each scan is a boarding tick / simulation phase.

        int currentNumberBoardingTicks = 0;
        // ORIGINAL: int currentNumberScansRow = 0;

        int indexMove;
        int finishPoint = 0;

        do {
            numberBoardingTicks++;
            hasFinishedBoarding = true;

            // =====================================================
            // PHASE RESET - key connection to the original code
            // =====================================================
            // ORIGINAL FIX:
            // hasPersonPrevMove = false;
            // hasPreviousPerson = false;
            // hasPersonMove = false;
            // hasPersonFinishedMoving = true;
            //
            // AIRPORT EQUIVALENT:
            // These reset lines are important. Each boarding tick must begin with a
            // clean interpretation state so that previous scan evidence does not leak
            // into the next tick. This preserves deterministic behaviour.
            hasPassengerPrevMove = false;
            hasPreviousPassenger = false;
            hasPassengerMove = false;
            hasPassengerFinishedBoarding = true;

            System.out.println("\nBOARDING TICK " + numberBoardingTicks + " START: " + Arrays.toString(boardingAisle));

            for (int i = start;
                 isStartAtFrontDoor ? i < (otherEnd - 1) : i > otherEnd;
                 i = isStartAtFrontDoor ? i + 1 : i - 1) {

                // Convert the selected direction into the next aisle tile to inspect.
                // This single index calculation is where front/rear movement becomes executable.
                if (boardingDirection.equals("towardFront")) {
                    indexMove = i - 1;
                } else {
                    indexMove = i + 1;
                }

                if ((boardingAisle[indexMove] == 0) && (boardingAisle[i] == 1)) {
                    // Core movement rule: a passenger moves only if the next tile is empty.
                    // This is the simplest form of local congestion. No passenger jumps,
                    // overtakes or changes corridor in this Volume 1 model.
                    hasPassengerMove = true;

                    if (!hasPassengerPrevMove && hasPassengerMove) {
                        if (i == (aisleLength - 1)) {
                            k = 1;
                        }

                        for (int m = 0; m < k; m++) {
                            if (i == recordPassengerFinishedAt[m]) {
                                // Anonymous identity preservation:
                                // because every passenger is encoded as 1, the program uses
                                // recorded finish locations to infer whether this is the same
                                // passenger continuing rather than a newly counted passenger.
                                System.out.println("\n1START POS / SAME PASSENGER CONTINUES: " + Arrays.toString(boardingAisle));
                                System.out.println("Same anonymous passenger continuing from aisle tile: " + i);
                                hasPassengerFinishedBoarding = false;
                                currentNumberBoardingTicks = numberBoardingTicks;
                                break;
                            } else if (m == (k - 1)) {
                                System.out.println("\n2START POS / NEW PASSENGER STARTS: " + Arrays.toString(boardingAisle));
                                System.out.println("New anonymous passenger has commenced movement at aisle tile: " + i);
                                hasPassengerFinishedBoarding = false;
                                numPassengersMoving++;
                                System.out.println("*************************NUMBER PASSENGERS MOVING: " + numPassengersMoving);
                                currentNumberBoardingTicks = numberBoardingTicks;
                            }
                        }

                        if (k == 0 && numberPassengers != 0) {
                            System.out.println("\n4START POS / FIRST PASSENGER STARTS: " + Arrays.toString(boardingAisle));
                            System.out.println("New anonymous passenger has commenced movement at aisle tile: " + i);
                            System.out.println("*************************NUMBER PASSENGERS MOVING: " + (numPassengersMoving + 1));
                            hasPassengerFinishedBoarding = false;
                            numPassengersMoving++;
                            currentNumberBoardingTicks = numberBoardingTicks;
                        }
                    }

                    // ORIGINAL MOVE:
                    // people[i] = 0;
                    // people[indexMove] = 1;
                    // moves++;
                    // System.out.println(" " + Arrays.toString(people));
                    //
                    // AIRPORT MOVE:
                    boardingAisle[i] = 0;
                    boardingAisle[indexMove] = 1;
                    moves++;
                    System.out.println(" " + Arrays.toString(boardingAisle));
                    hasPassengerPrevMove = true;
                    // Once set, this tells the current scan that movement is already underway.
                    // The next occupied tile can therefore close or register the movement chain.

                } else if (hasPassengerMove && boardingAisle[i] != 0) {
                    // A movement chain has met another occupied tile or reached a point
                    // where the current anonymous passenger must be registered as stopped,
                    // blocked or finished. This is where local clustering starts to matter.
                    if (numberBoardingTicks > currentNumberBoardingTicks && currentNumberBoardingTicks != 0) {
                        currentNumberBoardingTicks = numberBoardingTicks;
                        if ("towardRear".equals(boardingDirection)) {
                            finishPoint = aisleLength - 1;
                        } else if ("towardFront".equals(boardingDirection)) {
                            finishPoint = 0;
                        }
                        System.out.println("PREVIOUS PASSENGER FINISHED / BLOCKED AT POSITION: " + finishPoint);
                        hasPassengerFinishedBoarding = true;
                        System.out.println("REGISTERING UNIQUE ID APPROXIMATION FOR PREVIOUS PASSENGER: " + finishPoint);
                        recordPassengerFinishedAt[k] = 0;
                        k++;
                        hasPreviousPassenger = true;
                    } else {
                        System.out.println("PASSENGER FINISHED / BLOCKED AT POSITION: " + i);
                        hasPassengerFinishedBoarding = true;
                    }

                    hasPassengerMove = false;
                    hasPassengerPrevMove = false;

                    if (numberBoardingTicks > currentNumberBoardingTicks && currentNumberBoardingTicks != 0) {
                        System.out.println("REGISTERING UNIQUE ID APPROXIMATION FOR PASSENGER: " + 0);
                    } else {
                        if (!hasPassengerFinishedBoarding) {
                            System.out.println("PASSENGER FINISHED / BLOCKED AT POSITION: " + i);
                        }
                        if (hasPreviousPassenger) {
                            System.out.println("PASSENGER FINISHED / BLOCKED AT POSITION: " + i);
                            hasPreviousPassenger = false;
                        }
                        System.out.println("REGISTERING UNIQUE ID APPROXIMATION FOR PASSENGER: " + i);
                    }

                    recordPassengerFinishedAt[k] = i;
                    k++;
                }
            }

            // =====================================================
            // END-OF-SCAN FINISH CHECK
            // =====================================================
            // ORIGINAL:
            // if (hasPersonMove) { int endPoint = directionMove.equals("right") ? (length - 1) : 0; ... }
            if (hasPassengerMove) {
                int endPoint = boardingDirection.equals("towardRear") ? (aisleLength - 1) : 0;

                if (boardingAisle[endPoint] == 1) {
                    System.out.println("PASSENGER FINISHED / REACHED BOARDING BOUNDARY AT POSITION: " + endPoint);
                    System.out.println("REGISTERING UNIQUE ID APPROXIMATION FOR PASSENGER: " + endPoint);
                    recordPassengerFinishedAt[k] = endPoint;
                    k++;
                }
                hasPassengerMove = false;
                hasPassengerPrevMove = false;
            }

            // After every tick, test whether all passengers have reached the selected terminal zone.
            // This separates movement execution from completion detection.
            hasFinishedBoarding = checkAllPassengersFinished(hasFinishedBoarding, boardingDirection);
        } while (!hasFinishedBoarding);

        System.out.println("Number passengers moved: " + numPassengersMoving);
        k = 0;
        System.out.println("\n\n---------------------------------------------------------");
        System.out.println("Total number of moves " + boardingDirection + ": " + moves);
        System.out.println("FINISHED BOARDING AISLE: " + Arrays.toString(boardingAisle));
        System.out.println("Number passengers moving for average: " + numPassengersMoving);

        if (numPassengersMoving == 0) {
            averageMoves = 0.0;
        } else {
            averageMoves = (double) moves / (double) numPassengersMoving;
        }

        System.out.println("AVERAGE MOVE PER PASSENGER: " + averageMoves);
        System.out.println("---------------------------------------------------------");

        if ("towardRear".equals(boardingDirection)) {
            totalMovesTowardRear = moves;
            totalPassengersMovedTowardRear = numPassengersMoving;
            averageMovesTowardRear = averageMoves;
        } else {
            totalMovesTowardFront = moves;
            totalPassengersMovedTowardFront = numPassengersMoving;
            averageMovesTowardFront = averageMoves;
        }

        // Restore the original aisle before the opposite direction is compared.
        // Without this reset, the second simulation would start from the result of the first.
        System.arraycopy(original, 0, boardingAisle, 0, aisleLength);
    }

    public void reportMinimumAdjacentBoardingZoneMoves() {
        // ORIGINAL: reportMinimumAdjacencyMoves()
        // Airport equivalent:
        // What is the minimum number of single-tile moves to make passengers form one compact boarding queue?
        // ARCHITECTURAL NOTE - CLUSTERING / COMPACTING
        // This method evaluates how anonymous entities can be grouped into a compact
        // adjacent arrangement. The algorithm is general, but the meaning of clustering
        // depends on the dataset being modelled.
        //
        // In Volume 1 airport boarding, clustering applies primarily to the aisle
        // dataset. It represents passengers becoming locally grouped while waiting
        // for movement opportunities. In an aircraft cabin, this is usually congestion
        // analysis rather than an intended operational goal, because passengers have
        // assigned seats and the cabin geometry is highly constrained.
        //
        // In Volume 2, the model introduces an additional seat-bank dataset. There,
        // clustering has a different meaning: adjacent occupied seats represent local
        // seat interference, not moving passenger traffic. The aisle dataset still
        // models movement, while the seat dataset models occupancy around the target row.
        //
        // In less constrained domains, clustering may apply to different datasets
        // entirely: warehouse storage locations, robot fleets, hospital patient
        // positions, manufacturing workstations or container-yard slots. In those
        // domains, clustering may become an intentional optimisation strategy for
        // batching, staging, resource consolidation or reducing travel distance.
        //
        // Therefore this report is retained as a reusable computational principle:
        // the code clusters anonymous entities, while each domain decides what those
        // entities represent.
        System.out.println("\n=========================================================");
        System.out.println("MINIMUM MOVES TO MAKE ALL PASSENGERS ADJACENT ANYWHERE");
        System.out.println("=========================================================");

        if (numberPassengers <= 1) {
            System.out.println("Trivial case: numberPassengers = " + numberPassengers + ". Already adjacent.");
            System.out.println("Original aisle: " + Arrays.toString(boardingAisle));
            return;
        }

        int[] passengerOnePositions = new int[numberPassengers];
        int idx = 0;
        for (int i = 0; i < aisleLength; i++) {
            if (boardingAisle[i] == 1) {
                passengerOnePositions[idx] = i;
                idx++;
                if (idx == numberPassengers) {
                    break;
                }
            }
        }

        System.out.println("Original aisle: " + Arrays.toString(boardingAisle));
        System.out.println("Passenger positions left-to-right: " + Arrays.toString(passengerOnePositions));
        System.out.println("Evaluating compact boarding blocks of size: " + numberPassengers + "\n");

        int bestStart = -1;
        int bestMoves = Integer.MAX_VALUE;

        // Try every possible compact block of the correct size.
        // This is a pure analytical scan: it does not mutate boardingAisle.
        for (int s = 0; s <= (aisleLength - numberPassengers); s++) {
            int total = 0;
            StringBuilder details = new StringBuilder();

            for (int j = 0; j < numberPassengers; j++) {
                int from = passengerOnePositions[j];
                int to = s + j;
                // Distance cost for moving this passenger into the candidate compact block.
                int d = Math.abs(from - to);
                total += d;

                if (j > 0) {
                    details.append(" | ");
                }
                details.append("Passenger").append(j).append(": ").append(from)
                        .append("->").append(to).append(" (").append(d).append(")");
            }

            System.out.println("Block start s=" + s + " targets [" + s + "..." + (s + numberPassengers - 1) + "]"
                    + " TOTAL MOVES=" + total);
            System.out.println(" " + details);

            if (total < bestMoves) {
                bestMoves = total;
                bestStart = s;
            }
        }

        System.out.println("\n>>> BEST COMPACT BOARDING BLOCK START = " + bestStart
                + " targets [" + bestStart + "..." + (bestStart + numberPassengers - 1) + "]");
        System.out.println(">>> MINIMUM TOTAL MOVES = " + bestMoves);

        int[] target = new int[aisleLength];
        for (int i = bestStart; i < bestStart + numberPassengers; i++) {
            target[i] = 1;
        }

        System.out.println("Target arrangement: " + Arrays.toString(target));
        System.out.println("=========================================================\n");
    }
}
