function group<A>(subject: A[], keepTogether: A[][]): A[][] {
  type Value = {array: A[], added: boolean};

  let groups: Map<A, Value> = new Map;
  for (const xs of keepTogether) {
    let value = {array: xs, added: false};
    for (const x of xs) { groups.set(x, value); }
  }

  let res = [];

  for (const x of subject) {
    let group = groups.get(x);
    if (group?.added) { continue; }
    else if (group) {
      group.added = true;
      res.push(group.array);
    } else {
      res.push([x]);
    }
  }

  return res;
}

function shuffle<A>(subject: A[]): A[] {
  let res = Array.from(subject);

  for (let i = 0; i < res.length - 2; ++i) {
    const j = i + Math.floor(Math.random() * (res.length - i));
    if (i != j) {
      const k = res[i]!;
      res[i] = res[j]!;
      res[j] = k;
    }
  }

  return res;
}

function groupedShuffle<A>(subject: A[], keepTogether: A[][]): A[] {
  let groups = group(subject, keepTogether);
  return shuffle(groups).flat();
}

function shuffleAll() {
  let groups = [group('myno', 'abyss')];

  for (const elem of Array.from(document.getElementsByClassName('shuffle'))) {
    let shuffled = groupedShuffle(Array.from(elem.children), groups);

    elem.innerHTML = '';
    for (const child of shuffled) {
      elem.appendChild(child);
    }
  }

  function group(...xs: string[]) {
    let elements = xs.map(x => document.getElementById(x));
    return elements.every(x => x) ? elements as HTMLElement[] : [];
  }
}

document.addEventListener('DOMContentLoaded', shuffleAll);

export {};
