package it.univr.di.labeledvalue;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.netbeans.validation.api.Problems;
import org.netbeans.validation.api.Validator;
import it.univr.di.Debug;
import it.univr.di.labeledvalue.ALabelAlphabet.ALetter;
/**
* Simple class to represent a A-label in the CSTNU framework.
* A A-label is a conjunction of zero or more A-Letters in the alphabet ({@link it.univr.di.labeledvalue.ALabelAlphabet}).
* A label without letters is called empty label and it is represented graphically as
* {@link it.univr.di.labeledvalue.Constants#EMPTY_UPPER_CASE_LABEL}.
*
*
* Status of i-th A-Letter * bit0[i] * not present 0 * present 1 **/ private long bit0; /** * Number of A-letters in the label * Value -1 means that the size has to be calculated! */ private byte cacheOfSize; /** * Index of the last significant A-Letter of label. * On 2016-03-30 I showed by SizeofUtilTest.java that using byte it is possible to define also 'size' field without incrementing the memory footprint of the * object. */ private byte maxIndex; /** * The number of times this ALabel has been structurally modified. * Structural modifications are those that change the size, or otherwise perturb it in such a fashion that iterations in * progress may yield incorrect results. */ protected transient int modCount; /** * Just for internal use */ private ALabel() { this.bit0 = 0; this.maxIndex = -1; this.cacheOfSize = 0; this.modCount = 0; } /** * Constructs a label cloning the given label l. * * @param label the label to clone. It cannot be null or empty! */ private ALabel(final ALabel label) { this(); if (label == null || label.isEmpty()) throw new IllegalArgumentException("Label cannot be null or empty!"); this.alphabet = label.alphabet;// alphabet has to be shared! this.bit0 = label.bit0; this.maxIndex = label.maxIndex; this.cacheOfSize = label.cacheOfSize; } /** * Default constructor using a given alphabet. * * @param alphabet1 */ public ALabel(ALabelAlphabet alphabet1) { this(); if (alphabet1 == null) throw new IllegalArgumentException("Alphabet cannot be null!"); this.alphabet = alphabet1; } /** * Builds an a-label using the a-letter 'l' and 'alphabet'. * Be aware that if 'l' is not present into alphabet, it will be added. * * @param l first a-letter of label * @param alphabet1 alphabet of a-letters. It may be empty! */ public ALabel(ALetter l, ALabelAlphabet alphabet1) { this(alphabet1); this.conjunct(l); } /** * Helper constructor. It calls ALabel(ALetter, ALabelAlphabet). * Be aware that if 's' is not present into alphabet as a-letter, it will be added as a-letter. * * @param s the string to add. * @param alphabet1 alphabet of a-letters. It may be empty! */ public ALabel(String s, ALabelAlphabet alphabet1) { this(new ALetter(s), alphabet1); } /** * Makes the label empty. */ public void clear() { this.bit0 = 0; this.maxIndex = -1; this.cacheOfSize = 0; } /** * In order to speed up this method and considering that the {@link ALabelAlphabet} order may be not the expected alphabetic one, * (first letter in an {@link ALabelAlphabet} can be 'nodeZ' and the last one 'aNode'), the order of labels is given w.r.t. their indexes in * the their {@link ALabelAlphabet}. */ @Override public int compareTo(final ALabel label) { if (label == null) return 1; if (label.isEmpty()) { if (this.isEmpty()) return 0; return 1; } if (this.alphabet != label.alphabet) throw new IllegalArgumentException("Comparison is not possible because the given label has a different alphabet from the current one!"); return Long.compareUnsigned(this.bit0, label.bit0); } /** * It conjuncts
a-letter
to this.
*
* @param aLetter the a-letter to conjunct.
* @return true if a-letter is added, false otherwise.
*/
public boolean conjunct(final ALetter aLetter) {
if (aLetter == null)
return false;
byte propIndex = getIndex(aLetter);
if (propIndex < 0)
propIndex = this.alphabet.put(aLetter);
this.set(propIndex, State.present);
return true;
}
/**
* Conjuncts a-label
to this
and returns the result without modifying this
.
*
* @param label the label to conjunct
* @return a new label with the conjunction of 'this' and 'label'.
*/
public ALabel conjunction(final ALabel label) {
if (label == null || (!this.isEmpty() && !label.isEmpty() && this.alphabet != label.alphabet))
return null;
if (this.isEmpty()) {
return new ALabel(label);
}
if (label.isEmpty()) {
return new ALabel(this);
}
final ALabel newLabel = new ALabel(this.alphabet);
newLabel.bit0 = this.bit0 | label.bit0;
newLabel.maxIndex = (label.maxIndex > this.maxIndex) ? label.maxIndex : this.maxIndex;
newLabel.cacheOfSize = -1;// it has to be calculated... delay the stuff.
return newLabel;
}
/**
* L1 contains L2 if L1 contains all a-letters of L2.
*
* @param label the label to check
* @return true if this contains label.
*/
public boolean contains(final ALabel label) {
if ((label == null) || label.isEmpty())
return true;
if (this.alphabet != label.alphabet)
throw new IllegalArgumentException(
"label is not defined using the same alphabet: " + this.alphabet.toString() + " vs " + label.alphabet.toString());
// int max = (this.maxIndex > label.maxIndex) ? this.maxIndex : label.maxIndex;
// for (byte i = (byte) (max + 1); (--i) >= 0;) {
// State thisState = getState(i);
// State labelState = label.getState(i);
// if (thisState == labelState || labelState == State.absent)
// continue;
// if (thisState == State.absent)
// return false;
// }
// return true;
// 1st xor shows different bits. Masking them with the complement of this, shows the bits 1 in label.bit0 that are not present in this.bit0.
return (((this.bit0 ^ label.bit0) & (~this.bit0)) == 0);
}
/**
* @param name the proposition to check.
* @return true if this contains proposition in any state: straight, negated or unknown.
*/
public boolean contains(final ALetter name) {
if (name == null)
return true;
return this.getState(getIndex(name)) != State.absent;
}
/**
* Compare the letter with an a-letter name.
*
* @param name
* @return true if the label is equal to the a-letter name.
*/
public boolean equals(final ALetter name) {
if (name == null)
return false;
return this.size() == 1 && this.getState(getIndex(name)) == State.present;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (!(obj instanceof ALabel))
return false;
ALabel alabel = (ALabel) obj;
if (this.isEmpty() && alabel.isEmpty())
return true;
return this.alphabet == alabel.alphabet && this.bit0 == alabel.bit0;
}
/**
* @return the alphabet
*/
public ALabelAlphabet getAlphabet() {
return this.alphabet;
}
/**
* @return The array of a-letters of present literals in this label.
* public ALetter[] getAllLetter() {
* ALetter[] letters = new ALetter[size()];
* int j = 0;
* for (byte i = 0; i <= this.maxIndex; i++) {
* if (getState(i) != State.absent) {
* letters[j++] = this.getLetter(i);
* }
* }
* return letters;
* }
*/
/**
* @param letter
* @return the index of the letter in the alphabet.
*/
private final byte getIndex(final ALetter letter) {
return this.alphabet.index(letter);
}
/**
* @param letterIndex the index of the literal to retrieve.
* @return the letter with index literalIndex.
*/
final ALetter getLetter(final byte letterIndex) {
return this.alphabet.get(letterIndex);
}
/**
* @param letterIndex the index of the literal to retrieve.
* @return the status of literal with index literalIndex. If the literal is not present, it returns {@link State#absent}, otherwise .{@link State#present}.
*/
final State getState(final byte letterIndex) {
long mask = 1L << letterIndex;
return ((this.bit0 & mask) != 0) ? State.present : State.absent;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// It is impossible to guarantee a unique hashCode for each possible label.
return (int) (31 * this.maxIndex + this.bit0);
}
/**
* @param label
* @return the label containing common ALetter between this and label.
*/
public ALabel intersect(final ALabel label) {
if ((label == null) || this.isEmpty() || label.isEmpty())
return emptyLabel;
if (this.alphabet != label.alphabet)
throw new IllegalArgumentException(
"alabel is not defined using the same alphabet: " + this.alphabet.toString() + " vs " + label.alphabet.toString());
ALabel newl = new ALabel(this.alphabet);
newl.bit0 = this.bit0 & label.bit0;
if (newl.bit0 == 0)
return newl;
newl.maxIndex = (this.maxIndex > label.maxIndex) ? this.maxIndex : label.maxIndex;
while (newl.maxIndex >= 0 && (newl.getState(newl.maxIndex) == State.absent))
newl.maxIndex--;
return newl;
}
/**
* @return true if the label contains no literal.
*/
public boolean isEmpty() {
return this.bit0 == 0;
}
@Override
public Iterator* Status of i-th literal * bit0[i] * absent 0 * present 1 **/ if (aLetterIndex < 0 || aLetterIndex > MAX_ALABELALPHABET_SIZE) return; long mask = 1L << aLetterIndex; switch (letterStatus) { case present: if (((this.bit0) & mask) == 0) this.cacheOfSize++; this.bit0 |= mask; if (this.maxIndex < aLetterIndex) this.maxIndex = aLetterIndex; return; case absent: default: if (((this.bit0) & mask) != 0) this.cacheOfSize--; mask = ~mask; this.bit0 &= mask; if (this.maxIndex == aLetterIndex) { long u = this.bit0; mask = ~mask; do { this.maxIndex--; mask = mask >>> 1; } while ((u & mask) == 0 && this.maxIndex >= 0); } return; } } /** * @return Return the number of literals of the label */ public int size() { if (this.cacheOfSize >= 0) { return this.cacheOfSize; } // byte _cacheOfSize = 0; // long or = this.bit0; // for (int i = this.maxIndex + 1; (--i) >= 0;) { // _cacheOfSize += (or & 1); // or = or >>> 1; // } this.cacheOfSize = (byte) Long.bitCount(this.bit0); return this.cacheOfSize; } /** * @return lower case version */ public String toLowerCase() { return this.toString().toLowerCase(); } /** {@inheritDoc} */ @Override public String toString() { if (this.isEmpty()) return Constants.EMPTY_UPPER_CASE_LABELstring; final StringBuilder s = new StringBuilder(); State st; for (byte i = 0, j = 0; i <= this.maxIndex; i++) { st = getState(i); if (st == State.present) { s.append(getLetter(i)); if (++j < this.size()) { s.append(ALABEL_SEPARATOR); } } } return s.toString(); } /** * @return upper case version */ public String toUpperCase() { return this.toString().toUpperCase(); } }