/*
* esodata - data structures and other things, of varying utility
* Copyright 2022, Ben Culkin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package bjc.esodata;
import java.util.ArrayList;
/**
* A tape is a one-dimensional array that can only be accessed in one position
* at a time.
*
* A tape is essentially a 1D array with a cursor attached to it, and you can
* only affect elements at that cursor. The size of the array is theoretically
* unbounded to the right, but in practice bounded by available memory.
*
* You can choose whether or not you want the tape to automatically extend
* itself to the right with null elements by specifying its auto-extension
* policy.
*
* @param
* The element type of the tape.
*
* @author bjculkin
*/
public class SingleTape implements Tape {
/**
* Our backing store.
*/
protected ArrayList backing;
/**
* Our position in the list.
*/
protected int pos;
/**
* Whether to auto-extend the list on the left with nulls.
*/
protected boolean autoExtend;
/**
* Create a new tape with the specified contents that doesn't auto-extend.
*
* @param vals
* The values to put on the tape.
*/
@SafeVarargs
public SingleTape(T... vals) {
autoExtend = false;
backing = new ArrayList<>(vals.length);
for (T val : vals) backing.add(val);
}
/**
* Create a new empty tape that doesn't auto-extend.
*/
public SingleTape() {
this(false);
}
/**
* Create a new tape with values taken from an iterable.
*
* @param itr
* The iterable to get values from.
*/
public SingleTape(Iterable itr) {
this(false);
for (T itm : itr) backing.add(itm);
}
/**
* Create a new empty tape that follows the specified auto-extension policy.
*
* @param autoExtnd
* Whether or not to auto-extend the tape to the right w/
* nulls.
*/
public SingleTape(final boolean autoExtnd) {
autoExtend = autoExtnd;
backing = new ArrayList<>();
}
@Override
public T item() {
if (pos < 0 || pos >= backing.size()) return null;
return backing.get(pos);
}
@Override
public void item(final T itm) {
backing.set(pos, itm);
}
@Override
public int size() {
return backing.size();
}
@Override
public int position() {
return pos;
}
@Override
public void insertBefore(final T itm) {
backing.add(pos, itm);
}
@Override
public void insertAfter(final T itm) {
if (pos == backing.size() - 1) backing.add(itm);
else backing.add(pos + 1, itm);
}
@Override
public T remove() {
final T res = backing.remove(pos);
if (pos != 0) pos -= 1;
return res;
}
@Override
public void first() {
pos = 0;
}
@Override
public void last() {
pos = backing.size() - 1;
}
@Override
public boolean left() {
return left(1);
}
@Override
public boolean left(final int amt) {
if (pos - amt < 0) return false;
pos -= amt;
return true;
}
@Override
public boolean right() {
return right(1);
}
@Override
public boolean right(final int amt) {
if (pos + amt > backing.size()) {
if (autoExtend) {
while (pos + amt >= backing.size() - 1) backing.add(null);
} else {
return false;
}
}
pos += amt;
return true;
}
@Override
public boolean seekTo(int tgtPos) {
if (tgtPos < 0) return false;
if (tgtPos >= backing.size() - 1) {
if (autoExtend) {
while (tgtPos >= backing.size() - 1) backing.add(null);
} else {
return false;
}
}
pos = tgtPos;
return true;
}
@Override
public void append(T itm) {
backing.add(itm);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (backing == null ? 0 : backing.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof SingleTape>)) return false;
final SingleTape> other = (SingleTape>) obj;
if (backing == null) {
if (other.backing != null) return false;
} else if (!backing.equals(other.backing)) {
return false;
}
return true;
}
@Override
public String toString() {
return String.format("SingleTape [backing=%s, pos=%s, autoExtend=%s]", backing,
pos, autoExtend);
}
}