<body class="antialiased sans-serif text-slate-800 bg-opacity-50">
  <div class="fixed top-0 h-screen w-screen bg-[url('https://images.unsplash.com/photo-1579783901586-d88db74b4fe4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1048&q=80')] bg-center"></div>
  <div x-data="state" x-cloak>
    <div class="max-w-md mx-auto my-6 p-6 bg-white rounded drop-shadow-md">
      <h1 class="text-2xl font-bold mb-6 flex items-center">
        <div class="flex justify-center items-center rounded-full h-8 w-8 mr-2 bg-amber-100">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 fill-amber-600" viewBox="0 0 20 20" fill="currentColor">
            <path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1.323l3.954 1.582 1.599-.8a1 1 0 01.894 1.79l-1.233.616 1.738 5.42a1 1 0 01-.285 1.05A3.989 3.989 0 0115 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.715-5.349L11 6.477V16h2a1 1 0 110 2H7a1 1 0 110-2h2V6.477L6.237 7.582l1.715 5.349a1 1 0 01-.285 1.05A3.989 3.989 0 015 15a3.989 3.989 0 01-2.667-1.019 1 1 0 01-.285-1.05l1.738-5.42-1.233-.617a1 1 0 01.894-1.788l1.599.799L9 4.323V3a1 1 0 011-1zm-5 8.274l-.818 2.552c.25.112.526.174.818.174.292 0 .569-.062.818-.174L5 10.274zm10 0l-.818 2.552c.25.112.526.174.818.174.292 0 .569-.062.818-.174L15 10.274z" clip-rule="evenodd" />
          </svg>
        </div>
        Interactive Binary Search
      </h1>
      <div class="flex items-center">
        <div class="w-full">
          <label for="array" class="block mb-1 text-gray-600">Sorted list of integers</label>
          <textarea x-model="array" x-bind:disabled="!!iterator" x-on:change="validationErrors.array = array.length && !array.match(/^\d+(,\s*\d+)*$/)" x-bind:class="{ 'border-red-400': validationErrors.array }" id="array" placeholder="A sorted comma-separated list of positive integers" class="font-mono text-sm border border-gray-200 shadow-sm w-full px-4 py-2 mb-3 h-24 leading-normal bg-white rounded outline-none focus:ring-2 ring-amber-300 ring-offset-1 disabled:opacity-70 disabled:bg-gray-100 transition-all">
          </textarea>
          <label for="number" class="block mb-1 text-gray-600">Integer to search</label>
          <input x-model="number" x-bind:disabled="!!iterator" x-on:change="validationErrors.number = number.length && !number.match(/^\d+$/)" x-bind:class="{ 'border-red-400': validationErrors.number }" id="number" type="text" placeholder="Integer to search in the input array" class="font-mono text-sm border border-gray-200 shadow-sm w-full px-4 py-2 leading-normal bg-white rounded outline-none focus:ring-2 ring-amber-300 ring-offset-1 disabled:opacity-70 disabled:bg-gray-100 transition-all" />
        </div>
      </div>
      <div class="mt-6">
        <button x-on:click="handleStart()" x-bind:disabled="!!iterator || !array.length || !number.length || validationErrors.array || validationErrors.number" class="rounded px-4 py-2 mr-2 bg-green-600 text-white font-semibold shadow disabled:bg-gray-100 disabled:text-slate-500 hover:bg-green-700 transition-all">Start</button>
        <button x-on:click="handleStep()" x-bind:disabled="!iterator" class="rounded px-4 py-2 mr-2 bg-sky-600 text-white font-semibold shadow disabled:bg-slate-200 disabled:text-slate-500 hover:bg-sky-700 transition-all">Step</button>
        <button x-on:click="iterator = null" x-bind:disabled="!iterator" class="rounded px-4 py-2 mr-2 bg-amber-600 text-white font-semibold shadow disabled:bg-slate-200 disabled:text-slate-500 hover:bg-amber-700 transition-all">Stop</button>
      </div>
      <div x-show="parsedArray.length" class="my-6">
        <label for="number" class="block font-bold mb-1">Execution</label>
        <template x-for="(number, index) in parsedArray">
          <span x-bind:class="{ 'bg-amber-100': index === middle, 'bg-green-400': true && (index === result), 'bg-red-100': result === null, 'opacity-40': !(index >= start && index <= end) }" class="px-2 mx-0.5 relative text-center inline-block border-2 rounded border-slate-500 text-slate-800 transition-all">
            <span x-text="index" class="absolute top-7 text-xs left-1/2 -translate-x-1/2"></span>
            <span x-text="number" class="font-semibold"></span>
          </span>
        </template>
      </div>
      <div x-show="parsedArray.length" class="pt-4">
        <label for="number" class="block font-bold mb-1">Execution log</label>
        <template x-for="(message, index) in log">
          <div x-text="message" x-bind:class="{ 'opacity-40': index > 0 }"></span>
        </template>
      </div>
    </div>
  </div>
</body>
function* binarySearch(array, value, start, end) {
  yield {
    type: "step",
    data: { start, end, middle: null },
    log:
      end < start
        ? "No elements left to check"
        : end === start
        ? `Checking array at position ${start}`
        : `Checking array from ${start} to ${end}`
  };

  if (end < start) {
    yield {
      type: "done",
      data: null,
      log: "Element not found"
    };
  }

  const middle = start + Math.floor((end - start) / 2);
  yield {
    type: "step",
    data: { middle },
    log:
      start !== end
        ? `Element in the middle is ${array[middle]}`
        : middle < array.length
        ? `The only element left is ${array[middle]}`
        : "Middle index out of bounds"
  };

  if (value === array[middle]) {
    yield {
      type: "done",
      data: middle,
      log: `Found element at index ${middle}`
    };
  } else if (value < array[middle]) {
    yield* binarySearch(array, value, start, middle - 1);
  } else {
    yield* binarySearch(array, value, middle + 1, end);
  }
}

function search(array, value) {
  return binarySearch(array, parseInt(value), 0, array.length);
}

document.addEventListener("alpine:init", function init() {
  Alpine.data("state", () => ({
    array: "1, 3, 4, 5, 8, 16, 23, 29",
    number: "16",
    parsedArray: [],
    iterator: null,
    validationErrors: {
      array: false,
      number: false
    },
    log: [],
    handleStart: function start() {
      this.result = undefined;
      this.log = [];
      this.parsedArray = this.array.split(/,\s?/).map((el) => parseInt(el));
      this.iterator = search(this.parsedArray, this.number);
      this.handleStep();
    },
    handleStep: function step() {
      const next = this.iterator.next();
      const action = this.interactive[next.value.type].bind(this);
      action(next.value.data);
      this.log.unshift(next.value.log);
    },
    interactive: {
      step: function step(data) {
        const { start, end, middle } = data;
        if (middle) {
          this.middle = middle;
        } else {
          this.start = start;
          this.end = end;
          this.middle = null;
        }
      },
      done: function done(data) {
        this.result = data;
        this.iterator = null;
      }
    }
  }));
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.