All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.liblevenshtein.transducer.State Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
package com.github.liblevenshtein.transducer;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Objects;

/**
 * Levenshtein state that is used by all algorithms.  The algorithm-specific
 * components are injected via setters, so {@link State} doesn't need to be
 * coupled to specific implementations.
 * @author Dylon Edwards
 * @since 2.1.0
 */
public class State implements Iterable, Serializable {

  private static final long serialVersionUID = 1L;

  /**
   * Head (first element) of the linked-list of Levenshtein positions.
   */
  private Position head = null;

  /**
   * Updates {@link #head} to point to the new node. The new node is inserted
   * imnmediately-before {@link #head}.
   * @param head {@link Position} to set as the new {@link #head}.
   * @return This {@link State} for fluency.
   */
  public State head(final Position head) {
    head.next(this.head);
    this.head = head;
    return this;
  }

  public Position head() {
    return head;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public StateIterator iterator() {
    return new StateIterator(this, head, null, null);
  }

  /**
   * Adds a new position to the linked-list of positions in this state.
   * @param next Position to add to those already in this state.
   * @return This {@link State} for fluency.
   */
  public State add(final Position next) {
    if (null == head) {
      head = next;
    }
    else {
      Position curr = head;
      while (null != curr.next()) {
        curr = curr.next();
      }
      curr.next(next);
    }
    return this;
  }

  /**
   * Inserts a position into a specific location of the linked-list of positions.
   * @param curr {@link Position} after which to insert {@code next}..
   * @param next {@link Position} to insert after {@code curr}.
   * @return This {@link State} for fluency.
   */
  public State insertAfter(final Position curr, final Position next) {
    if (null != curr) {
      next.next(curr.next());
      curr.next(next);
    }
    else {
      add(next);
    }
    return this;
  }

  /**
   * Removes a position from this state.
   * @param prev {@link Position} preceding the one to remove (useful for not
   *   having to traverse the linked list to find {@code curr}).
   * @param curr {@link Position} to remove from this state.
   * @return This {@link State} for fluency.
   */
  public State remove(final Position prev, final Position curr) {
    if (null != prev) {
      prev.next(curr.next());
    }
    else {
      head = head.next();
    }
    return this;
  }

  /**
   * Merge-sorts the elements of the linked-list of position vectors, according
   * to the algorithm-specific comparator.
   * @param comparator Levenshtein algorithm-specific comparator for sorting the
   *   position elements.
   * @param lhsHead First element of the sublist to sort.
   * @return The new head of the sorted sublist.
   */
  private Position mergeSort(
      final Comparator comparator,
      final Position lhsHead) {

    if (null == lhsHead || null == lhsHead.next()) {
      return lhsHead;
    }

    final Position middle = middle(lhsHead);
    final Position rhsHead = middle.next();
    middle.next(null);

    return merge(comparator,
        mergeSort(comparator, lhsHead),
        mergeSort(comparator, rhsHead));
  }

  /**
   * Merges two sublists together.
   * @param comparator Levenshtein algorithm-specific comparator for sorting the
   *   position elements.
   * @param lhsHead First element of the first sublist.
   * @param rhsHead First element of the second sublist.
   * @return Head of the merged and sorted, sublist.
   */
  @SuppressWarnings("checkstyle:finalparameters")
  private Position merge(
      final Comparator comparator,
      Position lhsHead,
      Position rhsHead) {

    Position next = new Position(-1, -1);
    Position curr = next;

    while (null != lhsHead && null != rhsHead) {
      if (comparator.compare(lhsHead, rhsHead) <= 0) {
        curr.next(lhsHead);
        lhsHead = lhsHead.next();
      }
      else {
        curr.next(rhsHead);
        rhsHead = rhsHead.next();
      }
      curr = curr.next();
    }

    if (null != rhsHead) {
      curr.next(rhsHead);
    }
    else if (null != lhsHead) {
      curr.next(lhsHead);
    }

    curr = next.next();
    return curr;
  }

  /**
   * Returns the element in the middle of the sublist, begun by {@code head}.
   * @param head First element in the sublist.
   * @return Middle element of the sublist.
   */
  private Position middle(final Position head) {
    Position slow = head;
    Position fast = head;

    while (null != fast.next() && null != fast.next().next()) {
      slow = slow.next();
      fast = fast.next().next();
    }

    return slow;
  }

  /**
   * Merge-sorts the positions in this state in a fashion that makes
   * un-subsumption easy.
   * @param comparator Describes how to sort the positions (dependent on the
   *   Levenshtein algorithm).
   * @return This {@link State} for fluency.
   */
  public State sort(final Comparator comparator) {
    head = mergeSort(comparator, head);
    return this;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    State positions = (State) o;
    return Objects.equals(head, positions.head);
  }

  @Override
  public int hashCode() {
    return Objects.hash(head);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy