package bjc.utils.esodata;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* A stack, with support for combinators.
*
* A FILO stack with support for forth/factor style combinators.
*
*
*
Stack underflow
*
* NOTE: In general, using any operation that attempts to remove more data from
* the stack than exists will cause a {@link StackUnderflowException} to be
* thrown. Check the size of the stack if you want to avoid this.
*
*
*
* @param
* The datatype stored in the stack.
*
* @author Ben Culkin
*/
public abstract class Stack {
/**
* The exception thrown when attempting to access an element from the
* stack that isn't there.
*
* @author EVE
*
*/
public static class StackUnderflowException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1423867176204571539L;
}
/**
* Push an element onto the stack.
*
* @param elm
* The element to insert.
*/
public abstract void push(T elm);
/**
* Pop an element off of the stack.
*
* @return The element on top of the stack.
*/
public abstract T pop();
/**
* Retrieve the top element of this stack without removing it from the
* stack.
*
* @return The top element of this stack.
*/
public abstract T top();
/**
* Get the number of elements in the stack.
*
* @return the number of elements in the stack.
*/
public abstract int size();
/**
* Check if the stack is empty.
*
* @return Whether or not the stack is empty.
*/
public abstract boolean empty();
/**
* Create a spaghetti stack branching off of this one.
*
* @return A spaghetti stack with this stack as a parent.
*/
public Stack spaghettify() {
return new SpaghettiStack<>(this);
}
/*
* Basic combinators
*/
/**
* Drop n items from the stack.
*
* @param n
* The number of items to drop.
*/
public void drop(final int n) {
for (int i = 0; i < n; i++) {
pop();
}
}
/**
* Drop one item from the stack.
*/
public void drop() {
drop(1);
}
/**
* Delete n items below the current one.
*
* @param n
* The number of items below the top to delete.
*/
public void nip(final int n) {
final T elm = pop();
drop(n);
push(elm);
}
/**
* Delete the second element in the stack.
*/
public void nip() {
nip(1);
}
/**
* Replicate the top n items of the stack m times.
*
* @param n
* The number of items to duplicate.
* @param m
* The number of times to duplicate items.
*/
public void multidup(final int n, final int m) {
final List lst = new ArrayList<>(n);
for (int i = n; i > 0; i--) {
lst.set(i - 1, pop());
}
for (int i = 0; i < m; i++) {
for (final T elm : lst) {
push(elm);
}
}
}
/**
* Duplicate the top n items of the stack.
*
* @param n
* The number of items to duplicate.
*/
public void dup(final int n) {
multidup(n, 2);
}
/**
* Duplicate the top item on the stack.
*/
public void dup() {
dup(1);
}
/**
* Replicate the n elements below the top one m times.
*
* @param n
* The number of items to duplicate.
* @param m
* The number of times to duplicate items.
*/
public void multiover(final int n, final int m) {
final List lst = new ArrayList<>(n);
final T elm = pop();
for (int i = n; i > 0; i--) {
lst.set(i - 1, pop());
}
for (final T nelm : lst) {
push(nelm);
}
push(elm);
for (int i = 1; i < m; i++) {
for (final T nelm : lst) {
push(nelm);
}
}
}
/**
* Duplicate the n elements below the top one.
*
* @param n
* The number of items to duplicate.
*/
public void over(final int n) {
multiover(n, 2);
}
/**
* Duplicate the second item in the stack.
*/
public void over() {
over(1);
}
/**
* Duplicate the third item in the stack.
*/
public void pick() {
final T z = pop();
final T y = pop();
final T x = pop();
push(x);
push(y);
push(z);
push(x);
}
/**
* Swap the top two items on the stack.
*/
public void swap() {
final T y = pop();
final T x = pop();
push(y);
push(x);
}
/**
* Duplicate the second item below the first item.
*/
public void deepdup() {
final T y = pop();
final T x = pop();
push(x);
push(x);
push(y);
}
/**
* Swap the second and third items in the stack.
*/
public void deepswap() {
final T z = pop();
final T y = pop();
final T x = pop();
push(y);
push(x);
push(z);
}
/**
* Rotate the top three items on the stack
*/
public void rot() {
final T z = pop();
final T y = pop();
final T x = pop();
push(y);
push(z);
push(x);
}
/**
* Inversely rotate the top three items on the stack
*/
public void invrot() {
final T z = pop();
final T y = pop();
final T x = pop();
push(z);
push(x);
push(y);
}
/*
* Dataflow Combinators
*/
/**
* Hides the top n elements on the stack from cons.
*
* @param n
* The number of elements to hide.
* @param cons
* The action to hide the elements from
*/
public void dip(final int n, final Consumer> cons) {
final List elms = new ArrayList<>(n);
for (int i = n; i > 0; i--) {
elms.set(i - 1, pop());
}
cons.accept(this);
for (final T elm : elms) {
push(elm);
}
}
/**
* Hide the top element of the stack from cons.
*
* @param cons
* The action to hide the top from
*/
public void dip(final Consumer> cons) {
dip(1, cons);
}
/**
* Copy the top n elements on the stack, replacing them once cons is
* done.
*
* @param n
* The number of elements to copy.
* @param cons
* The action to execute.
*/
public void keep(final int n, final Consumer> cons) {
dup(n);
dip(n, cons);
}
/**
* Apply all the actions in conses to the top n elements of the stack.
*
* @param n
* The number of elements to give to cons.
* @param conses
* The actions to execute.
*/
public void multicleave(final int n, final List>> conses) {
final List elms = new ArrayList<>(n);
for (int i = n; i > 0; i--) {
elms.set(i - 1, pop());
}
for (final Consumer> cons : conses) {
for (final T elm : elms) {
push(elm);
}
cons.accept(this);
}
}
/**
* Apply all the actions in conses to the top element of the stack.
*
* @param conses
* The actions to execute.
*/
public void cleave(final List>> conses) {
multicleave(1, conses);
}
/**
* Apply every action in cons to n arguments.
*
* @param n
* The number of parameters each action takes.
* @param conses
* The actions to execute.
*/
public void multispread(final int n, final List>> conses) {
final List> nelms = new ArrayList<>(conses.size());
for (int i = conses.size(); i > 0; i--) {
final List elms = new ArrayList<>(n);
for (int j = n; j > 0; j--) {
elms.set(j, pop());
}
nelms.set(i, elms);
}
int i = 0;
for (final List elms : nelms) {
for (final T elm : elms) {
push(elm);
}
conses.get(i).accept(this);
i += 1;
}
}
/**
* Apply the actions in cons to corresponding elements from the stack.
*
* @param conses
* The actions to execute.
*/
public void spread(final List>> conses) {
multispread(1, conses);
}
/**
* Apply the action in cons to the first m groups of n arguments.
*
* @param n
* The number of arguments cons takes.
* @param m
* The number of time to call cons.
* @param cons
* The action to execute.
*/
public void multiapply(final int n, final int m, final Consumer> cons) {
final List>> conses = new ArrayList<>(m);
for (int i = 0; i < m; i++) {
conses.add(cons);
}
multispread(n, conses);
}
/**
* Apply cons n times to the corresponding elements in the stack.
*
* @param n
* The number of times to execute cons.
* @param cons
* The action to execute.
*/
public void apply(final int n, final Consumer> cons) {
multiapply(1, n, cons);
}
/*
* Misc. functions
*/
/**
* Get an array representing this stack.
*
* @return The stack as an array.
*/
public abstract T[] toArray();
}